diff --git a/.gitignore b/.gitignore
index 71e3ec1baf9d4681b33d959119899643f13990fd..33f8e0c524b5206b77d3abc976443ff851709b39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 # the default generated dir + db file
 data
 owncloud
+config/config.php
 
 # just sane ignores
 .*.sw[po]
@@ -16,3 +17,14 @@ _darcs/*
 CVS/*
 .svn/*
 RCS/*
+
+# kdevelop
+.kdev
+*.kdev4
+
+# Lokalize
+*lokalize*
+
+# eclipse
+.project
+.settings
diff --git a/.htaccess b/.htaccess
new file mode 100644
index 0000000000000000000000000000000000000000..181248267559e377dbac74e01b395cbc215558f6
--- /dev/null
+++ b/.htaccess
@@ -0,0 +1,7 @@
+ErrorDocument 404 //owncloud/core/templates/404.php
+<IfModule mod_php5.c>
+	php_value upload_max_filesize 512M
+	php_value post_max_size 512M
+	SetEnv htaccessWorking true
+</IfModule>
+Options -Indexes
diff --git a/.tx/config b/.tx/config
new file mode 100644
index 0000000000000000000000000000000000000000..fea041909c86eabcb89bf25c91c1848add3b3cd3
--- /dev/null
+++ b/.tx/config
@@ -0,0 +1,79 @@
+[main]
+host = https://www.transifex.net
+
+[owncloud.core]
+file_filter = l10n/<lang>/core.po
+host = http://www.transifex.net
+source_file = l10n/templates/core.pot
+source_lang = en
+trans.bg_BG = l10n/bg_BG/core.po
+trans.ca = l10n/ca/core.po
+trans.da = l10n/da/core.po
+trans.de = l10n/de/core.po
+trans.el = l10n/el/core.po
+trans.es = l10n/es/core.po
+trans.fr = l10n/fr/core.po
+trans.id = l10n/id/core.po
+trans.it = l10n/it/core.po
+trans.nl = l10n/nl/core.po
+trans.pl = l10n/pl/core.po
+trans.pt_BR = l10n/pt_BR/core.po
+trans.sv = l10n/sv/core.po
+
+[owncloud.settings]
+file_filter = l10n/<lang>/settings.po
+host = http://www.transifex.net
+source_file = l10n/templates/settings.pot
+source_lang = en
+trans.bg_BG = l10n/bg_BG/settings.po
+trans.ca = l10n/ca/settings.po
+trans.da = l10n/da/settings.po
+trans.de = l10n/de/settings.po
+trans.el = l10n/el/settings.po
+trans.es = l10n/es/settings.po
+trans.fr = l10n/fr/settings.po
+trans.id = l10n/id/settings.po
+trans.it = l10n/it/settings.po
+trans.nl = l10n/nl/settings.po
+trans.pl = l10n/pl/settings.po
+trans.pt_BR = l10n/pt_BR/settings.po
+trans.sv = l10n/sv/settings.po
+
+[owncloud.files]
+file_filter = translations/owncloud.files/<lang>.po
+host = http://www.transifex.net
+source_file = l10n/templates/files.pot
+source_lang = en
+trans.bg_BG = l10n/bg_BG/files.po
+trans.ca = l10n/ca/files.po
+trans.da = l10n/da/files.po
+trans.de = l10n/de/files.po
+trans.el = l10n/el/files.po
+trans.es = l10n/es/files.po
+trans.fr = l10n/fr/files.po
+trans.id = l10n/id/files.po
+trans.it = l10n/it/files.po
+trans.nl = l10n/nl/files.po
+trans.pl = l10n/pl/files.po
+trans.pt_BR = l10n/pt_BR/files.po
+trans.sv = l10n/sv/files.po
+
+[owncloud.media]
+file_filter = translations/owncloud.media/<lang>.po
+host = http://www.transifex.net
+source_file = l10n/templates/media.pot
+source_lang = en
+trans.bg_BG = l10n/bg_BG/media.po
+trans.ca = l10n/ca/media.po
+trans.da = l10n/da/media.po
+trans.de = l10n/de/media.po
+trans.el = l10n/el/media.po
+trans.es = l10n/es/media.po
+trans.fr = l10n/fr/media.po
+trans.id = l10n/id/media.po
+trans.it = l10n/it/media.po
+trans.nl = l10n/nl/media.po
+trans.pl = l10n/pl/media.po
+trans.pt_BR = l10n/pt_BR/media.po
+trans.sv = l10n/sv/media.po
+
diff --git a/3rdparty/COPYING-PHP b/3rdparty/COPYING-PHP
new file mode 100644
index 0000000000000000000000000000000000000000..3cc8b777b77259046155b82f23e189d4c5c56d6e
--- /dev/null
+++ b/3rdparty/COPYING-PHP
@@ -0,0 +1,68 @@
+-------------------------------------------------------------------- 
+                  The PHP License, version 3.01
+Copyright (c) 1999 - 2010 The PHP Group. All rights reserved.
+-------------------------------------------------------------------- 
+
+Redistribution and use in source and binary forms, with or without
+modification, is permitted provided that the following conditions
+are met:
+
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+ 
+  2. 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.
+ 
+  3. The name "PHP" must not be used to endorse or promote products
+     derived from this software without prior written permission. For
+     written permission, please contact group@php.net.
+  
+  4. Products derived from this software may not be called "PHP", nor
+     may "PHP" appear in their name, without prior written permission
+     from group@php.net.  You may indicate that your software works in
+     conjunction with PHP by saying "Foo for PHP" instead of calling
+     it "PHP Foo" or "phpfoo"
+ 
+  5. The PHP Group may publish revised and/or new versions of the
+     license from time to time. Each version will be given a
+     distinguishing version number.
+     Once covered code has been published under a particular version
+     of the license, you may always continue to use it under the terms
+     of that version. You may also choose to use such covered code
+     under the terms of any subsequent version of the license
+     published by the PHP Group. No one other than the PHP Group has
+     the right to modify the terms applicable to covered code created
+     under this License.
+
+  6. Redistributions of any form whatsoever must retain the following
+     acknowledgment:
+     "This product includes PHP software, freely available from
+     <http://www.php.net/software/>".
+
+THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 
+ANY EXPRESSED 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 PHP
+DEVELOPMENT TEAM OR ITS 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.
+
+-------------------------------------------------------------------- 
+
+This software consists of voluntary contributions made by many
+individuals on behalf of the PHP Group.
+
+The PHP Group can be contacted via Email at group@php.net.
+
+For more information on the PHP Group and the PHP project, 
+please see <http://www.php.net>.
+
+PHP includes the Zend Engine, freely available at
+<http://www.zend.com>.
diff --git a/3rdparty/COPYING-README b/3rdparty/COPYING-README
new file mode 100644
index 0000000000000000000000000000000000000000..2450ef1581307d360fc11788a0b7a7ce0b245f83
--- /dev/null
+++ b/3rdparty/COPYING-README
@@ -0,0 +1,7 @@
+HTTP is three clause BSD licence
+MDB2 uses a custom licence in the BSD style
+User is AGPL
+XML/RPC is both MIT and PHP License
+
+The rest all licenced under the PHP License see packages/ directory
+for details
diff --git a/3rdparty/Console/Getopt.php b/3rdparty/Console/Getopt.php
new file mode 100644
index 0000000000000000000000000000000000000000..aec980b34d571e1ff1a28a6ce7b0aabefb42e1a1
--- /dev/null
+++ b/3rdparty/Console/Getopt.php
@@ -0,0 +1,251 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Andrei Zmievski <andrei@php.net>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Getopt.php,v 1.21.4.7 2003/12/05 21:57:01 andrei Exp $
+
+require_once( 'PEAR.php');
+
+/**
+ * Command-line options parsing class.
+ *
+ * @author Andrei Zmievski <andrei@php.net>
+ *
+ */
+class Console_Getopt {
+    /**
+     * Parses the command-line options.
+     *
+     * The first parameter to this function should be the list of command-line
+     * arguments without the leading reference to the running program.
+     *
+     * The second parameter is a string of allowed short options. Each of the
+     * option letters can be followed by a colon ':' to specify that the option
+     * requires an argument, or a double colon '::' to specify that the option
+     * takes an optional argument.
+     *
+     * The third argument is an optional array of allowed long options. The
+     * leading '--' should not be included in the option name. Options that
+     * require an argument should be followed by '=', and options that take an
+     * option argument should be followed by '=='.
+     *
+     * The return value is an array of two elements: the list of parsed
+     * options and the list of non-option command-line arguments. Each entry in
+     * the list of parsed options is a pair of elements - the first one
+     * specifies the option, and the second one specifies the option argument,
+     * if there was one.
+     *
+     * Long and short options can be mixed.
+     *
+     * Most of the semantics of this function are based on GNU getopt_long().
+     *
+     * @param array  $args           an array of command-line arguments
+     * @param string $short_options  specifies the list of allowed short options
+     * @param array  $long_options   specifies the list of allowed long options
+     *
+     * @return array two-element array containing the list of parsed options and
+     * the non-option arguments
+     *
+     * @access public
+     *
+     */
+    function getopt2($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(2, $args, $short_options, $long_options);
+    }
+
+    /**
+     * This function expects $args to start with the script name (POSIX-style).
+     * Preserved for backwards compatibility.
+     * @see getopt2()
+     */
+    function getopt($args, $short_options, $long_options = null)
+    {
+        return Console_Getopt::doGetopt(1, $args, $short_options, $long_options);
+    }
+
+    /**
+     * The actual implementation of the argument parsing code.
+     */
+    function doGetopt($version, $args, $short_options, $long_options = null)
+    {
+        // in case you pass directly readPHPArgv() as the first arg
+        if (PEAR::isError($args)) {
+            return $args;
+        }
+        if (empty($args)) {
+            return array(array(), array());
+        }
+        $opts     = array();
+        $non_opts = array();
+
+        settype($args, 'array');
+
+        if ($long_options) {
+            sort($long_options);
+        }
+
+        /*
+         * Preserve backwards compatibility with callers that relied on
+         * erroneous POSIX fix.
+         */
+        if ($version < 2) {
+            if (isset($args[0]{0}) && $args[0]{0} != '-') {
+                array_shift($args);
+            }
+        }
+
+        reset($args);
+        while (list($i, $arg) = each($args)) {
+
+            /* The special element '--' means explicit end of
+               options. Treat the rest of the arguments as non-options
+               and end the loop. */
+            if ($arg == '--') {
+                $non_opts = array_merge($non_opts, array_slice($args, $i + 1));
+                break;
+            }
+
+            if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) {
+                $non_opts = array_merge($non_opts, array_slice($args, $i));
+                break;
+            } elseif (strlen($arg) > 1 && $arg{1} == '-') {
+                $error = Console_Getopt::_parseLongOption(substr($arg, 2), $long_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            } else {
+                $error = Console_Getopt::_parseShortOption(substr($arg, 1), $short_options, $opts, $args);
+                if (PEAR::isError($error))
+                    return $error;
+            }
+        }
+
+        return array($opts, $non_opts);
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseShortOption($arg, $short_options, &$opts, &$args)
+    {
+        for ($i = 0; $i < strlen($arg); $i++) {
+            $opt = $arg{$i};
+            $opt_arg = null;
+
+            /* Try to find the short option in the specifier string. */
+            if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':')
+            {
+                return PEAR::raiseError("Console_Getopt: unrecognized option -- $opt");
+            }
+
+            if (strlen($spec) > 1 && $spec{1} == ':') {
+                if (strlen($spec) > 2 && $spec{2} == ':') {
+                    if ($i + 1 < strlen($arg)) {
+                        /* Option takes an optional argument. Use the remainder of
+                           the arg string if there is anything left. */
+                        $opts[] = array($opt, substr($arg, $i + 1));
+                        break;
+                    }
+                } else {
+                    /* Option requires an argument. Use the remainder of the arg
+                       string if there is anything left. */
+                    if ($i + 1 < strlen($arg)) {
+                        $opts[] = array($opt,  substr($arg, $i + 1));
+                        break;
+                    } else if (list(, $opt_arg) = each($args))
+                        /* Else use the next argument. */;
+                    else
+                        return PEAR::raiseError("Console_Getopt: option requires an argument -- $opt");
+                }
+            }
+
+            $opts[] = array($opt, $opt_arg);
+        }
+    }
+
+    /**
+     * @access private
+     *
+     */
+    function _parseLongOption($arg, $long_options, &$opts, &$args)
+    {
+        @list($opt, $opt_arg) = explode('=', $arg);
+        $opt_len = strlen($opt);
+
+        for ($i = 0; $i < count($long_options); $i++) {
+            $long_opt  = $long_options[$i];
+            $opt_start = substr($long_opt, 0, $opt_len);
+
+            /* Option doesn't match. Go on to the next one. */
+            if ($opt_start != $opt)
+                continue;
+
+            $opt_rest  = substr($long_opt, $opt_len);
+
+            /* Check that the options uniquely matches one of the allowed
+               options. */
+            if ($opt_rest != '' && $opt{0} != '=' &&
+                $i + 1 < count($long_options) &&
+                $opt == substr($long_options[$i+1], 0, $opt_len)) {
+                return PEAR::raiseError("Console_Getopt: option --$opt is ambiguous");
+            }
+
+            if (substr($long_opt, -1) == '=') {
+                if (substr($long_opt, -2) != '==') {
+                    /* Long option requires an argument.
+                       Take the next argument if one wasn't specified. */;
+                    if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) {
+                        return PEAR::raiseError("Console_Getopt: option --$opt requires an argument");
+                    }
+                }
+            } else if ($opt_arg) {
+                return PEAR::raiseError("Console_Getopt: option --$opt doesn't allow an argument");
+            }
+
+            $opts[] = array('--' . $opt, $opt_arg);
+            return;
+        }
+
+        return PEAR::raiseError("Console_Getopt: unrecognized option --$opt");
+    }
+
+    /**
+    * Safely read the $argv PHP array across different PHP configurations.
+    * Will take care on register_globals and register_argc_argv ini directives
+    *
+    * @access public
+    * @return mixed the $argv PHP array or PEAR error if not registered
+    */
+    function readPHPArgv()
+    {
+        global $argv;
+        if (!is_array($argv)) {
+            if (!@is_array($_SERVER['argv'])) {
+                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
+                    return PEAR::raiseError("Console_Getopt: Could not read cmd args (register_argc_argv=Off?)");
+                }
+                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
+            }
+            return $_SERVER['argv'];
+        }
+        return $argv;
+    }
+
+}
+
+?>
diff --git a/3rdparty/Crypt_Blowfish/Blowfish.php b/3rdparty/Crypt_Blowfish/Blowfish.php
new file mode 100644
index 0000000000000000000000000000000000000000..a7b8948f043679ce6c5870438d82ae4a4a6ba7ff
--- /dev/null
+++ b/3rdparty/Crypt_Blowfish/Blowfish.php
@@ -0,0 +1,317 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Crypt_Blowfish allows for encryption and decryption on the fly using
+ * the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt
+ * PHP extension, it uses only PHP.
+ * Crypt_Blowfish support encryption/decryption with or without a secret key.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Encryption
+ * @package    Crypt_Blowfish
+ * @author     Matthew Fonda <mfonda@php.net>
+ * @copyright  2005 Matthew Fonda
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id: Blowfish.php,v 1.81 2005/05/30 18:40:36 mfonda Exp $
+ * @link       http://pear.php.net/package/Crypt_Blowfish
+ */
+
+
+require_once 'PEAR.php';
+
+
+/**
+ *
+ * Example usage:
+ * $bf = new Crypt_Blowfish('some secret key!');
+ * $encrypted = $bf->encrypt('this is some example plain text');
+ * $plaintext = $bf->decrypt($encrypted);
+ * echo "plain text: $plaintext";
+ *
+ *
+ * @category   Encryption
+ * @package    Crypt_Blowfish
+ * @author     Matthew Fonda <mfonda@php.net>
+ * @copyright  2005 Matthew Fonda
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @link       http://pear.php.net/package/Crypt_Blowfish
+ * @version    @package_version@
+ * @access     public
+ */
+class Crypt_Blowfish
+{
+    /**
+     * P-Array contains 18 32-bit subkeys
+     *
+     * @var array
+     * @access private
+     */
+    var $_P = array();
+    
+    
+    /**
+     * Array of four S-Blocks each containing 256 32-bit entries
+     *
+     * @var array
+     * @access private
+     */
+    var $_S = array();
+
+    /**
+     * Mcrypt td resource
+     *
+     * @var resource
+     * @access private
+     */
+    var $_td = null;
+
+    /**
+     * Initialization vector
+     *
+     * @var string
+     * @access private
+     */
+    var $_iv = null;
+
+    
+    /**
+     * Crypt_Blowfish Constructor
+     * Initializes the Crypt_Blowfish object, and gives a sets
+     * the secret key
+     *
+     * @param string $key
+     * @access public
+     */
+    function Crypt_Blowfish($key)
+    {
+        if (extension_loaded('mcrypt')) {
+            $this->_td = mcrypt_module_open(MCRYPT_BLOWFISH, '', 'ecb', '');
+            $this->_iv = mcrypt_create_iv(8, MCRYPT_RAND);
+        }
+        $this->setKey($key);
+    }
+    
+    /**
+     * Deprecated isReady method
+     *
+     * @return bool
+     * @access public
+     * @deprecated
+     */
+    function isReady()
+    {
+        return true;
+    }
+    
+    /**
+     * Deprecated init method - init is now a private
+     * method and has been replaced with _init
+     *
+     * @return bool
+     * @access public
+     * @deprecated
+     * @see Crypt_Blowfish::_init()
+     */
+    function init()
+    {
+        $this->_init();
+    }
+    
+    /**
+     * Initializes the Crypt_Blowfish object
+     *
+     * @access private
+     */
+    function _init()
+    {
+        $defaults = new Crypt_Blowfish_DefaultKey();
+        $this->_P = $defaults->P;
+        $this->_S = $defaults->S;
+    }
+            
+    /**
+     * Enciphers a single 64 bit block
+     *
+     * @param int &$Xl
+     * @param int &$Xr
+     * @access private
+     */
+    function _encipher(&$Xl, &$Xr)
+    {
+        for ($i = 0; $i < 16; $i++) {
+            $temp = $Xl ^ $this->_P[$i];
+            $Xl = ((($this->_S[0][($temp>>24) & 255] +
+                            $this->_S[1][($temp>>16) & 255]) ^
+                            $this->_S[2][($temp>>8) & 255]) +
+                            $this->_S[3][$temp & 255]) ^ $Xr;
+            $Xr = $temp;
+        }
+        $Xr = $Xl ^ $this->_P[16];
+        $Xl = $temp ^ $this->_P[17];
+    }
+    
+    
+    /**
+     * Deciphers a single 64 bit block
+     *
+     * @param int &$Xl
+     * @param int &$Xr
+     * @access private
+     */
+    function _decipher(&$Xl, &$Xr)
+    {
+        for ($i = 17; $i > 1; $i--) {
+            $temp = $Xl ^ $this->_P[$i];
+            $Xl = ((($this->_S[0][($temp>>24) & 255] +
+                            $this->_S[1][($temp>>16) & 255]) ^
+                            $this->_S[2][($temp>>8) & 255]) +
+                            $this->_S[3][$temp & 255]) ^ $Xr;
+            $Xr = $temp;
+        }
+        $Xr = $Xl ^ $this->_P[1];
+        $Xl = $temp ^ $this->_P[0];
+    }
+    
+    
+    /**
+     * Encrypts a string
+     *
+     * @param string $plainText
+     * @return string Returns cipher text on success, PEAR_Error on failure
+     * @access public
+     */
+    function encrypt($plainText)
+    {
+        if (!is_string($plainText)) {
+            PEAR::raiseError('Plain text must be a string', 0, PEAR_ERROR_DIE);
+        }
+
+        if (extension_loaded('mcrypt')) {
+            return mcrypt_generic($this->_td, $plainText);
+        }
+
+        $cipherText = '';
+        $len = strlen($plainText);
+        $plainText .= str_repeat(chr(0),(8 - ($len%8))%8);
+        for ($i = 0; $i < $len; $i += 8) {
+            list(,$Xl,$Xr) = unpack("N2",substr($plainText,$i,8));
+            $this->_encipher($Xl, $Xr);
+            $cipherText .= pack("N2", $Xl, $Xr);
+        }
+        return $cipherText;
+    }
+    
+    
+    /**
+     * Decrypts an encrypted string
+     *
+     * @param string $cipherText
+     * @return string Returns plain text on success, PEAR_Error on failure
+     * @access public
+     */
+    function decrypt($cipherText)
+    {
+        if (!is_string($cipherText)) {
+            PEAR::raiseError('Chiper text must be a string', 1, PEAR_ERROR_DIE);
+        }
+
+        if (extension_loaded('mcrypt')) {
+            return mdecrypt_generic($this->_td, $cipherText);
+        }
+
+        $plainText = '';
+        $len = strlen($cipherText);
+        $cipherText .= str_repeat(chr(0),(8 - ($len%8))%8);
+        for ($i = 0; $i < $len; $i += 8) {
+            list(,$Xl,$Xr) = unpack("N2",substr($cipherText,$i,8));
+            $this->_decipher($Xl, $Xr);
+            $plainText .= pack("N2", $Xl, $Xr);
+        }
+        return $plainText;
+    }
+    
+    
+    /**
+     * Sets the secret key
+     * The key must be non-zero, and less than or equal to
+     * 56 characters in length.
+     *
+     * @param string $key
+     * @return bool  Returns true on success, PEAR_Error on failure
+     * @access public
+     */
+    function setKey($key)
+    {
+        if (!is_string($key)) {
+            PEAR::raiseError('Key must be a string', 2, PEAR_ERROR_DIE);
+        }
+
+        $len = strlen($key);
+
+        if ($len > 56 || $len == 0) {
+            PEAR::raiseError('Key must be less than 56 characters and non-zero. Supplied key length: ' . $len, 3, PEAR_ERROR_DIE);
+        }
+
+        if (extension_loaded('mcrypt')) {
+            mcrypt_generic_init($this->_td, $key, $this->_iv);
+            return true;
+        }
+
+        require_once 'Blowfish/DefaultKey.php';
+        $this->_init();
+        
+        $k = 0;
+        $data = 0;
+        $datal = 0;
+        $datar = 0;
+        
+        for ($i = 0; $i < 18; $i++) {
+            $data = 0;
+            for ($j = 4; $j > 0; $j--) {
+                    $data = $data << 8 | ord($key{$k});
+                    $k = ($k+1) % $len;
+            }
+            $this->_P[$i] ^= $data;
+        }
+        
+        for ($i = 0; $i <= 16; $i += 2) {
+            $this->_encipher($datal, $datar);
+            $this->_P[$i] = $datal;
+            $this->_P[$i+1] = $datar;
+        }
+        for ($i = 0; $i < 256; $i += 2) {
+            $this->_encipher($datal, $datar);
+            $this->_S[0][$i] = $datal;
+            $this->_S[0][$i+1] = $datar;
+        }
+        for ($i = 0; $i < 256; $i += 2) {
+            $this->_encipher($datal, $datar);
+            $this->_S[1][$i] = $datal;
+            $this->_S[1][$i+1] = $datar;
+        }
+        for ($i = 0; $i < 256; $i += 2) {
+            $this->_encipher($datal, $datar);
+            $this->_S[2][$i] = $datal;
+            $this->_S[2][$i+1] = $datar;
+        }
+        for ($i = 0; $i < 256; $i += 2) {
+            $this->_encipher($datal, $datar);
+            $this->_S[3][$i] = $datal;
+            $this->_S[3][$i+1] = $datar;
+        }
+        
+        return true;
+    }
+    
+}
+
+?>
diff --git a/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php b/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ff8ac788a6b62a4abde68d476c673612b8bfe01
--- /dev/null
+++ b/3rdparty/Crypt_Blowfish/Blowfish/DefaultKey.php
@@ -0,0 +1,327 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Crypt_Blowfish allows for encryption and decryption on the fly using
+ * the Blowfish algorithm. Crypt_Blowfish does not require the mcrypt
+ * PHP extension, it uses only PHP.
+ * Crypt_Blowfish support encryption/decryption with or without a secret key.
+ *
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Encryption
+ * @package    Crypt_Blowfish
+ * @author     Matthew Fonda <mfonda@php.net>
+ * @copyright  2005 Matthew Fonda
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id: DefaultKey.php,v 1.81 2005/05/30 18:40:37 mfonda Exp $
+ * @link       http://pear.php.net/package/Crypt_Blowfish
+ */
+
+
+/**
+ * Class containing default key
+ *
+ * @category   Encryption
+ * @package    Crypt_Blowfish
+ * @author     Matthew Fonda <mfonda@php.net>
+ * @copyright  2005 Matthew Fonda
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @link       http://pear.php.net/package/Crypt_Blowfish
+ * @version    @package_version@
+ * @access     public
+ */
+class Crypt_Blowfish_DefaultKey
+{
+    var $P = array();
+    
+    var $S = array();
+    
+    function Crypt_Blowfish_DefaultKey()
+    {
+        $this->P = array(
+            0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344,
+            0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
+            0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C,
+            0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
+            0x9216D5D9, 0x8979FB1B
+        );
+        
+        $this->S = array(
+            array(
+                0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7,
+                0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99,
+                0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16,
+                0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E,
+                0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE,
+                0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013,
+                0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF,
+                0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E,
+                0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60,
+                0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440,
+                0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE,
+                0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A,
+                0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E,
+                0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677,
+                0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193,
+                0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032,
+                0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88,
+                0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239,
+                0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E,
+                0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0,
+                0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3,
+                0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98,
+                0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88,
+                0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE,
+                0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6,
+                0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D,
+                0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B,
+                0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7,
+                0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA,
+                0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463,
+                0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F,
+                0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09,
+                0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3,
+                0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB,
+                0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279,
+                0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8,
+                0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB,
+                0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82,
+                0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB,
+                0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573,
+                0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0,
+                0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B,
+                0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790,
+                0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8,
+                0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4,
+                0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0,
+                0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7,
+                0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C,
+                0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD,
+                0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1,
+                0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299,
+                0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9,
+                0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477,
+                0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF,
+                0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49,
+                0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF,
+                0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA,
+                0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5,
+                0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41,
+                0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915,
+                0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400,
+                0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915,
+                0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664,
+                0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A
+            ),
+            array(
+                0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623,
+                0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266,
+                0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1,
+                0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E,
+                0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6,
+                0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1,
+                0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E,
+                0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1,
+                0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737,
+                0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8,
+                0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF,
+                0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD,
+                0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701,
+                0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7,
+                0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41,
+                0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331,
+                0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF,
+                0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF,
+                0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E,
+                0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87,
+                0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C,
+                0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2,
+                0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16,
+                0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD,
+                0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B,
+                0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509,
+                0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E,
+                0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3,
+                0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F,
+                0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A,
+                0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4,
+                0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960,
+                0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66,
+                0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28,
+                0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802,
+                0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84,
+                0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510,
+                0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF,
+                0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14,
+                0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E,
+                0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50,
+                0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7,
+                0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8,
+                0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281,
+                0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99,
+                0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696,
+                0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128,
+                0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73,
+                0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0,
+                0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0,
+                0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105,
+                0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250,
+                0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3,
+                0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285,
+                0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00,
+                0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061,
+                0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB,
+                0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E,
+                0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735,
+                0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC,
+                0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9,
+                0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340,
+                0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20,
+                0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7
+            ),
+            array(
+                0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934,
+                0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068,
+                0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF,
+                0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840,
+                0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45,
+                0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504,
+                0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A,
+                0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB,
+                0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE,
+                0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6,
+                0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42,
+                0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B,
+                0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2,
+                0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB,
+                0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527,
+                0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B,
+                0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33,
+                0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C,
+                0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3,
+                0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC,
+                0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17,
+                0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564,
+                0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B,
+                0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115,
+                0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922,
+                0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728,
+                0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0,
+                0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E,
+                0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37,
+                0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D,
+                0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804,
+                0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B,
+                0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3,
+                0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB,
+                0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D,
+                0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C,
+                0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350,
+                0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9,
+                0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A,
+                0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE,
+                0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D,
+                0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC,
+                0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F,
+                0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61,
+                0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2,
+                0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9,
+                0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2,
+                0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C,
+                0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E,
+                0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633,
+                0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10,
+                0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169,
+                0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52,
+                0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027,
+                0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5,
+                0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62,
+                0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634,
+                0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76,
+                0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24,
+                0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC,
+                0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4,
+                0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C,
+                0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837,
+                0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0
+            ),
+            array(
+                0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B,
+                0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE,
+                0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B,
+                0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4,
+                0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8,
+                0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6,
+                0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304,
+                0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22,
+                0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4,
+                0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6,
+                0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9,
+                0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59,
+                0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593,
+                0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51,
+                0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28,
+                0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C,
+                0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B,
+                0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28,
+                0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C,
+                0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD,
+                0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A,
+                0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319,
+                0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB,
+                0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F,
+                0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991,
+                0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32,
+                0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680,
+                0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166,
+                0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE,
+                0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB,
+                0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5,
+                0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47,
+                0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370,
+                0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D,
+                0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84,
+                0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048,
+                0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8,
+                0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD,
+                0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9,
+                0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7,
+                0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38,
+                0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F,
+                0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C,
+                0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525,
+                0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1,
+                0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442,
+                0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964,
+                0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E,
+                0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8,
+                0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D,
+                0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F,
+                0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299,
+                0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02,
+                0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC,
+                0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614,
+                0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A,
+                0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6,
+                0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B,
+                0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0,
+                0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060,
+                0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E,
+                0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9,
+                0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F,
+                0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6
+            )
+        );
+    }
+    
+}
+
+?>
diff --git a/3rdparty/MDB2.php b/3rdparty/MDB2.php
new file mode 100644
index 0000000000000000000000000000000000000000..fbc7107914ebc7942817fa44b46149f6cf908588
--- /dev/null
+++ b/3rdparty/MDB2.php
@@ -0,0 +1,4317 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: MDB2.php,v 1.335 2008/11/29 14:57:01 afz Exp $
+//
+
+/**
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+
+require_once('PEAR.php');
+
+// {{{ Error constants
+
+/**
+ * The method mapErrorCode in each MDB2_dbtype implementation maps
+ * native error codes to one of these.
+ *
+ * If you add an error code here, make sure you also add a textual
+ * version of it in MDB2::errorMessage().
+ */
+
+define('MDB2_OK',                      true);
+define('MDB2_ERROR',                     -1);
+define('MDB2_ERROR_SYNTAX',              -2);
+define('MDB2_ERROR_CONSTRAINT',          -3);
+define('MDB2_ERROR_NOT_FOUND',           -4);
+define('MDB2_ERROR_ALREADY_EXISTS',      -5);
+define('MDB2_ERROR_UNSUPPORTED',         -6);
+define('MDB2_ERROR_MISMATCH',            -7);
+define('MDB2_ERROR_INVALID',             -8);
+define('MDB2_ERROR_NOT_CAPABLE',         -9);
+define('MDB2_ERROR_TRUNCATED',          -10);
+define('MDB2_ERROR_INVALID_NUMBER',     -11);
+define('MDB2_ERROR_INVALID_DATE',       -12);
+define('MDB2_ERROR_DIVZERO',            -13);
+define('MDB2_ERROR_NODBSELECTED',       -14);
+define('MDB2_ERROR_CANNOT_CREATE',      -15);
+define('MDB2_ERROR_CANNOT_DELETE',      -16);
+define('MDB2_ERROR_CANNOT_DROP',        -17);
+define('MDB2_ERROR_NOSUCHTABLE',        -18);
+define('MDB2_ERROR_NOSUCHFIELD',        -19);
+define('MDB2_ERROR_NEED_MORE_DATA',     -20);
+define('MDB2_ERROR_NOT_LOCKED',         -21);
+define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22);
+define('MDB2_ERROR_INVALID_DSN',        -23);
+define('MDB2_ERROR_CONNECT_FAILED',     -24);
+define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25);
+define('MDB2_ERROR_NOSUCHDB',           -26);
+define('MDB2_ERROR_ACCESS_VIOLATION',   -27);
+define('MDB2_ERROR_CANNOT_REPLACE',     -28);
+define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29);
+define('MDB2_ERROR_DEADLOCK',           -30);
+define('MDB2_ERROR_CANNOT_ALTER',       -31);
+define('MDB2_ERROR_MANAGER',            -32);
+define('MDB2_ERROR_MANAGER_PARSE',      -33);
+define('MDB2_ERROR_LOADMODULE',         -34);
+define('MDB2_ERROR_INSUFFICIENT_DATA',  -35);
+define('MDB2_ERROR_NO_PERMISSION',      -36);
+define('MDB2_ERROR_DISCONNECT_FAILED',  -37);
+
+// }}}
+// {{{ Verbose constants
+/**
+ * These are just helper constants to more verbosely express parameters to prepare()
+ */
+
+define('MDB2_PREPARE_MANIP', false);
+define('MDB2_PREPARE_RESULT', null);
+
+// }}}
+// {{{ Fetchmode constants
+
+/**
+ * This is a special constant that tells MDB2 the user hasn't specified
+ * any particular get mode, so the default should be used.
+ */
+define('MDB2_FETCHMODE_DEFAULT', 0);
+
+/**
+ * Column data indexed by numbers, ordered from 0 and up
+ */
+define('MDB2_FETCHMODE_ORDERED', 1);
+
+/**
+ * Column data indexed by column names
+ */
+define('MDB2_FETCHMODE_ASSOC', 2);
+
+/**
+ * Column data as object properties
+ */
+define('MDB2_FETCHMODE_OBJECT', 3);
+
+/**
+ * For multi-dimensional results: normally the first level of arrays
+ * is the row number, and the second level indexed by column number or name.
+ * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays
+ * is the column name, and the second level the row number.
+ */
+define('MDB2_FETCHMODE_FLIPPED', 4);
+
+// }}}
+// {{{ Portability mode constants
+
+/**
+ * Portability: turn off all portability features.
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_NONE', 0);
+
+/**
+ * Portability: convert names of tables and fields to case defined in the
+ * "field_case" option when using the query*(), fetch*() and tableInfo() methods.
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_FIX_CASE', 1);
+
+/**
+ * Portability: right trim the data output by query*() and fetch*().
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_RTRIM', 2);
+
+/**
+ * Portability: force reporting the number of rows deleted.
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_DELETE_COUNT', 4);
+
+/**
+ * Portability: not needed in MDB2 (just left here for compatibility to DB)
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_NUMROWS', 8);
+
+/**
+ * Portability: makes certain error messages in certain drivers compatible
+ * with those from other DBMS's.
+ *
+ * + mysql, mysqli:  change unique/primary key constraints
+ *   MDB2_ERROR_ALREADY_EXISTS -> MDB2_ERROR_CONSTRAINT
+ *
+ * + odbc(access):  MS's ODBC driver reports 'no such field' as code
+ *   07001, which means 'too few parameters.'  When this option is on
+ *   that code gets mapped to MDB2_ERROR_NOSUCHFIELD.
+ *
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_ERRORS', 16);
+
+/**
+ * Portability: convert empty values to null strings in data output by
+ * query*() and fetch*().
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32);
+
+/**
+ * Portability: removes database/table qualifiers from associative indexes
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64);
+
+/**
+ * Portability: turn on all portability features.
+ * @see MDB2_Driver_Common::setOption()
+ */
+define('MDB2_PORTABILITY_ALL', 127);
+
+// }}}
+// {{{ Globals for class instance tracking
+
+/**
+ * These are global variables that are used to track the various class instances
+ */
+
+$GLOBALS['_MDB2_databases'] = array();
+$GLOBALS['_MDB2_dsninfo_default'] = array(
+    'phptype'  => false,
+    'dbsyntax' => false,
+    'username' => false,
+    'password' => false,
+    'protocol' => false,
+    'hostspec' => false,
+    'port'     => false,
+    'socket'   => false,
+    'database' => false,
+    'mode'     => false,
+);
+
+// }}}
+// {{{ class MDB2
+
+/**
+ * The main 'MDB2' class is simply a container class with some static
+ * methods for creating DB objects as well as some utility functions
+ * common to all parts of DB.
+ *
+ * The object model of MDB2 is as follows (indentation means inheritance):
+ *
+ * MDB2          The main MDB2 class.  This is simply a utility class
+ *              with some 'static' methods for creating MDB2 objects as
+ *              well as common utility functions for other MDB2 classes.
+ *
+ * MDB2_Driver_Common   The base for each MDB2 implementation.  Provides default
+ * |            implementations (in OO lingo virtual methods) for
+ * |            the actual DB implementations as well as a bunch of
+ * |            query utility functions.
+ * |
+ * +-MDB2_Driver_mysql  The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common.
+ *              When calling MDB2::factory or MDB2::connect for MySQL
+ *              connections, the object returned is an instance of this
+ *              class.
+ * +-MDB2_Driver_pgsql  The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common.
+ *              When calling MDB2::factory or MDB2::connect for PostGreSQL
+ *              connections, the object returned is an instance of this
+ *              class.
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2
+{
+    // {{{ function setOptions(&$db, $options)
+
+    /**
+     * set option array   in an exiting database object
+     *
+     * @param   MDB2_Driver_Common  MDB2 object
+     * @param   array   An associative array of option names and their values.
+     *
+     * @return mixed   MDB2_OK or a PEAR Error object
+     *
+     * @access  public
+     */
+    static function setOptions(&$db, $options)
+    {
+        if (is_array($options)) {
+            foreach ($options as $option => $value) {
+                $test = $db->setOption($option, $value);
+                if (PEAR::isError($test)) {
+                    return $test;
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function classExists($classname)
+
+    /**
+     * Checks if a class exists without triggering __autoload
+     *
+     * @param   string  classname
+     *
+     * @return  bool    true success and false on error
+     * @static
+     * @access  public
+     */
+    static function classExists($classname)
+    {
+        if (version_compare(phpversion(), "5.0", ">=")) {
+            return class_exists($classname, false);
+        }
+        return class_exists($classname);
+    }
+
+    // }}}
+    // {{{ function loadClass($class_name, $debug)
+
+    /**
+     * Loads a PEAR class.
+     *
+     * @param   string  classname to load
+     * @param   bool    if errors should be suppressed
+     *
+     * @return  mixed   true success or PEAR_Error on failure
+     *
+     * @access  public
+     */
+    static function loadClass($class_name, $debug)
+    {
+        if (!MDB2::classExists($class_name)) {
+            $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
+            if ($debug) {
+                $include = include_once($file_name);
+            } else {
+                $include = include_once($file_name);
+            }
+            if (!$include) {
+                if (!MDB2::fileExists($file_name)) {
+                    $msg = "unable to find package '$class_name' file '$file_name'";
+                } else {
+                    $msg = "unable to load class '$class_name' from file '$file_name'";
+                }
+                $err =MDB2::raiseErrorStatic(MDB2_ERROR_NOT_FOUND, null, null, $msg);
+                return $err;
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function &factory($dsn, $options = false)
+
+    /**
+     * Create a new MDB2 object for the specified database type
+     *
+     * IMPORTANT: In order for MDB2 to work properly it is necessary that
+     * you make sure that you work with a reference of the original
+     * object instead of a copy (this is a PHP4 quirk).
+     *
+     * For example:
+     *     $db =& MDB2::factory($dsn);
+     *          ^^
+     * And not:
+     *     $db = MDB2::factory($dsn);
+     *
+     * @param   mixed   'data source name', see the MDB2::parseDSN
+     *                      method for a description of the dsn format.
+     *                      Can also be specified as an array of the
+     *                      format returned by MDB2::parseDSN.
+     * @param   array   An associative array of option names and
+     *                            their values.
+     *
+     * @return  mixed   a newly created MDB2 object, or false on error
+     *
+     * @access  public
+     */
+    static function factory($dsn, $options = false)
+    {
+        $dsninfo = MDB2::parseDSN($dsn);
+        if (empty($dsninfo['phptype'])) {
+            $err =MDB2::raiseErrorStatic(MDB2_ERROR_NOT_FOUND,
+                null, null, 'no RDBMS driver specified');
+            return $err;
+        }
+        $class_name = 'MDB2_Driver_'.$dsninfo['phptype'];
+
+        $debug = (!empty($options['debug']));
+        $err = MDB2::loadClass($class_name, $debug);
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        $db =new $class_name();
+        $db->setDSN($dsninfo);
+        $err = MDB2::setOptions($db, $options);
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        return $db;
+    }
+
+    // }}}
+    // {{{ function &connect($dsn, $options = false)
+
+    /**
+     * Create a new MDB2_Driver_* connection object and connect to the specified
+     * database
+     *
+     * IMPORTANT: In order for MDB2 to work properly it is necessary that
+     * you make sure that you work with a reference of the original
+     * object instead of a copy (this is a PHP4 quirk).
+     *
+     * For example:
+     *     $db =& MDB2::connect($dsn);
+     *          ^^
+     * And not:
+     *     $db = MDB2::connect($dsn);
+     *          ^^
+     *
+     * @param mixed $dsn     'data source name', see the MDB2::parseDSN
+     *                       method for a description of the dsn format.
+     *                       Can also be specified as an array of the
+     *                       format returned by MDB2::parseDSN.
+     * @param array $options An associative array of option names and
+     *                       their values.
+     *
+     * @return mixed a newly created MDB2 connection object, or a MDB2
+     *               error object on error
+     *
+     * @access  public
+     * @see     MDB2::parseDSN
+     */
+    function &connect($dsn, $options = false)
+    {
+        $db =MDB2::factory($dsn, $options);
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $err = $db->connect();
+        if (PEAR::isError($err)) {
+            $dsn = $db->getDSN('string', 'xxx');
+            $db->disconnect();
+            $err->addUserInfo($dsn);
+            return $err;
+        }
+
+        return $db;
+    }
+
+    // }}}
+    // {{{ function &singleton($dsn = null, $options = false)
+
+    /**
+     * Returns a MDB2 connection with the requested DSN.
+     * A new MDB2 connection object is only created if no object with the
+     * requested DSN exists yet.
+     *
+     * IMPORTANT: In order for MDB2 to work properly it is necessary that
+     * you make sure that you work with a reference of the original
+     * object instead of a copy (this is a PHP4 quirk).
+     *
+     * For example:
+     *     $db =& MDB2::singleton($dsn);
+     *          ^^
+     * And not:
+     *     $db = MDB2::singleton($dsn);
+     *          ^^
+     *
+     * @param   mixed   'data source name', see the MDB2::parseDSN
+     *                            method for a description of the dsn format.
+     *                            Can also be specified as an array of the
+     *                            format returned by MDB2::parseDSN.
+     * @param   array   An associative array of option names and
+     *                            their values.
+     *
+     * @return  mixed   a newly created MDB2 connection object, or a MDB2
+     *                  error object on error
+     *
+     * @access  public
+     * @see     MDB2::parseDSN
+     */
+    function &singleton($dsn = null, $options = false)
+    {
+        if ($dsn) {
+            $dsninfo = MDB2::parseDSN($dsn);
+            $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo);
+            $keys = array_keys($GLOBALS['_MDB2_databases']);
+            for ($i=0, $j=count($keys); $i<$j; ++$i) {
+                if (isset($GLOBALS['_MDB2_databases'][$keys[$i]])) {
+                    $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array');
+                    if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0) {
+                        MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options);
+                        return $GLOBALS['_MDB2_databases'][$keys[$i]];
+                    }
+                }
+            }
+        } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) {
+            $db =$GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])];
+            return $db;
+        }
+        $db =MDB2::factory($dsn, $options);
+        return $db;
+    }
+
+    // }}}
+    // {{{ function areEquals()
+
+    /**
+     * It looks like there's a memory leak in array_diff() in PHP 5.1.x,
+     * so use this method instead.
+     * @see http://pear.php.net/bugs/bug.php?id=11790
+     *
+     * @param array $arr1
+     * @param array $arr2
+     * @return boolean
+     */
+    static function areEquals($arr1, $arr2)
+    {
+        if (count($arr1) != count($arr2)) {
+            return false;
+        }
+        foreach (array_keys($arr1) as $k) {
+            if (!array_key_exists($k, $arr2) || $arr1[$k] != $arr2[$k]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ function loadFile($file)
+
+    /**
+     * load a file (like 'Date')
+     *
+     * @param   string  name of the file in the MDB2 directory (without '.php')
+     *
+     * @return  string  name of the file that was included
+     *
+     * @access  public
+     */
+    function loadFile($file)
+    {
+        $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php';
+        if (!MDB2::fileExists($file_name)) {
+            return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'unable to find: '.$file_name);
+        }
+        if (!include_once($file_name)) {
+            return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'unable to load driver class: '.$file_name);
+        }
+        return $file_name;
+    }
+
+    // }}}
+    // {{{ function apiVersion()
+
+    /**
+     * Return the MDB2 API version
+     *
+     * @return  string  the MDB2 API version number
+     *
+     * @access  public
+     */
+    function apiVersion()
+    {
+        return '2.5.0b2';
+    }
+
+    // }}}
+    // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
+
+    /**
+     * This method is used to communicate an error and invoke error
+     * callbacks etc.  Basically a wrapper for PEAR::raiseError
+     * without the message string.
+     *
+     * @param   mixed  int error code
+     *
+     * @param   int    error mode, see PEAR_Error docs
+     *
+     * @param   mixed  If error mode is PEAR_ERROR_TRIGGER, this is the
+     *                 error level (E_USER_NOTICE etc).  If error mode is
+     *                 PEAR_ERROR_CALLBACK, this is the callback function,
+     *                 either as a function name, or as an array of an
+     *                 object and method name.  For other error modes this
+     *                 parameter is ignored.
+     *
+     * @param   string Extra debug information.  Defaults to the last
+     *                 query and native error code.
+     *
+     * @return PEAR_Error instance of a PEAR Error object
+     *
+     * @access  private
+     * @see     PEAR_Error
+     */
+    function raiseError($code = null,
+                         $mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $dummy1 = null,
+                         $dummy2 = null,
+                         $dummy3 = false)
+    {
+        $err =PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
+        return $err;
+    }
+    static function raiseErrorStatic($code = null,
+                         $mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $dummy1 = null,
+                         $dummy2 = null,
+                         $dummy3 = false)
+    {
+	    $pear=new PEAR();
+        $err =$pear->raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
+        return $err;
+    }
+
+    // }}}
+    // {{{ function isError($data, $code = null)
+
+    /**
+     * Tell whether a value is a MDB2 error.
+     *
+     * @param   mixed   the value to test
+     * @param   int     if is an error object, return true
+     *                        only if $code is a string and
+     *                        $db->getMessage() == $code or
+     *                        $code is an integer and $db->getCode() == $code
+     *
+     * @return  bool    true if parameter is an error
+     *
+     * @access  public
+     */
+    static function isError($data, $code = null)
+    {
+        if ($data instanceof MDB2_Error) {
+            if (is_null($code)) {
+                return true;
+            } elseif (is_string($code)) {
+                return $data->getMessage() === $code;
+            } else {
+                $code = (array)$code;
+                return in_array($data->getCode(), $code);
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ function isConnection($value)
+
+    /**
+     * Tell whether a value is a MDB2 connection
+     *
+     * @param   mixed   value to test
+     *
+     * @return  bool    whether $value is a MDB2 connection
+     *
+     * @access  public
+     */
+    static function isConnection($value)
+    {
+        return ($value instanceof MDB2_Driver_Common);
+    }
+
+    // }}}
+    // {{{ function isResult($value)
+
+    /**
+     * Tell whether a value is a MDB2 result
+     *
+     * @param   mixed   value to test
+     *
+     * @return  bool    whether $value is a MDB2 result
+     *
+     * @access  public
+     */
+    static function isResult($value)
+    {
+        return $value instanceof MDB2_Result;
+    }
+
+    // }}}
+    // {{{ function isResultCommon($value)
+
+    /**
+     * Tell whether a value is a MDB2 result implementing the common interface
+     *
+     * @param   mixed   value to test
+     *
+     * @return  bool    whether $value is a MDB2 result implementing the common interface
+     *
+     * @access  public
+     */
+    static function isResultCommon($value)
+    {
+        return ($value instanceof MDB2_Result_Common);
+    }
+
+    // }}}
+    // {{{ function isStatement($value)
+
+    /**
+     * Tell whether a value is a MDB2 statement interface
+     *
+     * @param   mixed   value to test
+     *
+     * @return  bool    whether $value is a MDB2 statement interface
+     *
+     * @access  public
+     */
+    static function isStatement($value)
+    {
+        return $value instanceof MDB2_Statement_Common;
+    }
+
+    // }}}
+    // {{{ function errorMessage($value = null)
+
+    /**
+     * Return a textual error message for a MDB2 error code
+     *
+     * @param   int|array   integer error code,
+                                null to get the current error code-message map,
+                                or an array with a new error code-message map
+     *
+     * @return  string  error message, or false if the error code was
+     *                  not recognized
+     *
+     * @access  public
+     */
+    static function errorMessage($value = null)
+    {
+        static $errorMessages;
+
+        if (is_array($value)) {
+            $errorMessages = $value;
+            return MDB2_OK;
+        }
+
+        if (!isset($errorMessages)) {
+            $errorMessages = array(
+                MDB2_OK                       => 'no error',
+                MDB2_ERROR                    => 'unknown error',
+                MDB2_ERROR_ALREADY_EXISTS     => 'already exists',
+                MDB2_ERROR_CANNOT_CREATE      => 'can not create',
+                MDB2_ERROR_CANNOT_ALTER       => 'can not alter',
+                MDB2_ERROR_CANNOT_REPLACE     => 'can not replace',
+                MDB2_ERROR_CANNOT_DELETE      => 'can not delete',
+                MDB2_ERROR_CANNOT_DROP        => 'can not drop',
+                MDB2_ERROR_CONSTRAINT         => 'constraint violation',
+                MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
+                MDB2_ERROR_DIVZERO            => 'division by zero',
+                MDB2_ERROR_INVALID            => 'invalid',
+                MDB2_ERROR_INVALID_DATE       => 'invalid date or time',
+                MDB2_ERROR_INVALID_NUMBER     => 'invalid number',
+                MDB2_ERROR_MISMATCH           => 'mismatch',
+                MDB2_ERROR_NODBSELECTED       => 'no database selected',
+                MDB2_ERROR_NOSUCHFIELD        => 'no such field',
+                MDB2_ERROR_NOSUCHTABLE        => 'no such table',
+                MDB2_ERROR_NOT_CAPABLE        => 'MDB2 backend not capable',
+                MDB2_ERROR_NOT_FOUND          => 'not found',
+                MDB2_ERROR_NOT_LOCKED         => 'not locked',
+                MDB2_ERROR_SYNTAX             => 'syntax error',
+                MDB2_ERROR_UNSUPPORTED        => 'not supported',
+                MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
+                MDB2_ERROR_INVALID_DSN        => 'invalid DSN',
+                MDB2_ERROR_CONNECT_FAILED     => 'connect failed',
+                MDB2_ERROR_NEED_MORE_DATA     => 'insufficient data supplied',
+                MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
+                MDB2_ERROR_NOSUCHDB           => 'no such database',
+                MDB2_ERROR_ACCESS_VIOLATION   => 'insufficient permissions',
+                MDB2_ERROR_LOADMODULE         => 'error while including on demand module',
+                MDB2_ERROR_TRUNCATED          => 'truncated',
+                MDB2_ERROR_DEADLOCK           => 'deadlock detected',
+                MDB2_ERROR_NO_PERMISSION      => 'no permission',
+                MDB2_ERROR_DISCONNECT_FAILED  => 'disconnect failed',
+            );
+        }
+
+        if (is_null($value)) {
+            return $errorMessages;
+        }
+
+        if (PEAR::isError($value)) {
+            $value = $value->getCode();
+        }
+
+        return isset($errorMessages[$value]) ?
+           $errorMessages[$value] : $errorMessages[MDB2_ERROR];
+    }
+
+    // }}}
+    // {{{ function parseDSN($dsn)
+
+    /**
+     * Parse a data source name.
+     *
+     * Additional keys can be added by appending a URI query string to the
+     * end of the DSN.
+     *
+     * The format of the supplied DSN is in its fullest form:
+     * <code>
+     *  phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
+     * </code>
+     *
+     * Most variations are allowed:
+     * <code>
+     *  phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
+     *  phptype://username:password@hostspec/database_name
+     *  phptype://username:password@hostspec
+     *  phptype://username@hostspec
+     *  phptype://hostspec/database
+     *  phptype://hostspec
+     *  phptype(dbsyntax)
+     *  phptype
+     * </code>
+     *
+     * @param   string  Data Source Name to be parsed
+     *
+     * @return  array   an associative array with the following keys:
+     *  + phptype:  Database backend used in PHP (mysql, odbc etc.)
+     *  + dbsyntax: Database used with regards to SQL syntax etc.
+     *  + protocol: Communication protocol to use (tcp, unix etc.)
+     *  + hostspec: Host specification (hostname[:port])
+     *  + database: Database to use on the DBMS server
+     *  + username: User name for login
+     *  + password: Password for login
+     *
+     * @access  public
+     * @author  Tomas V.V.Cox <cox@idecnet.com>
+     */
+    static function parseDSN($dsn)
+    {
+        $parsed = $GLOBALS['_MDB2_dsninfo_default'];
+
+        if (is_array($dsn)) {
+            $dsn = array_merge($parsed, $dsn);
+            if (!$dsn['dbsyntax']) {
+                $dsn['dbsyntax'] = $dsn['phptype'];
+            }
+            return $dsn;
+        }
+
+        // Find phptype and dbsyntax
+        if (($pos = strpos($dsn, '://')) !== false) {
+            $str = substr($dsn, 0, $pos);
+            $dsn = substr($dsn, $pos + 3);
+        } else {
+            $str = $dsn;
+            $dsn = null;
+        }
+
+        // Get phptype and dbsyntax
+        // $str => phptype(dbsyntax)
+        if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
+            $parsed['phptype']  = $arr[1];
+            $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
+        } else {
+            $parsed['phptype']  = $str;
+            $parsed['dbsyntax'] = $str;
+        }
+
+        if (!count($dsn)) {
+            return $parsed;
+        }
+
+        // Get (if found): username and password
+        // $dsn => username:password@protocol+hostspec/database
+        if (($at = strrpos($dsn,'@')) !== false) {
+            $str = substr($dsn, 0, $at);
+            $dsn = substr($dsn, $at + 1);
+            if (($pos = strpos($str, ':')) !== false) {
+                $parsed['username'] = rawurldecode(substr($str, 0, $pos));
+                $parsed['password'] = rawurldecode(substr($str, $pos + 1));
+            } else {
+                $parsed['username'] = rawurldecode($str);
+            }
+        }
+
+        // Find protocol and hostspec
+
+        // $dsn => proto(proto_opts)/database
+        if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
+            $proto       = $match[1];
+            $proto_opts  = $match[2] ? $match[2] : false;
+            $dsn         = $match[3];
+
+        // $dsn => protocol+hostspec/database (old format)
+        } else {
+            if (strpos($dsn, '+') !== false) {
+                list($proto, $dsn) = explode('+', $dsn, 2);
+            }
+            if (   strpos($dsn, '//') === 0
+                && strpos($dsn, '/', 2) !== false
+                && $parsed['phptype'] == 'oci8'
+            ) {
+                //oracle's "Easy Connect" syntax:
+                //"username/password@[//]host[:port][/service_name]"
+                //e.g. "scott/tiger@//mymachine:1521/oracle"
+                $proto_opts = $dsn;
+                $dsn = substr($proto_opts, strrpos($proto_opts, '/') + 1);
+            } elseif (strpos($dsn, '/') !== false) {
+                list($proto_opts, $dsn) = explode('/', $dsn, 2);
+            } else {
+                $proto_opts = $dsn;
+                $dsn = null;
+            }
+        }
+
+        // process the different protocol options
+        $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
+        $proto_opts = rawurldecode($proto_opts);
+        if (strpos($proto_opts, ':') !== false) {
+            list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
+        }
+        if ($parsed['protocol'] == 'tcp') {
+            $parsed['hostspec'] = $proto_opts;
+        } elseif ($parsed['protocol'] == 'unix') {
+            $parsed['socket'] = $proto_opts;
+        }
+
+        // Get dabase if any
+        // $dsn => database
+        if ($dsn) {
+            // /database
+            if (($pos = strpos($dsn, '?')) === false) {
+                $parsed['database'] = $dsn;
+            // /database?param1=value1&param2=value2
+            } else {
+                $parsed['database'] = substr($dsn, 0, $pos);
+                $dsn = substr($dsn, $pos + 1);
+                if (strpos($dsn, '&') !== false) {
+                    $opts = explode('&', $dsn);
+                } else { // database?param1=value1
+                    $opts = array($dsn);
+                }
+                foreach ($opts as $opt) {
+                    list($key, $value) = explode('=', $opt);
+                    if (!isset($parsed[$key])) {
+                        // don't allow params overwrite
+                        $parsed[$key] = rawurldecode($value);
+                    }
+                }
+            }
+        }
+
+        return $parsed;
+    }
+
+    // }}}
+    // {{{ function fileExists($file)
+
+    /**
+     * Checks if a file exists in the include path
+     *
+     * @param   string  filename
+     *
+     * @return  bool    true success and false on error
+     *
+     * @access  public
+     */
+    static function fileExists($file)
+    {
+        // safe_mode does notwork with is_readable()
+        global $SERVERROOT;
+        if (!@ini_get('safe_mode')) {
+             $dirs = explode(PATH_SEPARATOR, ini_get('include_path'));
+             array_unshift($dirs,$SERVERROOT);
+             array_unshift($dirs,$SERVERROOT. DIRECTORY_SEPARATOR .'inc');
+//              print_r($dirs);die();
+             foreach ($dirs as $dir) {
+                 if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
+                     return true;
+                 }
+            }
+        } else {
+            $fp = @fopen($file, 'r', true);
+            if (is_resource($fp)) {
+                @fclose($fp);
+                return true;
+            }
+        }
+        return false;
+    }
+    // }}}
+}
+
+// }}}
+// {{{ class MDB2_Error extends PEAR_Error
+
+/**
+ * MDB2_Error implements a class for reporting portable database error
+ * messages.
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author Stig Bakken <ssb@fast.no>
+ */
+class MDB2_Error extends PEAR_Error
+{
+    // {{{ constructor: function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null)
+
+    /**
+     * MDB2_Error constructor.
+     *
+     * @param   mixed   MDB2 error code, or string with error message.
+     * @param   int     what 'error mode' to operate in
+     * @param   int     what error level to use for $mode raPEAR_ERROR_TRIGGER
+     * @param   mixed   additional debug info, such as the last query
+     */
+    function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN,
+              $level = E_USER_NOTICE, $debuginfo = null, $dummy = null)
+    {
+        if (is_null($code)) {
+            $code = MDB2_ERROR;
+        }
+        $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code,
+            $mode, $level, $debuginfo);
+    }
+
+    // }}}
+}
+
+// }}}
+// {{{ class MDB2_Driver_Common extends PEAR
+
+/**
+ * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Common extends PEAR
+{
+    // {{{ Variables (Properties)
+
+    /**
+     * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array
+     * @var     int
+     * @access  public
+     */
+    var $db_index = 0;
+
+    /**
+     * DSN used for the next query
+     * @var     array
+     * @access  protected
+     */
+    var $dsn = array();
+
+    /**
+     * DSN that was used to create the current connection
+     * @var     array
+     * @access  protected
+     */
+    var $connected_dsn = array();
+
+    /**
+     * connection resource
+     * @var     mixed
+     * @access  protected
+     */
+    var $connection = 0;
+
+    /**
+     * if the current opened connection is a persistent connection
+     * @var     bool
+     * @access  protected
+     */
+    var $opened_persistent;
+
+    /**
+     * the name of the database for the next query
+     * @var     string
+     * @access  protected
+     */
+    var $database_name = '';
+
+    /**
+     * the name of the database currently selected
+     * @var     string
+     * @access  protected
+     */
+    var $connected_database_name = '';
+
+    /**
+     * server version information
+     * @var     string
+     * @access  protected
+     */
+    var $connected_server_info = '';
+
+    /**
+     * list of all supported features of the given driver
+     * @var     array
+     * @access  public
+     */
+    var $supported = array(
+        'sequences' => false,
+        'indexes' => false,
+        'affected_rows' => false,
+        'summary_functions' => false,
+        'order_by_text' => false,
+        'transactions' => false,
+        'savepoints' => false,
+        'current_id' => false,
+        'limit_queries' => false,
+        'LOBs' => false,
+        'replace' => false,
+        'sub_selects' => false,
+        'triggers' => false,
+        'auto_increment' => false,
+        'primary_key' => false,
+        'result_introspection' => false,
+        'prepared_statements' => false,
+        'identifier_quoting' => false,
+        'pattern_escaping' => false,
+        'new_link' => false,
+    );
+
+    /**
+     * Array of supported options that can be passed to the MDB2 instance.
+     * 
+     * The options can be set during object creation, using
+     * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can 
+     * also be set after the object is created, using MDB2::setOptions() or 
+     * MDB2_Driver_Common::setOption().
+     * The list of available option includes:
+     * <ul>
+     *  <li>$options['ssl'] -> boolean: determines if ssl should be used for connections</li>
+     *  <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names</li>
+     *  <li>$options['disable_query'] -> boolean: determines if queries should be executed</li>
+     *  <li>$options['result_class'] -> string: class used for result sets</li>
+     *  <li>$options['buffered_result_class'] -> string: class used for buffered result sets</li>
+     *  <li>$options['result_wrap_class'] -> string: class used to wrap result sets into</li>
+     *  <li>$options['result_buffering'] -> boolean should results be buffered or not?</li>
+     *  <li>$options['fetch_class'] -> string: class to use when fetch mode object is used</li>
+     *  <li>$options['persistent'] -> boolean: persistent connection?</li>
+     *  <li>$options['debug'] -> integer: numeric debug level</li>
+     *  <li>$options['debug_handler'] -> string: function/method that captures debug messages</li>
+     *  <li>$options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler</li>
+     *  <li>$options['default_text_field_length'] -> integer: default text field length to use</li>
+     *  <li>$options['lob_buffer_length'] -> integer: LOB buffer length</li>
+     *  <li>$options['log_line_break'] -> string: line-break format</li>
+     *  <li>$options['idxname_format'] -> string: pattern for index name</li>
+     *  <li>$options['seqname_format'] -> string: pattern for sequence name</li>
+     *  <li>$options['savepoint_format'] -> string: pattern for auto generated savepoint names</li>
+     *  <li>$options['statement_format'] -> string: pattern for prepared statement names</li>
+     *  <li>$options['seqcol_name'] -> string: sequence column name</li>
+     *  <li>$options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used</li>
+     *  <li>$options['use_transactions'] -> boolean: if transaction use should be enabled</li>
+     *  <li>$options['decimal_places'] -> integer: number of decimal places to handle</li>
+     *  <li>$options['portability'] -> integer: portability constant</li>
+     *  <li>$options['modules'] -> array: short to long module name mapping for __call()</li>
+     *  <li>$options['emulate_prepared'] -> boolean: force prepared statements to be emulated</li>
+     *  <li>$options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes</li>
+     *  <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li>
+     *  <li>$options['bindname_format'] -> string: regular expression pattern for named parameters</li>
+     *  <li>$options['multi_query'] -> boolean: determines if queries returning multiple result sets should be executed</li>
+     *  <li>$options['max_identifiers_length'] -> integer: max identifier length</li>
+     *  <li>$options['default_fk_action_onupdate'] -> string: default FOREIGN KEY ON UPDATE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li>
+     *  <li>$options['default_fk_action_ondelete'] -> string: default FOREIGN KEY ON DELETE action ['RESTRICT'|'NO ACTION'|'SET DEFAULT'|'SET NULL'|'CASCADE']</li>
+     * </ul>
+     *
+     * @var     array
+     * @access  public
+     * @see     MDB2::connect()
+     * @see     MDB2::factory()
+     * @see     MDB2::singleton()
+     * @see     MDB2_Driver_Common::setOption()
+     */
+    var $options = array(
+        'ssl' => false,
+        'field_case' => CASE_LOWER,
+        'disable_query' => false,
+        'result_class' => 'MDB2_Result_%s',
+        'buffered_result_class' => 'MDB2_BufferedResult_%s',
+        'result_wrap_class' => false,
+        'result_buffering' => true,
+        'fetch_class' => 'stdClass',
+        'persistent' => false,
+        'debug' => 0,
+        'debug_handler' => 'MDB2_defaultDebugOutput',
+        'debug_expanded_output' => false,
+        'default_text_field_length' => 4096,
+        'lob_buffer_length' => 8192,
+        'log_line_break' => "\n",
+        'idxname_format' => '%s_idx',
+        'seqname_format' => '%s_seq',
+        'savepoint_format' => 'MDB2_SAVEPOINT_%s',
+        'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s',
+        'seqcol_name' => 'sequence',
+        'quote_identifier' => false,
+        'use_transactions' => true,
+        'decimal_places' => 2,
+        'portability' => MDB2_PORTABILITY_ALL,
+        'modules' => array(
+            'ex' => 'Extended',
+            'dt' => 'Datatype',
+            'mg' => 'Manager',
+            'rv' => 'Reverse',
+            'na' => 'Native',
+            'fc' => 'Function',
+        ),
+        'emulate_prepared' => false,
+        'datatype_map' => array(),
+        'datatype_map_callback' => array(),
+        'nativetype_map_callback' => array(),
+        'lob_allow_url_include' => false,
+        'bindname_format' => '(?:\d+)|(?:[a-zA-Z][a-zA-Z0-9_]*)',
+        'max_identifiers_length' => 30,
+        'default_fk_action_onupdate' => 'RESTRICT',
+        'default_fk_action_ondelete' => 'RESTRICT',
+    );
+
+    /**
+     * string array
+     * @var     string
+     * @access  protected
+     */
+    var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false);
+
+    /**
+     * identifier quoting
+     * @var     array
+     * @access  protected
+     */
+    var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
+
+    /**
+     * sql comments
+     * @var     array
+     * @access  protected
+     */
+    var $sql_comments = array(
+        array('start' => '--', 'end' => "\n", 'escape' => false),
+        array('start' => '/*', 'end' => '*/', 'escape' => false),
+    );
+
+    /**
+     * comparision wildcards
+     * @var     array
+     * @access  protected
+     */
+    var $wildcards = array('%', '_');
+
+    /**
+     * column alias keyword
+     * @var     string
+     * @access  protected
+     */
+    var $as_keyword = ' AS ';
+
+    /**
+     * warnings
+     * @var     array
+     * @access  protected
+     */
+    var $warnings = array();
+
+    /**
+     * string with the debugging information
+     * @var     string
+     * @access  public
+     */
+    var $debug_output = '';
+
+    /**
+     * determine if there is an open transaction
+     * @var     bool
+     * @access  protected
+     */
+    var $in_transaction = false;
+
+    /**
+     * the smart transaction nesting depth
+     * @var     int
+     * @access  protected
+     */
+    var $nested_transaction_counter = null;
+
+    /**
+     * the first error that occured inside a nested transaction
+     * @var     MDB2_Error|bool
+     * @access  protected
+     */
+    var $has_transaction_error = false;
+
+    /**
+     * result offset used in the next query
+     * @var     int
+     * @access  protected
+     */
+    var $offset = 0;
+
+    /**
+     * result limit used in the next query
+     * @var     int
+     * @access  protected
+     */
+    var $limit = 0;
+
+    /**
+     * Database backend used in PHP (mysql, odbc etc.)
+     * @var     string
+     * @access  public
+     */
+    var $phptype;
+
+    /**
+     * Database used with regards to SQL syntax etc.
+     * @var     string
+     * @access  public
+     */
+    var $dbsyntax;
+
+    /**
+     * the last query sent to the driver
+     * @var     string
+     * @access  public
+     */
+    var $last_query;
+
+    /**
+     * the default fetchmode used
+     * @var     int
+     * @access  protected
+     */
+    var $fetchmode = MDB2_FETCHMODE_ORDERED;
+
+    /**
+     * array of module instances
+     * @var     array
+     * @access  protected
+     */
+    var $modules = array();
+
+    /**
+     * determines of the PHP4 destructor emulation has been enabled yet
+     * @var     array
+     * @access  protected
+     */
+    var $destructor_registered = true;
+
+    // }}}
+    // {{{ constructor: function __construct()
+
+    /**
+     * Constructor
+     */
+    function __construct()
+    {
+        end($GLOBALS['_MDB2_databases']);
+        $db_index = key($GLOBALS['_MDB2_databases']) + 1;
+        $GLOBALS['_MDB2_databases'][$db_index] = &$this;
+        $this->db_index = $db_index;
+    }
+
+
+    // }}}
+    // {{{ destructor: function __destruct()
+
+    /**
+     *  Destructor
+     */
+    function __destruct()
+    {
+        $this->disconnect(false);
+    }
+
+    // }}}
+    // {{{ function free()
+
+    /**
+     * Free the internal references so that the instance can be destroyed
+     *
+     * @return  bool    true on success, false if result is invalid
+     *
+     * @access  public
+     */
+    function free()
+    {
+        unset($GLOBALS['_MDB2_databases'][$this->db_index]);
+        unset($this->db_index);
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function __toString()
+
+    /**
+     * String conversation
+     *
+     * @return  string representation of the object
+     *
+     * @access  public
+     */
+    function __toString()
+    {
+        $info = get_class($this);
+        $info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.')';
+        if ($this->connection) {
+            $info.= ' [connected]';
+        }
+        return $info;
+    }
+
+    // }}}
+    // {{{ function errorInfo($error = null)
+
+    /**
+     * This method is used to collect information about an error
+     *
+     * @param   mixed   error code or resource
+     *
+     * @return  array   with MDB2 errorcode, native error code, native message
+     *
+     * @access  public
+     */
+    function errorInfo($error = null)
+    {
+        return array($error, null, null);
+    }
+
+    // }}}
+    // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
+
+    /**
+     * This method is used to communicate an error and invoke error
+     * callbacks etc.  Basically a wrapper for PEAR::raiseError
+     * without the message string.
+     *
+     * @param mixed  $code     integer error code, or a PEAR error object (all 
+     *                         other parameters are ignored if this parameter is
+     *                         an object
+     * @param int    $mode     error mode, see PEAR_Error docs
+     * @param mixed  $options  If error mode is PEAR_ERROR_TRIGGER, this is the
+     *                         error level (E_USER_NOTICE etc). If error mode is
+     *                         PEAR_ERROR_CALLBACK, this is the callback function,
+     *                         either as a function name, or as an array of an
+     *                         object and method name. For other error modes this
+     *                         parameter is ignored.
+     * @param string $userinfo Extra debug information. Defaults to the last
+     *                         query and native error code.
+     * @param string $method   name of the method that triggered the error
+     * @param string $dummy1   not used
+     * @param bool   $dummy2   not used
+     *
+     * @return PEAR_Error instance of a PEAR Error object
+     * @access public
+     * @see    PEAR_Error
+     */
+    function raiseError($code = null,
+                         $mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $method = null,
+                         $dummy1 = null,
+                         $dummy2 = false
+    ) {
+        $userinfo = "[Error message: $userinfo]\n";
+        // The error is yet a MDB2 error object
+        if (PEAR::isError($code)) {
+            // because we use the static PEAR::raiseError, our global
+            // handler should be used if it is set
+            if (is_null($mode) && !empty($this->_default_error_mode)) {
+                $mode    = $this->_default_error_mode;
+                $options = $this->_default_error_options;
+            }
+            if (is_null($userinfo)) {
+                $userinfo = $code->getUserinfo();
+            }
+            $code = $code->getCode();
+        } elseif ($code == MDB2_ERROR_NOT_FOUND) {
+            // extension not loaded: don't call $this->errorInfo() or the script
+            // will die
+        } elseif (isset($this->connection)) {
+            if (!empty($this->last_query)) {
+                $userinfo.= "[Last executed query: {$this->last_query}]\n";
+            }
+            $native_errno = $native_msg = null;
+            list($code, $native_errno, $native_msg) = $this->errorInfo($code);
+            if (!is_null($native_errno) && $native_errno !== '') {
+                $userinfo.= "[Native code: $native_errno]\n";
+            }
+            if (!is_null($native_msg) && $native_msg !== '') {
+                $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n";
+            }
+            echo $userinfo;
+            if (!is_null($method)) {
+                $userinfo = $method.': '.$userinfo;
+            }
+        }
+
+        $err = PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
+        if ($err->getMode() !== PEAR_ERROR_RETURN
+            && isset($this->nested_transaction_counter) && !$this->has_transaction_error) {
+            $this->has_transaction_error =$err;
+        }
+        return $err;
+    }
+
+    // }}}
+    // {{{ function resetWarnings()
+
+    /**
+     * reset the warning array
+     *
+     * @return void
+     *
+     * @access  public
+     */
+    function resetWarnings()
+    {
+        $this->warnings = array();
+    }
+
+    // }}}
+    // {{{ function getWarnings()
+
+    /**
+     * Get all warnings in reverse order.
+     * This means that the last warning is the first element in the array
+     *
+     * @return  array   with warnings
+     *
+     * @access  public
+     * @see     resetWarnings()
+     */
+    function getWarnings()
+    {
+        return array_reverse($this->warnings);
+    }
+
+    // }}}
+    // {{{ function setFetchMode($fetchmode, $object_class = 'stdClass')
+
+    /**
+     * Sets which fetch mode should be used by default on queries
+     * on this connection
+     *
+     * @param   int     MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC
+     *                               or MDB2_FETCHMODE_OBJECT
+     * @param   string  the class name of the object to be returned
+     *                               by the fetch methods when the
+     *                               MDB2_FETCHMODE_OBJECT mode is selected.
+     *                               If no class is specified by default a cast
+     *                               to object from the assoc array row will be
+     *                               done.  There is also the possibility to use
+     *                               and extend the 'MDB2_row' class.
+     *
+     * @return  mixed   MDB2_OK or MDB2 Error Object
+     *
+     * @access  public
+     * @see     MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT
+     */
+    function setFetchMode($fetchmode, $object_class = 'stdClass')
+    {
+        switch ($fetchmode) {
+        case MDB2_FETCHMODE_OBJECT:
+            $this->options['fetch_class'] = $object_class;
+        case MDB2_FETCHMODE_ORDERED:
+        case MDB2_FETCHMODE_ASSOC:
+            $this->fetchmode = $fetchmode;
+            break;
+        default:
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'invalid fetchmode mode', __FUNCTION__);
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function setOption($option, $value)
+
+    /**
+     * set the option for the db class
+     *
+     * @param   string  option name
+     * @param   mixed   value for the option
+     *
+     * @return  mixed   MDB2_OK or MDB2 Error Object
+     *
+     * @access  public
+     */
+    function setOption($option, $value)
+    {
+        if (array_key_exists($option, $this->options)) {
+            $this->options[$option] = $value;
+            return MDB2_OK;
+        }
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            "unknown option $option", __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function getOption($option)
+
+    /**
+     * Returns the value of an option
+     *
+     * @param   string  option name
+     *
+     * @return  mixed   the option value or error object
+     *
+     * @access  public
+     */
+    function getOption($option)
+    {
+        if (array_key_exists($option, $this->options)) {
+            return $this->options[$option];
+        }
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            "unknown option $option", __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function debug($message, $scope = '', $is_manip = null)
+
+    /**
+     * set a debug message
+     *
+     * @param   string  message that should be appended to the debug variable
+     * @param   string  usually the method name that triggered the debug call:
+     *                  for example 'query', 'prepare', 'execute', 'parameters',
+     *                  'beginTransaction', 'commit', 'rollback'
+     * @param   array   contains context information about the debug() call
+     *                  common keys are: is_manip, time, result etc.
+     *
+     * @return void
+     *
+     * @access  public
+     */
+    function debug($message, $scope = '', $context = array())
+    {
+        if ($this->options['debug'] && $this->options['debug_handler']) {
+            if (!$this->options['debug_expanded_output']) {
+                if (!empty($context['when']) && $context['when'] !== 'pre') {
+                    return null;
+                }
+                $context = empty($context['is_manip']) ? false : $context['is_manip'];
+            }
+            return call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $context));
+        }
+        return null;
+    }
+
+    // }}}
+    // {{{ function getDebugOutput()
+
+    /**
+     * output debug info
+     *
+     * @return  string  content of the debug_output class variable
+     *
+     * @access  public
+     */
+    function getDebugOutput()
+    {
+        return $this->debug_output;
+    }
+
+    // }}}
+    // {{{ function escape($text)
+
+    /**
+     * 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
+     */
+    function escape($text, $escape_wildcards = false)
+    {
+        if ($escape_wildcards) {
+            $text = $this->escapePattern($text);
+        }
+
+        $text = str_replace($this->string_quoting['end'], $this->string_quoting['escape'] . $this->string_quoting['end'], $text);
+        return $text;
+    }
+
+    // }}}
+    // {{{ function escapePattern($text)
+
+    /**
+     * Quotes pattern (% and _) characters in a string)
+     *
+     * @param   string  the input string to quote
+     *
+     * @return  string  quoted string
+     *
+     * @access  public
+     */
+    function escapePattern($text)
+    {
+        if ($this->string_quoting['escape_pattern']) {
+            $text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text);
+            foreach ($this->wildcards as $wildcard) {
+                $text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text);
+            }
+        }
+        return $text;
+    }
+
+    // }}}
+    // {{{ function quoteIdentifier($str, $check_option = false)
+
+    /**
+     * Quote a string so it can be safely used as a table or column name
+     *
+     * Delimiting style depends on which database driver is being used.
+     *
+     * NOTE: just because you CAN use delimited identifiers doesn't mean
+     * you SHOULD use them.  In general, they end up causing way more
+     * problems than they solve.
+     *
+     * NOTE: if you have table names containing periods, don't use this method
+     * (@see bug #11906)
+     *
+     * Portability is broken by using the following characters inside
+     * delimited identifiers:
+     *   + backtick (<kbd>`</kbd>) -- due to MySQL
+     *   + double quote (<kbd>"</kbd>) -- due to Oracle
+     *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
+     *
+     * Delimited identifiers are known to generally work correctly under
+     * the following drivers:
+     *   + mssql
+     *   + mysql
+     *   + mysqli
+     *   + oci8
+     *   + pgsql
+     *   + sqlite
+     *
+     * InterBase doesn't seem to be able to use delimited identifiers
+     * via PHP 4.  They work fine under PHP 5.
+     *
+     * @param   string  identifier name to be quoted
+     * @param   bool    check the 'quote_identifier' option
+     *
+     * @return  string  quoted identifier string
+     *
+     * @access  public
+     */
+    function quoteIdentifier($str, $check_option = false)
+    {
+        if ($check_option && !$this->options['quote_identifier']) {
+            return $str;
+        }
+        $str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str);
+        $parts = explode('.', $str);
+        foreach (array_keys($parts) as $k) {
+            $parts[$k] = $this->identifier_quoting['start'] . $parts[$k] . $this->identifier_quoting['end'];
+        }
+        return implode('.', $parts);
+    }
+
+    // }}}
+    // {{{ function getAsKeyword()
+
+    /**
+     * Gets the string to alias column
+     *
+     * @return string to use when aliasing a column
+     */
+    function getAsKeyword()
+    {
+        return $this->as_keyword;
+    }
+
+    // }}}
+    // {{{ function getConnection()
+
+    /**
+     * Returns a native connection
+     *
+     * @return  mixed   a valid MDB2 connection object,
+     *                  or a MDB2 error object on error
+     *
+     * @access  public
+     */
+    function getConnection()
+    {
+        $result = $this->connect();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        return $this->connection;
+    }
+
+     // }}}
+    // {{{ function _fixResultArrayValues(&$row, $mode)
+
+    /**
+     * Do all necessary conversions on result arrays to fix DBMS quirks
+     *
+     * @param   array   the array to be fixed (passed by reference)
+     * @param   array   bit-wise addition of the required portability modes
+     *
+     * @return  void
+     *
+     * @access  protected
+     */
+    function _fixResultArrayValues(&$row, $mode)
+    {
+        switch ($mode) {
+        case MDB2_PORTABILITY_EMPTY_TO_NULL:
+            foreach ($row as $key => $value) {
+                if ($value === '') {
+                    $row[$key] = null;
+                }
+            }
+            break;
+        case MDB2_PORTABILITY_RTRIM:
+            foreach ($row as $key => $value) {
+                if (is_string($value)) {
+                    $row[$key] = rtrim($value);
+                }
+            }
+            break;
+        case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES:
+            $tmp_row = array();
+            foreach ($row as $key => $value) {
+                $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
+            }
+            $row = $tmp_row;
+            break;
+        case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL):
+            foreach ($row as $key => $value) {
+                if ($value === '') {
+                    $row[$key] = null;
+                } elseif (is_string($value)) {
+                    $row[$key] = rtrim($value);
+                }
+            }
+            break;
+        case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
+            $tmp_row = array();
+            foreach ($row as $key => $value) {
+                if (is_string($value)) {
+                    $value = rtrim($value);
+                }
+                $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
+            }
+            $row = $tmp_row;
+            break;
+        case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
+            $tmp_row = array();
+            foreach ($row as $key => $value) {
+                if ($value === '') {
+                    $value = null;
+                }
+                $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
+            }
+            $row = $tmp_row;
+            break;
+        case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
+            $tmp_row = array();
+            foreach ($row as $key => $value) {
+                if ($value === '') {
+                    $value = null;
+                } elseif (is_string($value)) {
+                    $value = rtrim($value);
+                }
+                $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
+            }
+            $row = $tmp_row;
+            break;
+        }
+    }
+
+    // }}}
+    // {{{ function &loadModule($module, $property = null, $phptype_specific = null)
+
+    /**
+     * loads a module
+     *
+     * @param   string  name of the module that should be loaded
+     *                  (only used for error messages)
+     * @param   string  name of the property into which the class will be loaded
+     * @param   bool    if the class to load for the module is specific to the
+     *                  phptype
+     *
+     * @return  object  on success a reference to the given module is returned
+     *                  and on failure a PEAR error
+     *
+     * @access  public
+     */
+    function &loadModule($module, $property = null, $phptype_specific = null)
+    {
+        if (!$property) {
+            $property = strtolower($module);
+        }
+
+        if (!isset($this->{$property})) {
+            $version = $phptype_specific;
+            if ($phptype_specific !== false) {
+                $version = true;
+                $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype;
+                $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
+            }
+            if ($phptype_specific === false
+                || (!MDB2::classExists($class_name) && !MDB2::fileExists($file_name))
+            ) {
+                $version = false;
+                $class_name = 'MDB2_'.$module;
+                $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
+            }
+
+            $err = MDB2::loadClass($class_name, $this->getOption('debug'));
+            if (PEAR::isError($err)) {
+                return $err;
+            }
+            // load module in a specific version
+            if ($version) {
+                if (method_exists($class_name, 'getClassName')) {
+                    $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index);
+                    if ($class_name != $class_name_new) {
+                        $class_name = $class_name_new;
+                        $err = MDB2::loadClass($class_name, $this->getOption('debug'));
+                        if (PEAR::isError($err)) {
+                            return $err;
+                        }
+                    }
+                }
+            }
+
+            if (!MDB2::classExists($class_name)) {
+                $err =$this->raiseError(MDB2_ERROR_LOADMODULE, null, null,
+                    "unable to load module '$module' into property '$property'", __FUNCTION__);
+                return $err;
+            }
+            $this->{$property} = new $class_name($this->db_index);
+            $this->modules[$module] =$this->{$property};
+            if ($version) {
+                // this will be used in the connect method to determine if the module
+                // needs to be loaded with a different version if the server
+                // version changed in between connects
+                $this->loaded_version_modules[] = $property;
+            }
+        }
+
+        return $this->{$property};
+    }
+
+    // }}}
+    // {{{ function __call($method, $params)
+
+    /**
+     * Calls a module method using the __call magic method
+     *
+     * @param   string  Method name.
+     * @param   array   Arguments.
+     *
+     * @return  mixed   Returned value.
+     */
+    function __call($method, $params)
+    {
+        $module = null;
+        if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
+            && isset($this->options['modules'][$match[1]])
+        ) {
+            $module = $this->options['modules'][$match[1]];
+            $method = strtolower($match[2]).$match[3];
+            if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) {
+                $result =& $this->loadModule($module);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        } else {
+            foreach ($this->modules as $key => $foo) {
+                if (is_object($this->modules[$key])
+                    && method_exists($this->modules[$key], $method)
+                ) {
+                    $module = $key;
+                    break;
+                }
+            }
+        }
+        if (!is_null($module)) {
+            return call_user_func_array(array(&$this->modules[$module], $method), $params);
+        }
+        trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR);
+    }
+
+    // }}}
+    // {{{ function beginTransaction($savepoint = null)
+
+    /**
+     * 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', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'transactions are not supported', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function commit($savepoint = null)
+
+    /**
+     * 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));
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'commiting transactions is not supported', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function rollback($savepoint = null)
+
+    /**
+     * 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));
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'rolling back transactions is not supported', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function inTransaction($ignore_nested = false)
+
+    /**
+     * If a transaction is currently open.
+     *
+     * @param   bool    if the nested transaction count should be ignored
+     * @return  int|bool    - an integer with the nesting depth is returned if a
+     *                      nested transaction is open
+     *                      - true is returned for a normal open transaction
+     *                      - false is returned if no transaction is open
+     *
+     * @access  public
+     */
+    function inTransaction($ignore_nested = false)
+    {
+        if (!$ignore_nested && isset($this->nested_transaction_counter)) {
+            return $this->nested_transaction_counter;
+        }
+        return $this->in_transaction;
+    }
+
+    // }}}
+    // {{{ function setTransactionIsolation($isolation)
+
+    /**
+     * 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)
+     * @param   array some transaction options:
+     *                  'wait' => 'WAIT' | 'NO WAIT'
+     *                  'rw'   => 'READ WRITE' | 'READ ONLY'
+     * @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));
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'isolation level setting is not supported', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function beginNestedTransaction($savepoint = false)
+
+    /**
+     * Start a nested transaction.
+     *
+     * @return  mixed   MDB2_OK on success/savepoint name, a MDB2 error on failure
+     *
+     * @access  public
+     * @since   2.1.1
+     */
+    function beginNestedTransaction()
+    {
+        if ($this->in_transaction) {
+            ++$this->nested_transaction_counter;
+            $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
+            if ($this->supports('savepoints') && $savepoint) {
+                return $this->beginTransaction($savepoint);
+            }
+            return MDB2_OK;
+        }
+        $this->has_transaction_error = false;
+        $result = $this->beginTransaction();
+        $this->nested_transaction_counter = 1;
+        return $result;
+    }
+
+    // }}}
+    // {{{ function completeNestedTransaction($force_rollback = false, $release = false)
+
+    /**
+     * Finish a nested transaction by rolling back if an error occured or
+     * committing otherwise.
+     *
+     * @param   bool    if the transaction should be rolled back regardless
+     *                  even if no error was set within the nested transaction
+     * @return  mixed   MDB_OK on commit/counter decrementing, false on rollback
+     *                  and a MDB2 error on failure
+     *
+     * @access  public
+     * @since   2.1.1
+     */
+    function completeNestedTransaction($force_rollback = false)
+    {
+        if ($this->nested_transaction_counter > 1) {
+            $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
+            if ($this->supports('savepoints') && $savepoint) {
+                if ($force_rollback || $this->has_transaction_error) {
+                    $result = $this->rollback($savepoint);
+                    if (!PEAR::isError($result)) {
+                        $result = false;
+                        $this->has_transaction_error = false;
+                    }
+                } else {
+                    $result = $this->commit($savepoint);
+                }
+            } else {
+                $result = MDB2_OK;
+            }
+            --$this->nested_transaction_counter;
+            return $result;
+        }
+
+        $this->nested_transaction_counter = null;
+        $result = MDB2_OK;
+
+        // transaction has not yet been rolled back
+        if ($this->in_transaction) {
+            if ($force_rollback || $this->has_transaction_error) {
+                $result = $this->rollback();
+                if (!PEAR::isError($result)) {
+                    $result = false;
+                }
+            } else {
+                $result = $this->commit();
+            }
+        }
+        $this->has_transaction_error = false;
+        return $result;
+    }
+
+    // }}}
+    // {{{ function failNestedTransaction($error = null, $immediately = false)
+
+    /**
+     * Force setting nested transaction to failed.
+     *
+     * @param   mixed   value to return in getNestededTransactionError()
+     * @param   bool    if the transaction should be rolled back immediately
+     * @return  bool    MDB2_OK
+     *
+     * @access  public
+     * @since   2.1.1
+     */
+    function failNestedTransaction($error = null, $immediately = false)
+    {
+        if (is_null($error)) {
+            $error = $this->has_transaction_error ? $this->has_transaction_error : true;
+        } elseif (!$error) {
+            $error = true;
+        }
+        $this->has_transaction_error = $error;
+        if (!$immediately) {
+            return MDB2_OK;
+        }
+        return $this->rollback();
+    }
+
+    // }}}
+    // {{{ function getNestedTransactionError()
+
+    /**
+     * The first error that occured since the transaction start.
+     *
+     * @return  MDB2_Error|bool     MDB2 error object if an error occured or false.
+     *
+     * @access  public
+     * @since   2.1.1
+     */
+    function getNestedTransactionError()
+    {
+        return $this->has_transaction_error;
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database
+     *
+     * @return true on success, MDB2 Error Object on failure
+     */
+    function connect()
+    {
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ setCharset($charset, $connection = null)
+
+    /**
+     * Set the charset on the current connection
+     *
+     * @param string    charset
+     * @param resource  connection handle
+     *
+     * @return true on success, MDB2 Error Object on failure
+     */
+    function setCharset($charset, $connection = null)
+    {
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function disconnect($force = true)
+
+    /**
+     * Log out and disconnect from the database.
+     *
+     * @param boolean $force whether 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)
+    {
+        $this->connection = 0;
+        $this->connected_dsn = array();
+        $this->connected_database_name = '';
+        $this->opened_persistent = null;
+        $this->connected_server_info = '';
+        $this->in_transaction = null;
+        $this->nested_transaction_counter = null;
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function setDatabase($name)
+
+    /**
+     * Select a different database
+     *
+     * @param   string  name of the database that should be selected
+     *
+     * @return  string  name of the database previously connected to
+     *
+     * @access  public
+     */
+    function setDatabase($name)
+    {
+        $previous_database_name = (isset($this->database_name)) ? $this->database_name : '';
+        $this->database_name = $name;
+        if (!empty($this->connected_database_name) && ($this->connected_database_name != $this->database_name)) {
+            $this->disconnect(false);
+        }
+        return $previous_database_name;
+    }
+
+    // }}}
+    // {{{ function getDatabase()
+
+    /**
+     * Get the current database
+     *
+     * @return  string  name of the database
+     *
+     * @access  public
+     */
+    function getDatabase()
+    {
+        return $this->database_name;
+    }
+
+    // }}}
+    // {{{ function setDSN($dsn)
+
+    /**
+     * set the DSN
+     *
+     * @param   mixed   DSN string or array
+     *
+     * @return  MDB2_OK
+     *
+     * @access  public
+     */
+    function setDSN($dsn)
+    {
+        $dsn_default = $GLOBALS['_MDB2_dsninfo_default'];
+        $dsn = MDB2::parseDSN($dsn);
+        if (array_key_exists('database', $dsn)) {
+            $this->database_name = $dsn['database'];
+            unset($dsn['database']);
+        }
+        $this->dsn = array_merge($dsn_default, $dsn);
+        return $this->disconnect(false);
+    }
+
+    // }}}
+    // {{{ function getDSN($type = 'string', $hidepw = false)
+
+    /**
+     * return the DSN as a string
+     *
+     * @param   string  format to return ("array", "string")
+     * @param   string  string to hide the password with
+     *
+     * @return  mixed   DSN in the chosen type
+     *
+     * @access  public
+     */
+    function getDSN($type = 'string', $hidepw = false)
+    {
+        $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn);
+        $dsn['phptype'] = $this->phptype;
+        $dsn['database'] = $this->database_name;
+        if ($hidepw) {
+            $dsn['password'] = $hidepw;
+        }
+        switch ($type) {
+        // expand to include all possible options
+        case 'string':
+           $dsn = $dsn['phptype'].
+               ($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : '').
+               '://'.$dsn['username'].':'.
+                $dsn['password'].'@'.$dsn['hostspec'].
+                ($dsn['port'] ? (':'.$dsn['port']) : '').
+                '/'.$dsn['database'];
+            break;
+        case 'array':
+        default:
+            break;
+        }
+        return $dsn;
+    }
+
+    // }}}
+    // {{{ _isNewLinkSet()
+
+    /**
+     * Check if the 'new_link' option is set
+     *
+     * @return boolean
+     *
+     * @access protected
+     */
+    function _isNewLinkSet()
+    {
+        return (isset($this->dsn['new_link'])
+            && ($this->dsn['new_link'] === true
+             || (is_string($this->dsn['new_link']) && preg_match('/^true$/i', $this->dsn['new_link']))
+             || (is_numeric($this->dsn['new_link']) && 0 != (int)$this->dsn['new_link'])
+            )
+        );
+    }
+
+    // }}}
+    // {{{ function &standaloneQuery($query, $types = null, $is_manip = false)
+
+   /**
+     * execute a query as database administrator
+     *
+     * @param   string  the SQL query
+     * @param   mixed   array that contains the types of the columns in
+     *                        the result set
+     * @param   bool    if the query is a manipulation query
+     *
+     * @return  mixed   MDB2_OK on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function &standaloneQuery($query, $types = null, $is_manip = false)
+    {
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
+
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $result =$this->_doQuery($query, $is_manip, $connection, false);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        if ($is_manip) {
+            $affected_rows =  $this->_affectedRows($connection, $result);
+            return $affected_rows;
+        }
+        $result =$this->_wrapResult($result, $types, true, false, $limit, $offset);
+        return $result;
+    }
+
+    // }}}
+    // {{{ function _modifyQuery($query, $is_manip, $limit, $offset)
+
+    /**
+     * Changes a query string for various DBMS specific reasons
+     *
+     * @param   string  query to modify
+     * @param   bool    if it is a DML query
+     * @param   int  limit the number of rows
+     * @param   int  start reading from given offset
+     *
+     * @return  string  modified query
+     *
+     * @access  protected
+     */
+    function _modifyQuery($query, $is_manip, $limit, $offset)
+    {
+        return $query;
+    }
+
+    // }}}
+    // {{{ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
+
+    /**
+     * Execute a query
+     * @param   string  query
+     * @param   bool    if the query is a manipulation query
+     * @param   resource connection handle
+     * @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;
+        }
+        $err =$this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+        return $err;
+    }
+
+    // }}}
+    // {{{ function _affectedRows($connection, $result = null)
+
+    /**
+     * Returns the number of rows affected
+     *
+     * @param   resource result handle
+     * @param   resource connection handle
+     *
+     * @return  mixed   MDB2 Error Object or the number of rows affected
+     *
+     * @access  private
+     */
+    function _affectedRows($connection, $result = null)
+    {
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function &exec($query)
+
+    /**
+     * Execute a manipulation query to the database and return the number of affected rows
+     *
+     * @param   string  the SQL query
+     *
+     * @return  mixed   number of affected rows on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function &exec($query)
+    {
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $query = $this->_modifyQuery($query, true, $limit, $offset);
+
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $result =$this->_doQuery($query, true, $connection, $this->database_name);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $affectedRows = $this->_affectedRows($connection, $result);
+        return $affectedRows;
+    }
+
+    // }}}
+    // {{{ function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
+
+    /**
+     * Send a query to the database and return any results
+     *
+     * @param   string  the SQL query
+     * @param   mixed   array that contains the types of the columns in
+     *                        the result set
+     * @param   mixed   string which specifies which result class to use
+     * @param   mixed   string which specifies which class to wrap results in
+     *
+     * @return mixed   an MDB2_Result handle on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
+    {
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $query = $this->_modifyQuery($query, false, $limit, $offset);
+
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $result =$this->_doQuery($query, false, $connection, $this->database_name);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $result =$this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset);
+        return $result;
+    }
+
+    // }}}
+    // {{{ function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null)
+
+    /**
+     * wrap a result set into the correct class
+     *
+     * @param   resource result handle
+     * @param   mixed   array that contains the types of the columns in
+     *                        the result set
+     * @param   mixed   string which specifies which result class to use
+     * @param   mixed   string which specifies which class to wrap results in
+     * @param   string  number of rows to select
+     * @param   string  first row to select
+     *
+     * @return mixed   an MDB2_Result, a MDB2 error on failure
+     *
+     * @access  protected
+     */
+    function &_wrapResult($result, $types = array(), $result_class = true,
+        $result_wrap_class = false, $limit = null, $offset = null)
+    {
+        if ($types === true) {
+            if ($this->supports('result_introspection')) {
+                $this->loadModule('Reverse', null, true);
+                $tableInfo = $this->reverse->tableInfo($result);
+                if (PEAR::isError($tableInfo)) {
+                    return $tableInfo;
+                }
+                $types = array();
+                foreach ($tableInfo as $field) {
+                    $types[] = $field['mdb2type'];
+                }
+            } else {
+                $types = null;
+            }
+        }
+
+        if ($result_class === true) {
+            $result_class = $this->options['result_buffering']
+                ? $this->options['buffered_result_class'] : $this->options['result_class'];
+        }
+
+        if ($result_class) {
+            $class_name = sprintf($result_class, $this->phptype);
+            if (!MDB2::classExists($class_name)) {
+                $err =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                    'result class does not exist '.$class_name, __FUNCTION__);
+                return $err;
+            }
+            $result =new $class_name($this, $result, $limit, $offset);
+            if (!MDB2::isResultCommon($result)) {
+                $err =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                    'result class is not extended from MDB2_Result_Common', __FUNCTION__);
+                return $err;
+            }
+            if (!empty($types)) {
+                $err = $result->setResultTypes($types);
+                if (PEAR::isError($err)) {
+                    $result->free();
+                    return $err;
+                }
+            }
+        }
+        if ($result_wrap_class === true) {
+            $result_wrap_class = $this->options['result_wrap_class'];
+        }
+        if ($result_wrap_class) {
+            if (!MDB2::classExists($result_wrap_class)) {
+                $err =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                    'result wrap class does not exist '.$result_wrap_class, __FUNCTION__);
+                return $err;
+            }
+            $result = new $result_wrap_class($result, $this->fetchmode);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ function getServerVersion($native = false)
+
+    /**
+     * return version information about the server
+     *
+     * @param   bool    determines if the raw version string should be returned
+     *
+     * @return  mixed   array with version information or row string
+     *
+     * @access  public
+     */
+    function getServerVersion($native = false)
+    {
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function setLimit($limit, $offset = null)
+
+    /**
+     * set the range of the next query
+     *
+     * @param   string  number of rows to select
+     * @param   string  first row to select
+     *
+     * @return  mixed   MDB2_OK on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function setLimit($limit, $offset = null)
+    {
+        if (!$this->supports('limit_queries')) {
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'limit is not supported by this driver', __FUNCTION__);
+        }
+        $limit = (int)$limit;
+        if ($limit < 0) {
+            return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
+                'it was not specified a valid selected range row limit', __FUNCTION__);
+        }
+        $this->limit = $limit;
+        if (!is_null($offset)) {
+            $offset = (int)$offset;
+            if ($offset < 0) {
+                return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
+                    'it was not specified a valid first selected range row', __FUNCTION__);
+            }
+            $this->offset = $offset;
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function subSelect($query, $type = false)
+
+    /**
+     * simple subselect emulation: leaves the query untouched for all RDBMS
+     * that support subselects
+     *
+     * @param   string  the SQL query for the subselect that may only
+     *                      return a column
+     * @param   string  determines type of the field
+     *
+     * @return  string  the query
+     *
+     * @access  public
+     */
+    function subSelect($query, $type = false)
+    {
+        if ($this->supports('sub_selects') === true) {
+            return $query;
+        }
+
+        if (!$this->supports('sub_selects')) {
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'method not implemented', __FUNCTION__);
+        }
+
+        $col = $this->queryCol($query, $type);
+        if (PEAR::isError($col)) {
+            return $col;
+        }
+        if (!is_array($col) || count($col) == 0) {
+            return 'NULL';
+        }
+        if ($type) {
+            $this->loadModule('Datatype', null, true);
+            return $this->datatype->implodeArray($col, $type);
+        }
+        return implode(', ', $col);
+    }
+
+    // }}}
+    // {{{ function replace($table, $fields)
+
+    /**
+     * 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 MySQL and SQLite implement it natively, this type of
+     * query isemulated through this method for other DBMS using standard types
+     * of queries inside a transaction to assure the atomicity of the operation.
+     *
+     * @param   string  name of the table on which the REPLACE query will
+     *       be executed.
+     * @param   array   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 MDB2
+     *           are supported except for clob and blob.
+     *
+     *           Default: no type conversion
+     *
+     *       null
+     *           bool    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
+     *           bool    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
+     *
+     * @access  public
+     */
+    function replace($table, $fields)
+    {
+        if (!$this->supports('replace')) {
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'replace query is not supported', __FUNCTION__);
+        }
+        $count = count($fields);
+        $condition = $values = array();
+        for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum++) {
+            $name = key($fields);
+            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);
+            }
+            $values[$name] = $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__);
+                }
+                $condition[] = $this->quoteIdentifier($name, true) . '=' . $value;
+            }
+        }
+        if (empty($condition)) {
+            return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
+                'not specified which fields are keys', __FUNCTION__);
+        }
+
+        $result = null;
+        $in_transaction = $this->in_transaction;
+        if (!$in_transaction && PEAR::isError($result = $this->beginTransaction())) {
+            return $result;
+        }
+
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $condition = ' WHERE '.implode(' AND ', $condition);
+        $query = 'DELETE FROM ' . $this->quoteIdentifier($table, true) . $condition;
+        $result =$this->_doQuery($query, true, $connection);
+        if (!PEAR::isError($result)) {
+            $affected_rows = $this->_affectedRows($connection, $result);
+            $insert = '';
+            foreach ($values as $key => $value) {
+                $insert .= ($insert?', ':'') . $this->quoteIdentifier($key, true);
+            }
+            $values = implode(', ', $values);
+            $query = 'INSERT INTO '. $this->quoteIdentifier($table, true) . "($insert) VALUES ($values)";
+            $result =$this->_doQuery($query, true, $connection);
+            if (!PEAR::isError($result)) {
+                $affected_rows += $this->_affectedRows($connection, $result);;
+            }
+        }
+
+        if (!$in_transaction) {
+            if (PEAR::isError($result)) {
+                $this->rollback();
+            } else {
+                $result = $this->commit();
+            }
+        }
+
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        return $affected_rows;
+    }
+
+    // }}}
+    // {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array())
+
+    /**
+     * 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  the query to prepare
+     * @param   mixed   array that contains the types of the placeholders
+     * @param   mixed   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   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())
+    {
+        $is_manip = ($result_types === MDB2_PREPARE_MANIP);
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $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 (!empty($types) && is_array($types)) {
+                        if ($placeholder_type == ':') {
+                            if (is_int(key($types))) {
+                                $types_tmp = $types;
+                                $types = array();
+                                $count = -1;
+                            }
+                        } else {
+                            $types = array_values($types);
+                        }
+                    }
+                }
+                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);
+                    // use parameter name in type array
+                    if (isset($count) && isset($types_tmp[++$count])) {
+                        $types[$parameter] = $types_tmp[$count];
+                    }
+                } else {
+                    $positions[$p_position] = count($positions);
+                }
+                $position = $p_position + 1;
+            } else {
+                $position = $p_position;
+            }
+        }
+        $class_name = 'MDB2_Statement_'.$this->phptype;
+        $statement = null;
+        $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;
+    }
+
+    // }}}
+    // {{{ function _skipDelimitedStrings($query, $position, $p_position)
+    
+    /**
+     * Utility method, used by prepare() to avoid replacing placeholders within delimited strings.
+     * Check if the placeholder is contained within a delimited string.
+     * If so, skip it and advance the position, otherwise return the current position,
+     * which is valid
+     *
+     * @param string $query
+     * @param integer $position current string cursor position
+     * @param integer $p_position placeholder position
+     *
+     * @return mixed integer $new_position on success
+     *               MDB2_Error on failure
+     *
+     * @access  protected
+     */
+    function _skipDelimitedStrings($query, $position, $p_position)
+    {
+        $ignores = $this->string_quoting;
+        $ignores[] = $this->identifier_quoting;
+        $ignores = array_merge($ignores, $this->sql_comments);
+        
+        foreach ($ignores as $ignore) {
+            if (!empty($ignore['start'])) {
+                if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position) {
+                    $end_quote = $start_quote;
+                    do {
+                        if (!is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1))) {
+                            if ($ignore['end'] === "\n") {
+                                $end_quote = strlen($query) - 1;
+                            } else {
+                                $err =$this->raiseError(MDB2_ERROR_SYNTAX, null, null,
+                                    'query with an unterminated text string specified', __FUNCTION__);
+                                return $err;
+                            }
+                        }
+                    } while ($ignore['escape']
+                        && $end_quote-1 != $start_quote
+                        && $query[($end_quote - 1)] == $ignore['escape']
+                        && (   $ignore['escape_pattern'] !== $ignore['escape']
+                            || $query[($end_quote - 2)] != $ignore['escape'])
+                    );
+
+                    $position = $end_quote + 1;
+                    return $position;
+                }
+            }
+        }
+        return $position;
+    }
+    
+    // }}}
+    // {{{ function quote($value, $type = null, $quote = true)
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param   string  text string value that is intended to be converted.
+     * @param   string  type to which the value should be converted to
+     * @param   bool    quote
+     * @param   bool    escape wildcards
+     *
+     * @return  string  text string that represents the given argument value in
+     *       a DBMS specific format.
+     *
+     * @access  public
+     */
+    function quote($value, $type = null, $quote = true, $escape_wildcards = false)
+    {
+        $result = $this->loadModule('Datatype', null, true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        return $this->datatype->quote($value, $type, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ function getDeclaration($type, $name, $field)
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare
+     * of the given type
+     *
+     * @param   string  type to which the value should be converted to
+     * @param   string  name the field to be declared.
+     * @param   string  definition of the field
+     *
+     * @return  string  DBMS specific SQL code portion that should be used to
+     *                 declare the specified field.
+     *
+     * @access  public
+     */
+    function getDeclaration($type, $name, $field)
+    {
+        $result = $this->loadModule('Datatype', null, true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        return $this->datatype->getDeclaration($type, $name, $field);
+    }
+
+    // }}}
+    // {{{ function compareDefinition($current, $previous)
+
+    /**
+     * Obtain an array of changes that may need to applied
+     *
+     * @param   array   new definition
+     * @param   array   old definition
+     *
+     * @return  array   containing all changes that will need to be applied
+     *
+     * @access  public
+     */
+    function compareDefinition($current, $previous)
+    {
+        $result = $this->loadModule('Datatype', null, true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        return $this->datatype->compareDefinition($current, $previous);
+    }
+
+    // }}}
+    // {{{ function supports($feature)
+
+    /**
+     * Tell whether a DB implementation or its backend extension
+     * supports a given feature.
+     *
+     * @param   string  name of the feature (see the MDB2 class doc)
+     *
+     * @return  bool|string if this DB implementation supports a given feature
+     *                      false means no, true means native,
+     *                      'emulated' means emulated
+     *
+     * @access  public
+     */
+    function supports($feature)
+    {
+        if (array_key_exists($feature, $this->supported)) {
+            return $this->supported[$feature];
+        }
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            "unknown support feature $feature", __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function getSequenceName($sqn)
+
+    /**
+     * adds sequence name formatting to a sequence name
+     *
+     * @param   string  name of the sequence
+     *
+     * @return  string  formatted sequence name
+     *
+     * @access  public
+     */
+    function getSequenceName($sqn)
+    {
+        return sprintf($this->options['seqname_format'],
+            preg_replace('/[^a-z0-9_\-\$.]/i', '_', $sqn));
+    }
+
+    // }}}
+    // {{{ function getIndexName($idx)
+
+    /**
+     * adds index name formatting to a index name
+     *
+     * @param   string  name of the index
+     *
+     * @return  string  formatted index name
+     *
+     * @access  public
+     */
+    function getIndexName($idx)
+    {
+        return sprintf($this->options['idxname_format'],
+            preg_replace('/[^a-z0-9_\-\$.]/i', '_', $idx));
+    }
+
+    // }}}
+    // {{{ function nextID($seq_name, $ondemand = true)
+
+    /**
+     * Returns the next free id of a sequence
+     *
+     * @param   string  name of the sequence
+     * @param   bool    when true missing sequences are automatic created
+     *
+     * @return  mixed   MDB2 Error Object or id
+     *
+     * @access  public
+     */
+    function nextID($seq_name, $ondemand = true)
+    {
+        return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function lastInsertID($table = null, $field = null)
+
+    /**
+     * Returns the autoincrement ID if supported or $id or fetches the current
+     * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
+     *
+     * @param   string  name of the table into which a new row was inserted
+     * @param   string  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->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function currID($seq_name)
+
+    /**
+     * Returns the current id of a sequence
+     *
+     * @param   string  name of the sequence
+     *
+     * @return  mixed   MDB2 Error Object or id
+     *
+     * @access  public
+     */
+    function currID($seq_name)
+    {
+        $this->warnings[] = 'database does not support getting current
+            sequence value, the sequence value was incremented';
+        return $this->nextID($seq_name);
+    }
+
+    // }}}
+    // {{{ function queryOne($query, $type = null, $colnum = 0)
+
+    /**
+     * Execute the specified query, fetch the value from the first column of
+     * the first row of the result set and then frees
+     * the result set.
+     *
+     * @param string $query  the SELECT query statement to be executed.
+     * @param string $type   optional argument that specifies the expected
+     *                       datatype of the result set field, so that an eventual
+     *                       conversion may be performed. The default datatype is
+     *                       text, meaning that no conversion is performed
+     * @param mixed  $colnum the column number (or name) to fetch
+     *
+     * @return  mixed   MDB2_OK or field value on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function queryOne($query, $type = null, $colnum = 0)
+    {
+        $result = $this->query($query, $type);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $one = $result->fetchOne($colnum);
+        $result->free();
+        return $one;
+    }
+
+    // }}}
+    // {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
+
+    /**
+     * Execute the specified query, fetch the values from the first
+     * row of the result set into an array and then frees
+     * the result set.
+     *
+     * @param   string  the SELECT query statement to be executed.
+     * @param   array   optional array argument that specifies a list of
+     *       expected datatypes of the result set columns, so that the eventual
+     *       conversions may be performed. The default list of datatypes is
+     *       empty, meaning that no conversion is performed.
+     * @param   int     how the array data should be indexed
+     *
+     * @return  mixed   MDB2_OK or data array on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
+    {
+        $result = $this->query($query, $types);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $row = $result->fetchRow($fetchmode);
+        $result->free();
+        return $row;
+    }
+
+    // }}}
+    // {{{ function queryCol($query, $type = null, $colnum = 0)
+
+    /**
+     * Execute the specified query, fetch the value from the first column of
+     * each row of the result set into an array and then frees the result set.
+     *
+     * @param string $query  the SELECT query statement to be executed.
+     * @param string $type   optional argument that specifies the expected
+     *                       datatype of the result set field, so that an eventual
+     *                       conversion may be performed. The default datatype is text,
+     *                       meaning that no conversion is performed
+     * @param mixed  $colnum the column number (or name) to fetch
+     *
+     * @return  mixed   MDB2_OK or data array on success, a MDB2 error on failure
+     * @access  public
+     */
+    function queryCol($query, $type = null, $colnum = 0)
+    {
+        $result = $this->query($query, $type);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $col = $result->fetchCol($colnum);
+        $result->free();
+        return $col;
+    }
+
+    // }}}
+    // {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
+
+    /**
+     * Execute the specified query, fetch all the rows of the result set into
+     * a two dimensional array and then frees the result set.
+     *
+     * @param   string  the SELECT query statement to be executed.
+     * @param   array   optional array argument that specifies a list of
+     *       expected datatypes of the result set columns, so that the eventual
+     *       conversions may be performed. The default list of datatypes is
+     *       empty, meaning that no conversion is performed.
+     * @param   int     how the array data should be indexed
+     * @param   bool    if set to true, the $all will have the first
+     *       column as its first dimension
+     * @param   bool    used only when the query returns exactly
+     *       two columns. If true, the values of the returned array will be
+     *       one-element arrays instead of scalars.
+     * @param   bool    if true, the values of the returned array is
+     *       wrapped in another array.  If the same key value (in the first
+     *       column) repeats itself, the values will be appended to this array
+     *       instead of overwriting the existing values.
+     *
+     * @return  mixed   MDB2_OK or data array on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
+        $rekey = false, $force_array = false, $group = false)
+    {
+        $result = $this->query($query, $types);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
+        $result->free();
+        return $all;
+    }
+
+    // }}}
+}
+
+// }}}
+// {{{ class MDB2_Result
+
+/**
+ * The dummy class that all user space result classes should extend from
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Result
+{
+}
+
+// }}}
+// {{{ class MDB2_Result_Common extends MDB2_Result
+
+/**
+ * The common result class for MDB2 result objects
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Result_Common extends MDB2_Result
+{
+    // {{{ Variables (Properties)
+
+    var $db;
+    var $result;
+    var $rownum = -1;
+    var $types = array();
+    var $values = array();
+    var $offset;
+    var $offset_count = 0;
+    var $limit;
+    var $column_names;
+
+    // }}}
+    // {{{ constructor: function __construct(&$db, &$result, $limit = 0, $offset = 0)
+
+    /**
+     * Constructor
+     */
+    function __construct(&$db, &$result, $limit = 0, $offset = 0)
+    {
+        $this->db =$db;
+        $this->result =$result;
+        $this->offset = $offset;
+        $this->limit = max(0, $limit - 1);
+    }
+
+
+    // }}}
+    // {{{ function setResultTypes($types)
+
+    /**
+     * Define the list of types to be associated with the columns of a given
+     * result set.
+     *
+     * This function may be called before invoking fetchRow(), fetchOne(),
+     * fetchCol() and fetchAll() so that the necessary data type
+     * conversions are performed on the data to be retrieved by them. If this
+     * function is not called, the type of all result set columns is assumed
+     * to be text, thus leading to not perform any conversions.
+     *
+     * @param   array   variable that lists the
+     *       data types to be expected in the result set columns. If this array
+     *       contains less types than the number of columns that are returned
+     *       in the result set, the remaining columns are assumed to be of the
+     *       type text. Currently, the types clob and blob are not fully
+     *       supported.
+     *
+     * @return  mixed   MDB2_OK on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function setResultTypes($types)
+    {
+        $load = $this->db->loadModule('Datatype', null, true);
+        if (PEAR::isError($load)) {
+            return $load;
+        }
+        $types = $this->db->datatype->checkResultTypes($types);
+        if (PEAR::isError($types)) {
+            return $types;
+        }
+        $this->types = $types;
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function seek($rownum = 0)
+
+    /**
+     * Seek to a specific row in a result set
+     *
+     * @param   int     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)
+    {
+        $target_rownum = $rownum - 1;
+        if ($this->rownum > $target_rownum) {
+            return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'seeking to previous rows not implemented', __FUNCTION__);
+        }
+        while ($this->rownum < $target_rownum) {
+            $this->fetchRow();
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
+
+    /**
+     * Fetch and return a row of data
+     *
+     * @param   int     how the array data should be indexed
+     * @param   int     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)
+    {
+        $err =$this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+        return $err;
+    }
+
+    // }}}
+    // {{{ function fetchOne($colnum = 0)
+
+    /**
+     * fetch single column from the next row from a result set
+     *
+     * @param int|string the column number (or name) to fetch
+     * @param int        number of the row where the data can be found
+     *
+     * @return string data on success, a MDB2 error on failure
+     * @access  public
+     */
+    function fetchOne($colnum = 0, $rownum = null)
+    {
+        $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
+        $row = $this->fetchRow($fetchmode, $rownum);
+        if (!is_array($row) || PEAR::isError($row)) {
+            return $row;
+        }
+        if (!array_key_exists($colnum, $row)) {
+            return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
+                'column is not defined in the result set: '.$colnum, __FUNCTION__);
+        }
+        return $row[$colnum];
+    }
+
+    // }}}
+    // {{{ function fetchCol($colnum = 0)
+
+    /**
+     * Fetch and return a column from the current row pointer position
+     *
+     * @param int|string the column number (or name) to fetch
+     *
+     * @return  mixed data array on success, a MDB2 error on failure
+     * @access  public
+     */
+    function fetchCol($colnum = 0)
+    {
+        $column = array();
+        $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
+        $row = $this->fetchRow($fetchmode);
+        if (is_array($row)) {
+            if (!array_key_exists($colnum, $row)) {
+                return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
+                    'column is not defined in the result set: '.$colnum, __FUNCTION__);
+            }
+            do {
+                $column[] = $row[$colnum];
+            } while (is_array($row = $this->fetchRow($fetchmode)));
+        }
+        if (PEAR::isError($row)) {
+            return $row;
+        }
+        return $column;
+    }
+
+    // }}}
+    // {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
+
+    /**
+     * Fetch and return all rows from the current row pointer position
+     *
+     * @param   int     $fetchmode  the fetch mode to use:
+     *                            + MDB2_FETCHMODE_ORDERED
+     *                            + MDB2_FETCHMODE_ASSOC
+     *                            + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED
+     *                            + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED
+     * @param   bool    if set to true, the $all will have the first
+     *       column as its first dimension
+     * @param   bool    used only when the query returns exactly
+     *       two columns. If true, the values of the returned array will be
+     *       one-element arrays instead of scalars.
+     * @param   bool    if true, the values of the returned array is
+     *       wrapped in another array.  If the same key value (in the first
+     *       column) repeats itself, the values will be appended to this array
+     *       instead of overwriting the existing values.
+     *
+     * @return  mixed   data array on success, a MDB2 error on failure
+     *
+     * @access  public
+     * @see     getAssoc()
+     */
+    function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false,
+        $force_array = false, $group = false)
+    {
+        $all = array();
+        $row = $this->fetchRow($fetchmode);
+        if (PEAR::isError($row)) {
+            return $row;
+        } elseif (!$row) {
+            return $all;
+        }
+
+        $shift_array = $rekey ? false : null;
+        if (!is_null($shift_array)) {
+            if (is_object($row)) {
+                $colnum = count(get_object_vars($row));
+            } else {
+                $colnum = count($row);
+            }
+            if ($colnum < 2) {
+                return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
+                    'rekey feature requires atleast 2 column', __FUNCTION__);
+            }
+            $shift_array = (!$force_array && $colnum == 2);
+        }
+
+        if ($rekey) {
+            do {
+                if (is_object($row)) {
+                    $arr = get_object_vars($row);
+                    $key = reset($arr);
+                    unset($row->{$key});
+                } else {
+                    if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
+                        $key = reset($row);
+                        unset($row[key($row)]);
+                    } else {
+                        $key = array_shift($row);
+                    }
+                    if ($shift_array) {
+                        $row = array_shift($row);
+                    }
+                }
+                if ($group) {
+                    $all[$key][] = $row;
+                } else {
+                    $all[$key] = $row;
+                }
+            } while (($row = $this->fetchRow($fetchmode)));
+        } elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED) {
+            do {
+                foreach ($row as $key => $val) {
+                    $all[$key][] = $val;
+                }
+            } while (($row = $this->fetchRow($fetchmode)));
+        } else {
+            do {
+                $all[] = $row;
+            } while (($row = $this->fetchRow($fetchmode)));
+        }
+
+        return $all;
+    }
+
+    // }}}
+    // {{{ function rowCount()
+    /**
+     * Returns the actual row number that was last fetched (count from 0)
+     * @return  int
+     *
+     * @access  public
+     */
+    function rowCount()
+    {
+        return $this->rownum + 1;
+    }
+
+    // }}}
+    // {{{ function numRows()
+
+    /**
+     * Returns the number of rows in a result object
+     *
+     * @return  mixed   MDB2 Error Object or the number of rows
+     *
+     * @access  public
+     */
+    function numRows()
+    {
+        return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function nextResult()
+
+    /**
+     * Move the internal result pointer to the next available result
+     *
+     * @return  true on success, false if there is no more result set or an error object on failure
+     *
+     * @access  public
+     */
+    function nextResult()
+    {
+        return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function getColumnNames()
+
+    /**
+     * Retrieve the names of columns returned by the DBMS in a query result or
+     * from the cache.
+     *
+     * @param   bool    If set to true the values are the column names,
+     *                  otherwise the names of the columns are the keys.
+     * @return  mixed   Array variable that holds the names of columns or an
+     *                  MDB2 error on failure.
+     *                  Some DBMS may not return any columns when the result set
+     *                  does not contain any rows.
+     *
+     * @access  public
+     */
+    function getColumnNames($flip = false)
+    {
+        if (!isset($this->column_names)) {
+            $result = $this->_getColumnNames();
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+            $this->column_names = $result;
+        }
+        if ($flip) {
+            return array_flip($this->column_names);
+        }
+        return $this->column_names;
+    }
+
+    // }}}
+    // {{{ function _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()
+    {
+        return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function numCols()
+
+    /**
+     * Count the number of columns returned by the DBMS in a query result.
+     *
+     * @return  mixed   integer value with the number of columns, a MDB2 error
+     *       on failure
+     *
+     * @access  public
+     */
+    function numCols()
+    {
+        return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ function getResource()
+
+    /**
+     * return the resource associated with the result object
+     *
+     * @return  resource
+     *
+     * @access  public
+     */
+    function getResource()
+    {
+        return $this->result;
+    }
+
+    // }}}
+    // {{{ function bindColumn($column, &$value, $type = null)
+
+    /**
+     * Set bind variable to a column.
+     *
+     * @param   int     column number or name
+     * @param   mixed   variable reference
+     * @param   string  specifies the type of the field
+     *
+     * @return  mixed   MDB2_OK on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function bindColumn($column, &$value, $type = null)
+    {
+        if (!is_numeric($column)) {
+            $column_names = $this->getColumnNames();
+            if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+                if ($this->db->options['field_case'] == CASE_LOWER) {
+                    $column = strtolower($column);
+                } else {
+                    $column = strtoupper($column);
+                }
+            }
+            $column = $column_names[$column];
+        }
+        $this->values[$column] =$value;
+        if (!is_null($type)) {
+            $this->types[$column] = $type;
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function _assignBindColumns($row)
+
+    /**
+     * Bind a variable to a value in the result row.
+     *
+     * @param   array   row data
+     *
+     * @return  mixed   MDB2_OK on success, a MDB2 error on failure
+     *
+     * @access  private
+     */
+    function _assignBindColumns($row)
+    {
+        $row = array_values($row);
+        foreach ($row as $column => $value) {
+            if (array_key_exists($column, $this->values)) {
+                $this->values[$column] = $value;
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function free()
+
+    /**
+     * Free the internal resources associated with result.
+     *
+     * @return  bool    true on success, false if result is invalid
+     *
+     * @access  public
+     */
+    function free()
+    {
+        $this->result = false;
+        return MDB2_OK;
+    }
+
+    // }}}
+}
+
+// }}}
+// {{{ class MDB2_Row
+
+/**
+ * The simple class that accepts row data as an array
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Row
+{
+    // {{{ constructor: function __construct(&$row)
+
+    /**
+     * constructor
+     *
+     * @param   resource    row data as array
+     */
+    function __construct(&$row)
+    {
+        foreach ($row as $key => $value) {
+            $this->$key = &$row[$key];
+        }
+    }
+}
+
+// }}}
+// {{{ class MDB2_Statement_Common
+
+/**
+ * The common statement class for MDB2 statement objects
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Statement_Common
+{
+    // {{{ Variables (Properties)
+
+    var $db;
+    var $statement;
+    var $query;
+    var $result_types;
+    var $types;
+    var $values = array();
+    var $limit;
+    var $offset;
+    var $is_manip;
+
+    // }}}
+    // {{{ constructor: function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
+
+    /**
+     * Constructor
+     */
+    function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
+    {
+        $this->db =$db;
+        $this->statement =$statement;
+        $this->positions = $positions;
+        $this->query = $query;
+        $this->types = (array)$types;
+        $this->result_types = (array)$result_types;
+        $this->limit = $limit;
+        $this->is_manip = $is_manip;
+        $this->offset = $offset;
+    }
+
+
+    // }}}
+    // {{{ function bindValue($parameter, &$value, $type = null)
+
+    /**
+     * 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 (!is_numeric($parameter)) {
+            $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
+        }
+        if (!in_array($parameter, $this->positions)) {
+            return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
+        }
+        $this->values[$parameter] = $value;
+        if (!is_null($type)) {
+            $this->types[$parameter] = $type;
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ function bindValueArray($values, $types = null)
+
+    /**
+     * 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, $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 bindParam($parameter, &$value, $type = null)
+
+    /**
+     * 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 (!is_numeric($parameter)) {
+            $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
+        }
+        if (!in_array($parameter, $this->positions)) {
+            return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
+        }
+        $this->values[$parameter] =$value;
+        if (!is_null($type)) {
+            $this->types[$parameter] = $type;
+        }
+        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, $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)) {
+            $err = $this->bindValueArray($values);
+            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 &_execute($result_class = true, $result_wrap_class = false)
+
+    /**
+     * Execute a prepared query statement helper method.
+     *
+     * @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  private
+     */
+    function &_execute($result_class = true, $result_wrap_class = false)
+    {
+        $this->last_query = $this->query;
+        $query = '';
+        $last_position = 0;
+        foreach ($this->positions as $current_position => $parameter) {
+            if (!array_key_exists($parameter, $this->values)) {
+                return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                    'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
+            }
+            $value = $this->values[$parameter];
+            $query.= substr($this->query, $last_position, $current_position - $last_position);
+            if (!isset($value)) {
+                $value_quoted = 'NULL';
+            } else {
+                $type = !empty($this->types[$parameter]) ? $this->types[$parameter] : null;
+                $value_quoted = $this->db->quote($value, $type);
+                if (PEAR::isError($value_quoted)) {
+                    return $value_quoted;
+                }
+            }
+            $query.= $value_quoted;
+            $last_position = $current_position + 1;
+        }
+        $query.= substr($this->query, $last_position);
+
+        $this->db->offset = $this->offset;
+        $this->db->limit = $this->limit;
+        if ($this->is_manip) {
+            $result = $this->db->exec($query);
+        } else {
+            $result =$this->db->query($query, $this->result_types, $result_class, $result_wrap_class);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ function free()
+
+    /**
+     * Release resources allocated for the specified prepared query.
+     *
+     * @return  mixed   MDB2_OK on success, a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function free()
+    {
+        if (is_null($this->positions)) {
+            return $this->db->raiseError(MDB2_ERROR, null, null,
+                'Prepared statement has already been freed', __FUNCTION__);
+        }
+
+        $this->statement = null;
+        $this->positions = null;
+        $this->query = null;
+        $this->types = null;
+        $this->result_types = null;
+        $this->limit = null;
+        $this->is_manip = null;
+        $this->offset = null;
+        $this->values = null;
+
+        return MDB2_OK;
+    }
+
+    // }}}
+}
+
+// }}}
+// {{{ class MDB2_Module_Common
+
+/**
+ * The common modules class for MDB2 module objects
+ *
+ * @package     MDB2
+ * @category    Database
+ * @author      Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Module_Common
+{
+    // {{{ Variables (Properties)
+
+    /**
+     * contains the key to the global MDB2 instance array of the associated
+     * MDB2 instance
+     *
+     * @var     int
+     * @access  protected
+     */
+    var $db_index;
+
+    // }}}
+    // {{{ constructor: function __construct($db_index)
+
+    /**
+     * Constructor
+     */
+    function __construct($db_index)
+    {
+        $this->db_index = $db_index;
+    }
+
+    // }}}
+    // {{{ function &getDBInstance()
+
+    /**
+     * Get the instance of MDB2 associated with the module instance
+     *
+     * @return  object  MDB2 instance or a MDB2 error on failure
+     *
+     * @access  public
+     */
+    function getDBInstance()
+    {
+        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            $result =$GLOBALS['_MDB2_databases'][$this->db_index];
+        } else {
+            $result =$this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'could not find MDB2 instance');
+        }
+        return $result;
+    }
+
+    // }}}
+}
+
+// }}}
+// {{{ function MDB2_closeOpenTransactions()
+
+/**
+ * Close any open transactions form persistent connections
+ *
+ * @return  void
+ *
+ * @access  public
+ */
+
+function MDB2_closeOpenTransactions()
+{
+    reset($GLOBALS['_MDB2_databases']);
+    while (next($GLOBALS['_MDB2_databases'])) {
+        $key = key($GLOBALS['_MDB2_databases']);
+        if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent
+            && $GLOBALS['_MDB2_databases'][$key]->in_transaction
+        ) {
+            $GLOBALS['_MDB2_databases'][$key]->rollback();
+        }
+    }
+}
+
+// }}}
+// {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null)
+
+/**
+ * default debug output handler
+ *
+ * @param   object  reference to an MDB2 database object
+ * @param   string  usually the method name that triggered the debug call:
+ *                  for example 'query', 'prepare', 'execute', 'parameters',
+ *                  'beginTransaction', 'commit', 'rollback'
+ * @param   string  message that should be appended to the debug variable
+ * @param   array   contains context information about the debug() call
+ *                  common keys are: is_manip, time, result etc.
+ *
+ * @return  void|string optionally return a modified message, this allows
+ *                      rewriting a query before being issued or prepared
+ *
+ * @access  public
+ */
+function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array())
+{
+    $db->debug_output.= $scope.'('.$db->db_index.'): ';
+    $db->debug_output.= $message.$db->getOption('log_line_break');
+    return $message;
+}
+
+// }}}
+?>
diff --git a/3rdparty/MDB2/Date.php b/3rdparty/MDB2/Date.php
new file mode 100644
index 0000000000000000000000000000000000000000..ce846543a50e8383fe1fa2140ec8903c8a48daf2
--- /dev/null
+++ b/3rdparty/MDB2/Date.php
@@ -0,0 +1,183 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Date.php,v 1.10 2006/03/01 12:15:32 lsmith Exp $
+//
+
+/**
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+
+/**
+ * Several methods to convert the MDB2 native timestamp format (ISO based)
+ * to and from data structures that are convenient to worth with in side of php.
+ * For more complex date arithmetic please take a look at the Date package in PEAR
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Date
+{
+    // {{{ mdbNow()
+
+    /**
+     * return the current datetime
+     *
+     * @return string current datetime in the MDB2 format
+     * @access public
+     */
+    function mdbNow()
+    {
+        return date('Y-m-d H:i:s');
+    }
+    // }}}
+
+    // {{{ mdbToday()
+
+    /**
+     * return the current date
+     *
+     * @return string current date in the MDB2 format
+     * @access public
+     */
+    function mdbToday()
+    {
+        return date('Y-m-d');
+    }
+    // }}}
+
+    // {{{ mdbTime()
+
+    /**
+     * return the current time
+     *
+     * @return string current time in the MDB2 format
+     * @access public
+     */
+    function mdbTime()
+    {
+        return date('H:i:s');
+    }
+    // }}}
+
+    // {{{ date2Mdbstamp()
+
+    /**
+     * convert a date into a MDB2 timestamp
+     *
+     * @param int hour of the date
+     * @param int minute of the date
+     * @param int second of the date
+     * @param int month of the date
+     * @param int day of the date
+     * @param int year of the date
+     *
+     * @return string a valid MDB2 timestamp
+     * @access public
+     */
+    function date2Mdbstamp($hour = null, $minute = null, $second = null,
+        $month = null, $day = null, $year = null)
+    {
+        return MDB2_Date::unix2Mdbstamp(mktime($hour, $minute, $second, $month, $day, $year, -1));
+    }
+    // }}}
+
+    // {{{ unix2Mdbstamp()
+
+    /**
+     * convert a unix timestamp into a MDB2 timestamp
+     *
+     * @param int a valid unix timestamp
+     *
+     * @return string a valid MDB2 timestamp
+     * @access public
+     */
+    function unix2Mdbstamp($unix_timestamp)
+    {
+        return date('Y-m-d H:i:s', $unix_timestamp);
+    }
+    // }}}
+
+    // {{{ mdbstamp2Unix()
+
+    /**
+     * convert a MDB2 timestamp into a unix timestamp
+     *
+     * @param int a valid MDB2 timestamp
+     * @return string unix timestamp with the time stored in the MDB2 format
+     *
+     * @access public
+     */
+    function mdbstamp2Unix($mdb_timestamp)
+    {
+        $arr = MDB2_Date::mdbstamp2Date($mdb_timestamp);
+
+        return mktime($arr['hour'], $arr['minute'], $arr['second'], $arr['month'], $arr['day'], $arr['year'], -1);
+    }
+    // }}}
+
+    // {{{ mdbstamp2Date()
+
+    /**
+     * convert a MDB2 timestamp into an array containing all
+     * values necessary to pass to php's date() function
+     *
+     * @param int a valid MDB2 timestamp
+     *
+     * @return array with the time split
+     * @access public
+     */
+    function mdbstamp2Date($mdb_timestamp)
+    {
+        list($arr['year'], $arr['month'], $arr['day'], $arr['hour'], $arr['minute'], $arr['second']) =
+            sscanf($mdb_timestamp, "%04u-%02u-%02u %02u:%02u:%02u");
+        return $arr;
+    }
+    // }}}
+}
+
+?>
diff --git a/3rdparty/MDB2/Driver/Datatype/Common.php b/3rdparty/MDB2/Driver/Datatype/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..85a74f64fca85f3d06ae56284f248eb27428ecae
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Datatype/Common.php
@@ -0,0 +1,1824 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.139 2008/12/04 11:50:42 afz Exp $
+
+require_once('MDB2/LOB.php');
+
+/**
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+
+/**
+ * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
+ *
+ * To load this module in the MDB2 object:
+ * $mdb->loadModule('Datatype');
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Datatype_Common extends MDB2_Module_Common
+{
+    var $valid_default_values = array(
+        'text'      => '',
+        'boolean'   => true,
+        'integer'   => 0,
+        'decimal'   => 0.0,
+        'float'     => 0.0,
+        'timestamp' => '1970-01-01 00:00:00',
+        'time'      => '00:00:00',
+        'date'      => '1970-01-01',
+        'clob'      => '',
+        'blob'      => '',
+    );
+
+    /**
+     * contains all LOB objects created with this MDB2 instance
+     * @var array
+     * @access protected
+     */
+    var $lobs = array();
+
+    // }}}
+    // {{{ getValidTypes()
+
+    /**
+     * Get the list of valid types
+     *
+     * This function returns an array of valid types as keys with the values
+     * being possible default values for all native datatypes and mapped types
+     * for custom datatypes.
+     *
+     * @return mixed array on success, a MDB2 error on failure
+     * @access public
+     */
+    function getValidTypes()
+    {
+        $types = $this->valid_default_values;
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        if (!empty($db->options['datatype_map'])) {
+            foreach ($db->options['datatype_map'] as $type => $mapped_type) {
+                if (array_key_exists($mapped_type, $types)) {
+                    $types[$type] = $types[$mapped_type];
+                } elseif (!empty($db->options['datatype_map_callback'][$type])) {
+                    $parameter = array('type' => $type, 'mapped_type' => $mapped_type);
+                    $default =  call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+                    $types[$type] = $default;
+                }
+            }
+        }
+        return $types;
+    }
+
+    // }}}
+    // {{{ checkResultTypes()
+
+    /**
+     * Define the list of types to be associated with the columns of a given
+     * result set.
+     *
+     * This function may be called before invoking fetchRow(), fetchOne()
+     * fetchCole() and fetchAll() so that the necessary data type
+     * conversions are performed on the data to be retrieved by them. If this
+     * function is not called, the type of all result set columns is assumed
+     * to be text, thus leading to not perform any conversions.
+     *
+     * @param array $types array variable that lists the
+     *       data types to be expected in the result set columns. If this array
+     *       contains less types than the number of columns that are returned
+     *       in the result set, the remaining columns are assumed to be of the
+     *       type text. Currently, the types clob and blob are not fully
+     *       supported.
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function checkResultTypes($types)
+    {
+        $types = is_array($types) ? $types : array($types);
+        foreach ($types as $key => $type) {
+            if (!isset($this->valid_default_values[$type])) {
+                $db =$this->getDBInstance();
+                if (PEAR::isError($db)) {
+                    return $db;
+                }
+                if (empty($db->options['datatype_map'][$type])) {
+                    return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                        $type.' for '.$key.' is not a supported column type', __FUNCTION__);
+                }
+            }
+        }
+        return $types;
+    }
+
+    // }}}
+    // {{{ _baseConvertResult()
+
+    /**
+     * General type conversion method
+     *
+     * @param mixed   $value reference to a value to be converted
+     * @param string  $type  specifies which type to convert to
+     * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
+     * @return object an MDB2 error on failure
+     * @access protected
+     */
+    function _baseConvertResult($value, $type, $rtrim = true)
+    {
+        switch ($type) {
+        case 'text':
+            if ($rtrim) {
+                $value = rtrim($value);
+            }
+            return $value;
+        case 'integer':
+            return intval($value);
+        case 'boolean':
+            return !empty($value);
+        case 'decimal':
+            return $value;
+        case 'float':
+            return doubleval($value);
+        case 'date':
+            return $value;
+        case 'time':
+            return $value;
+        case 'timestamp':
+            return $value;
+        case 'clob':
+        case 'blob':
+            $this->lobs[] = array(
+                'buffer' => null,
+                'position' => 0,
+                'lob_index' => null,
+                'endOfLOB' => false,
+                'resource' => $value,
+                'value' => null,
+                'loaded' => false,
+            );
+            end($this->lobs);
+            $lob_index = key($this->lobs);
+            $this->lobs[$lob_index]['lob_index'] = $lob_index;
+            return fopen('MDB2LOB://'.$lob_index.'@'.$this->db_index, 'r+');
+        }
+
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_INVALID, null, null,
+            'attempt to convert result value to an unknown type :' . $type, __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ convertResult()
+
+    /**
+     * Convert a value to a RDBMS indipendent MDB2 type
+     *
+     * @param mixed   $value value to be converted
+     * @param string  $type  specifies which type to convert to
+     * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
+     * @return mixed converted value
+     * @access public
+     */
+    function convertResult($value, $type, $rtrim = true)
+    {
+        if (is_null($value)) {
+            return null;
+        }
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        if (!empty($db->options['datatype_map'][$type])) {
+            $type = $db->options['datatype_map'][$type];
+            if (!empty($db->options['datatype_map_callback'][$type])) {
+                $parameter = array('type' => $type, 'value' => $value, 'rtrim' => $rtrim);
+                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+            }
+        }
+        return $this->_baseConvertResult($value, $type, $rtrim);
+    }
+
+    // }}}
+    // {{{ convertResultRow()
+
+    /**
+     * Convert a result row
+     *
+     * @param array   $types
+     * @param array   $row   specifies the types to convert to
+     * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
+     * @return mixed MDB2_OK on success, an MDB2 error on failure
+     * @access public
+     */
+    function convertResultRow($types, $row, $rtrim = true)
+    {
+        $types = $this->_sortResultFieldTypes(array_keys($row), $types);
+        foreach ($row as $key => $value) {
+            if (empty($types[$key])) {
+                continue;
+            }
+            $value = $this->convertResult($row[$key], $types[$key], $rtrim);
+            if (PEAR::isError($value)) {
+                return $value;
+            }
+            $row[$key] = $value;
+        }
+        return $row;
+    }
+
+    // }}}
+    // {{{ _sortResultFieldTypes()
+
+    /**
+     * convert a result row
+     *
+     * @param array $types
+     * @param array $row specifies the types to convert to
+     * @param bool   $rtrim   if to rtrim text values or not
+     * @return mixed MDB2_OK on success,  a MDB2 error on failure
+     * @access public
+     */
+    function _sortResultFieldTypes($columns, $types)
+    {
+        $n_cols = count($columns);
+        $n_types = count($types);
+        if ($n_cols > $n_types) {
+            for ($i= $n_cols - $n_types; $i >= 0; $i--) {
+                $types[] = null;
+            }
+        }
+        $sorted_types = array();
+        foreach ($columns as $col) {
+            $sorted_types[$col] = null;
+        }
+        foreach ($types as $name => $type) {
+            if (array_key_exists($name, $sorted_types)) {
+                $sorted_types[$name] = $type;
+                unset($types[$name]);
+            }
+        }
+        // if there are left types in the array, fill the null values of the
+        // sorted array with them, in order.
+        if (count($types)) {
+            reset($types);
+            foreach (array_keys($sorted_types) as $k) {
+                if (is_null($sorted_types[$k])) {
+                    $sorted_types[$k] = current($types);
+                    next($types);
+                }
+            }
+        }
+        return $sorted_types;
+    }
+
+    // }}}
+    // {{{ getDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare
+     * of the given type
+     *
+     * @param string $type type to which the value should be converted to
+     * @param string  $name   name the field to be declared.
+     * @param string  $field  definition of the field
+     * @return string  DBMS specific SQL code portion that should be used to
+     *                 declare the specified field.
+     * @access public
+     */
+    function getDeclaration($type, $name, $field)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (!empty($db->options['datatype_map'][$type])) {
+            $type = $db->options['datatype_map'][$type];
+            if (!empty($db->options['datatype_map_callback'][$type])) {
+                $parameter = array('type' => $type, 'name' => $name, 'field' => $field);
+                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+            }
+            $field['type'] = $type;
+        }
+
+        if (!method_exists($this, "_get{$type}Declaration")) {
+            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'type not defined: '.$type, __FUNCTION__);
+        }
+        return $this->{"_get{$type}Declaration"}($name, $field);
+    }
+
+    // }}}
+    // {{{ 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'] : $db->options['default_text_field_length'];
+            $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':
+            return 'TEXT';
+        case 'blob':
+            return 'TEXT';
+        case 'integer':
+            return 'INT';
+        case 'boolean':
+            return 'INT';
+        case 'date':
+            return 'CHAR ('.strlen('YYYY-MM-DD').')';
+        case 'time':
+            return 'CHAR ('.strlen('HH:MM:SS').')';
+        case 'timestamp':
+            return 'CHAR ('.strlen('YYYY-MM-DD HH:MM:SS').')';
+        case 'float':
+            return 'TEXT';
+        case 'decimal':
+            return 'TEXT';
+        }
+        return '';
+    }
+
+    // }}}
+    // {{{ _getDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a generic type
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name   name the field to be declared.
+     * @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.
+     *      charset
+     *          Text value with the default CHARACTER SET for this field.
+     *      collation
+     *          Text value with the default COLLATION for this field.
+     * @return string  DBMS specific SQL code portion that should be used to
+     *      declare the specified field, or a MDB2_Error on failure
+     * @access protected
+     */
+    function _getDeclaration($name, $field)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        $declaration_options = $db->datatype->_getDeclarationOptions($field);
+        if (PEAR::isError($declaration_options)) {
+            return $declaration_options;
+        }
+        return $name.' '.$this->getTypeDeclaration($field).$declaration_options;
+    }
+
+    // }}}
+    // {{{ _getDeclarationOptions()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a generic type
+     * field to be used in statement like CREATE TABLE, without the field name
+     * and type values (ie. just the character set, default value, if the
+     * field is permitted to be NULL or not, and the collation options).
+     *
+     * @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:
+     *
+     *      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.
+     *      charset
+     *          Text value with the default CHARACTER SET for this field.
+     *      collation
+     *          Text value with the default COLLATION for this field.
+     * @return string  DBMS specific SQL code portion that should be used to
+     *      declare the specified field's options.
+     * @access protected
+     */
+    function _getDeclarationOptions($field)
+    {
+        $charset = empty($field['charset']) ? '' :
+            ' '.$this->_getCharsetFieldDeclaration($field['charset']);
+
+        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
+        $default = '';
+        if (array_key_exists('default', $field)) {
+            if ($field['default'] === '') {
+                $db =$this->getDBInstance();
+                if (PEAR::isError($db)) {
+                    return $db;
+                }
+                $valid_default_values = $this->getValidTypes();
+                $field['default'] = $valid_default_values[$field['type']];
+                if ($field['default'] === ''&& ($db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)) {
+                    $field['default'] = ' ';
+                }
+            }
+            if (!is_null($field['default'])) {
+                $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
+            }
+        }
+
+        $collation = empty($field['collation']) ? '' :
+            ' '.$this->_getCollationFieldDeclaration($field['collation']);
+
+        return $charset.$default.$notnull.$collation;
+    }
+
+    // }}}
+    // {{{ _getCharsetFieldDeclaration()
+    
+    /**
+     * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
+     * of a field declaration to be used in statements like CREATE TABLE.
+     *
+     * @param string $charset   name of the charset
+     * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
+     *                 of a field declaration.
+     */
+    function _getCharsetFieldDeclaration($charset)
+    {
+        return '';
+    }
+
+    // }}}
+    // {{{ _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 '';
+    }
+
+    // }}}
+    // {{{ _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 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:
+     *
+     *       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)
+    {
+        if (!empty($field['unsigned'])) {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+
+            $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
+        }
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getTextDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare an text type
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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 protected
+     */
+    function _getTextDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getCLOBDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare an character
+     * large object type field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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 large
+     *            object field. If this argument is missing the field should be
+     *            declared to have the longest length allowed by the DBMS.
+     *
+     *        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 _getCLOBDeclaration($name, $field)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
+        $name = $db->quoteIdentifier($name, true);
+        return $name.' '.$this->getTypeDeclaration($field).$notnull;
+    }
+
+    // }}}
+    // {{{ _getBLOBDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare an binary large
+     * object type field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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 large
+     *            object field. If this argument is missing the field should be
+     *            declared to have the longest length allowed by the DBMS.
+     *
+     *        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 _getBLOBDeclaration($name, $field)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
+        $name = $db->quoteIdentifier($name, true);
+        return $name.' '.$this->getTypeDeclaration($field).$notnull;
+    }
+
+    // }}}
+    // {{{ _getBooleanDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a boolean type
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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:
+     *
+     *       default
+     *           Boolean value to be used as default for this field.
+     *
+     *       notnullL
+     *           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 _getBooleanDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getDateDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a date type
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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:
+     *
+     *       default
+     *           Date 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 _getDateDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getTimestampDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a timestamp
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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:
+     *
+     *       default
+     *           Timestamp 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 _getTimestampDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getTimeDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a time
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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:
+     *
+     *       default
+     *           Time 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 _getTimeDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getFloatDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a float type
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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:
+     *
+     *       default
+     *           Float 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 _getFloatDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getDecimalDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare a decimal type
+     * field to be used in statements like CREATE TABLE.
+     *
+     * @param string $name name the field to be declared.
+     * @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:
+     *
+     *       default
+     *           Decimal 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 _getDecimalDeclaration($name, $field)
+    {
+        return $this->_getDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ compareDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access public
+     */
+    function compareDefinition($current, $previous)
+    {
+        $type = !empty($current['type']) ? $current['type'] : null;
+
+        if (!method_exists($this, "_compare{$type}Definition")) {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+            if (!empty($db->options['datatype_map_callback'][$type])) {
+                $parameter = array('current' => $current, 'previous' => $previous);
+                $change =  call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+                return $change;
+            }
+            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'type "'.$current['type'].'" is not yet supported', __FUNCTION__);
+        }
+
+        if (empty($previous['type']) || $previous['type'] != $type) {
+            return $current;
+        }
+
+        $change = $this->{"_compare{$type}Definition"}($current, $previous);
+
+        if ($previous['type'] != $type) {
+            $change['type'] = true;
+        }
+
+        $previous_notnull = !empty($previous['notnull']) ? $previous['notnull'] : false;
+        $notnull = !empty($current['notnull']) ? $current['notnull'] : false;
+        if ($previous_notnull != $notnull) {
+            $change['notnull'] = true;
+        }
+
+        $previous_default = array_key_exists('default', $previous) ? $previous['default'] :
+            ($previous_notnull ? '' : null);
+        $default = array_key_exists('default', $current) ? $current['default'] :
+            ($notnull ? '' : null);
+        if ($previous_default !== $default) {
+            $change['default'] = true;
+        }
+
+        return $change;
+    }
+
+    // }}}
+    // {{{ _compareIntegerDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an integer field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareIntegerDefinition($current, $previous)
+    {
+        $change = array();
+        $previous_unsigned = !empty($previous['unsigned']) ? $previous['unsigned'] : false;
+        $unsigned = !empty($current['unsigned']) ? $current['unsigned'] : false;
+        if ($previous_unsigned != $unsigned) {
+            $change['unsigned'] = true;
+        }
+        $previous_autoincrement = !empty($previous['autoincrement']) ? $previous['autoincrement'] : false;
+        $autoincrement = !empty($current['autoincrement']) ? $current['autoincrement'] : false;
+        if ($previous_autoincrement != $autoincrement) {
+            $change['autoincrement'] = true;
+        }
+        return $change;
+    }
+
+    // }}}
+    // {{{ _compareTextDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an text field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareTextDefinition($current, $previous)
+    {
+        $change = array();
+        $previous_length = !empty($previous['length']) ? $previous['length'] : 0;
+        $length = !empty($current['length']) ? $current['length'] : 0;
+        if ($previous_length != $length) {
+            $change['length'] = true;
+        }
+        $previous_fixed = !empty($previous['fixed']) ? $previous['fixed'] : 0;
+        $fixed = !empty($current['fixed']) ? $current['fixed'] : 0;
+        if ($previous_fixed != $fixed) {
+            $change['fixed'] = true;
+        }
+        return $change;
+    }
+
+    // }}}
+    // {{{ _compareCLOBDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an CLOB field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareCLOBDefinition($current, $previous)
+    {
+        return $this->_compareTextDefinition($current, $previous);
+    }
+
+    // }}}
+    // {{{ _compareBLOBDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an BLOB field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareBLOBDefinition($current, $previous)
+    {
+        return $this->_compareTextDefinition($current, $previous);
+    }
+
+    // }}}
+    // {{{ _compareDateDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an date field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareDateDefinition($current, $previous)
+    {
+        return array();
+    }
+
+    // }}}
+    // {{{ _compareTimeDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an time field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareTimeDefinition($current, $previous)
+    {
+        return array();
+    }
+
+    // }}}
+    // {{{ _compareTimestampDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an timestamp field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareTimestampDefinition($current, $previous)
+    {
+        return array();
+    }
+
+    // }}}
+    // {{{ _compareBooleanDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an boolean field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareBooleanDefinition($current, $previous)
+    {
+        return array();
+    }
+
+    // }}}
+    // {{{ _compareFloatDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an float field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareFloatDefinition($current, $previous)
+    {
+        return array();
+    }
+
+    // }}}
+    // {{{ _compareDecimalDefinition()
+
+    /**
+     * Obtain an array of changes that may need to applied to an decimal field
+     *
+     * @param array $current new definition
+     * @param array  $previous old definition
+     * @return array  containing all changes that will need to be applied
+     * @access protected
+     */
+    function _compareDecimalDefinition($current, $previous)
+    {
+        return array();
+    }
+
+    // }}}
+    // {{{ quote()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param string $type type to which the value should be converted to
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access public
+     */
+    function quote($value, $type = null, $quote = true, $escape_wildcards = false)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (is_null($value)
+            || ($value === '' && $db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL)
+        ) {
+            if (!$quote) {
+                return null;
+            }
+            return 'NULL';
+        }
+
+        if (is_null($type)) {
+            switch (gettype($value)) {
+            case 'integer':
+                $type = 'integer';
+                break;
+            case 'double':
+                // todo: default to decimal as float is quite unusual
+                // $type = 'float';
+                $type = 'decimal';
+                break;
+            case 'boolean':
+                $type = 'boolean';
+                break;
+            case 'array':
+                 $value = serialize($value);
+            case 'object':
+                 $type = 'text';
+                break;
+            default:
+                if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
+                    $type = 'timestamp';
+                } elseif (preg_match('/^\d{2}:\d{2}$/', $value)) {
+                    $type = 'time';
+                } elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $value)) {
+                    $type = 'date';
+                } else {
+                    $type = 'text';
+                }
+                break;
+            }
+        } elseif (!empty($db->options['datatype_map'][$type])) {
+            $type = $db->options['datatype_map'][$type];
+            if (!empty($db->options['datatype_map_callback'][$type])) {
+                $parameter = array('type' => $type, 'value' => $value, 'quote' => $quote, 'escape_wildcards' => $escape_wildcards);
+                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+            }
+        }
+
+        if (!method_exists($this, "_quote{$type}")) {
+            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'type not defined: '.$type, __FUNCTION__);
+        }
+        $value = $this->{"_quote{$type}"}($value, $quote, $escape_wildcards);
+        if ($quote && $escape_wildcards && $db->string_quoting['escape_pattern']
+            && $db->string_quoting['escape'] !== $db->string_quoting['escape_pattern']
+        ) {
+            $value.= $this->patternEscapeString();
+        }
+        return $value;
+    }
+
+    // }}}
+    // {{{ _quoteInteger()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteInteger($value, $quote, $escape_wildcards)
+    {
+        return (int)$value;
+    }
+
+    // }}}
+    // {{{ _quoteText()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that already contains any DBMS specific
+     *       escaped character sequences.
+     * @access protected
+     */
+    function _quoteText($value, $quote, $escape_wildcards)
+    {
+        if (!$quote) {
+            return $value;
+        }
+
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $value = $db->escape($value, $escape_wildcards);
+        if (PEAR::isError($value)) {
+            return $value;
+        }
+        return "'".$value."'";
+    }
+
+    // }}}
+    // {{{ _readFile()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _readFile($value)
+    {
+        $close = false;
+        if (preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
+            $close = true;
+            if ($match[1] == 'file://') {
+                $value = $match[2];
+            }
+            $value = @fopen($value, 'r');
+        }
+
+        if (is_resource($value)) {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+
+            $fp = $value;
+            $value = '';
+            while (!@feof($fp)) {
+                $value.= @fread($fp, $db->options['lob_buffer_length']);
+            }
+            if ($close) {
+                @fclose($fp);
+            }
+        }
+
+        return $value;
+    }
+
+    // }}}
+    // {{{ _quoteLOB()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteLOB($value, $quote, $escape_wildcards)
+    {
+        $value = $this->_readFile($value);
+        if (PEAR::isError($value)) {
+            return $value;
+        }
+        return $this->_quoteText($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteCLOB()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteCLOB($value, $quote, $escape_wildcards)
+    {
+        return $this->_quoteLOB($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteBLOB()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteBLOB($value, $quote, $escape_wildcards)
+    {
+        return $this->_quoteLOB($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteBoolean()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteBoolean($value, $quote, $escape_wildcards)
+    {
+        return ($value ? 1 : 0);
+    }
+
+    // }}}
+    // {{{ _quoteDate()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteDate($value, $quote, $escape_wildcards)
+    {
+        if ($value === 'CURRENT_DATE') {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
+                return $db->function->now('date');
+            }
+            return 'CURRENT_DATE';
+        }
+        return $this->_quoteText($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteTimestamp()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteTimestamp($value, $quote, $escape_wildcards)
+    {
+        if ($value === 'CURRENT_TIMESTAMP') {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
+                return $db->function->now('timestamp');
+            }
+            return 'CURRENT_TIMESTAMP';
+        }
+        return $this->_quoteText($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteTime()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     *       compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteTime($value, $quote, $escape_wildcards)
+    {
+        if ($value === 'CURRENT_TIME') {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+            if (isset($db->function) && is_a($db->function, 'MDB2_Driver_Function_Common')) {
+                return $db->function->now('time');
+            }
+            return 'CURRENT_TIME';
+        }
+        return $this->_quoteText($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteFloat()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteFloat($value, $quote, $escape_wildcards)
+    {
+        if (preg_match('/^(.*)e([-+])(\d+)$/i', $value, $matches)) {
+            $decimal = $this->_quoteDecimal($matches[1], $quote, $escape_wildcards);
+            $sign = $matches[2];
+            $exponent = str_pad($matches[3], 2, '0', STR_PAD_LEFT);
+            $value = $decimal.'E'.$sign.$exponent;
+        } else {
+            $value = $this->_quoteDecimal($value, $quote, $escape_wildcards);
+        }
+        return $value;
+    }
+
+    // }}}
+    // {{{ _quoteDecimal()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteDecimal($value, $quote, $escape_wildcards)
+    {
+        $value = (string)$value;
+        $value = preg_replace('/[^\d\.,\-+eE]/', '', $value);
+        if (preg_match('/[^\.\d]/', $value)) {
+            if (strpos($value, ',')) {
+                // 1000,00
+                if (!strpos($value, '.')) {
+                    // convert the last "," to a "."
+                    $value = strrev(str_replace(',', '.', strrev($value)));
+                // 1.000,00
+                } elseif (strpos($value, '.') && strpos($value, '.') < strpos($value, ',')) {
+                    $value = str_replace('.', '', $value);
+                    // convert the last "," to a "."
+                    $value = strrev(str_replace(',', '.', strrev($value)));
+                // 1,000.00
+                } else {
+                    $value = str_replace(',', '', $value);
+                }
+            }
+        }
+        return $value;
+    }
+
+    // }}}
+    // {{{ writeLOBToFile()
+
+    /**
+     * retrieve LOB from the database
+     *
+     * @param resource $lob stream handle
+     * @param string $file name of the file into which the LOb should be fetched
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access protected
+     */
+    function writeLOBToFile($lob, $file)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (preg_match('/^(\w+:\/\/)(.*)$/', $file, $match)) {
+            if ($match[1] == 'file://') {
+                $file = $match[2];
+            }
+        }
+
+        $fp = @fopen($file, 'wb');
+        while (!@feof($lob)) {
+            $result = @fread($lob, $db->options['lob_buffer_length']);
+            $read = strlen($result);
+            if (@fwrite($fp, $result, $read) != $read) {
+                @fclose($fp);
+                return $db->raiseError(MDB2_ERROR, null, null,
+                    'could not write to the output file', __FUNCTION__);
+            }
+        }
+        @fclose($fp);
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ _retrieveLOB()
+
+    /**
+     * retrieve LOB from the database
+     *
+     * @param array $lob array
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access protected
+     */
+    function _retrieveLOB(&$lob)
+    {
+        if (is_null($lob['value'])) {
+            $lob['value'] = $lob['resource'];
+        }
+        $lob['loaded'] = true;
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ readLOB()
+
+    /**
+     * Read data from large object input stream.
+     *
+     * @param resource $lob stream handle
+     * @param string $data reference to a variable that will hold data
+     *                          to be read from the large object input stream
+     * @param integer $length    value that indicates the largest ammount ofdata
+     *                          to be read from the large object input stream.
+     * @return mixed the effective number of bytes read from the large object
+     *                      input stream on sucess or an MDB2 error object.
+     * @access public
+     * @see endOfLOB()
+     */
+    function _readLOB($lob, $length)
+    {
+        return substr($lob['value'], $lob['position'], $length);
+    }
+
+    // }}}
+    // {{{ _endOfLOB()
+
+    /**
+     * Determine whether it was reached the end of the large object and
+     * therefore there is no more data to be read for the its input stream.
+     *
+     * @param array $lob array
+     * @return mixed true or false on success, a MDB2 error on failure
+     * @access protected
+     */
+    function _endOfLOB($lob)
+    {
+        return $lob['endOfLOB'];
+    }
+
+    // }}}
+    // {{{ destroyLOB()
+
+    /**
+     * Free any resources allocated during the lifetime of the large object
+     * handler object.
+     *
+     * @param resource $lob stream handle
+     * @access public
+     */
+    function destroyLOB($lob)
+    {
+        $lob_data = stream_get_meta_data($lob);
+        $lob_index = $lob_data['wrapper_data']->lob_index;
+        fclose($lob);
+        if (isset($this->lobs[$lob_index])) {
+            $this->_destroyLOB($this->lobs[$lob_index]);
+            unset($this->lobs[$lob_index]);
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ _destroyLOB()
+
+    /**
+     * Free any resources allocated during the lifetime of the large object
+     * handler object.
+     *
+     * @param array $lob array
+     * @access private
+     */
+    function _destroyLOB(&$lob)
+    {
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ implodeArray()
+
+    /**
+     * apply a type to all values of an array and return as a comma seperated string
+     * useful for generating IN statements
+     *
+     * @access public
+     *
+     * @param array $array data array
+     * @param string $type determines type of the field
+     *
+     * @return string comma seperated values
+     */
+    function implodeArray($array, $type = false)
+    {
+        if (!is_array($array) || empty($array)) {
+            return 'NULL';
+        }
+        if ($type) {
+            foreach ($array as $value) {
+                $return[] = $this->quote($value, $type);
+            }
+        } else {
+            $return = $array;
+        }
+        return implode(', ', $return);
+    }
+
+    // }}}
+    // {{{ 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)) {
+            $operator = strtoupper($operator);
+            switch ($operator) {
+            // case insensitive
+            case 'ILIKE':
+                if (is_null($field)) {
+                    return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                        'case insensitive LIKE matching requires passing the field name', __FUNCTION__);
+                }
+                $db->loadModule('Function', null, true);
+                $match = $db->function->lower($field).' LIKE ';
+                break;
+            // case sensitive
+            case 'LIKE':
+                $match = is_null($field) ? 'LIKE ' : $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 {
+                if ($operator === 'ILIKE') {
+                    $value = strtolower($value);
+                }
+                $escaped = $db->escape($value);
+                if (PEAR::isError($escaped)) {
+                    return $escaped;
+                }
+                $match.= $db->escapePattern($escaped);
+            }
+        }
+        $match.= "'";
+        $match.= $this->patternEscapeString();
+        return $match;
+    }
+
+    // }}}
+    // {{{ patternEscapeString()
+
+    /**
+     * build string to define pattern escape character
+     *
+     * @access public
+     *
+     * @return string define pattern escape character
+     */
+    function patternEscapeString()
+    {
+        return '';
+    }
+
+    // }}}
+    // {{{ 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 =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        // If the user has specified an option to map the native field
+        // type to a custom MDB2 datatype...
+        $db_type = strtok($field['type'], '(), ');
+        if (!empty($db->options['nativetype_map_callback'][$db_type])) {
+            return call_user_func_array($db->options['nativetype_map_callback'][$db_type], array($db, $field));
+        }
+
+        // Otherwise perform the built-in (i.e. normal) MDB2 native type to
+        // MDB2 datatype conversion
+        return $this->_mapNativeDatatype($field);
+    }
+
+    // }}}
+    // {{{ _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 =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ mapPrepareDatatype()
+
+    /**
+     * Maps an mdb2 datatype to mysqli prepare type
+     *
+     * @param string $type
+     * @return string
+     * @access public
+     */
+    function mapPrepareDatatype($type)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (!empty($db->options['datatype_map'][$type])) {
+            $type = $db->options['datatype_map'][$type];
+            if (!empty($db->options['datatype_map_callback'][$type])) {
+                $parameter = array('type' => $type);
+                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+            }
+        }
+
+        return $type;
+    }
+}
+?>
diff --git a/3rdparty/MDB2/Driver/Datatype/mysql.php b/3rdparty/MDB2/Driver/Datatype/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..93dd40524e6795ac4f2ee1a5a152df0d9fb22f63
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Datatype/mysql.php
@@ -0,0 +1,553 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.65 2008/02/22 19:23:49 quipo Exp $
+//
+
+require_once('MDB2/Driver/Datatype/Common.php');
+
+/**
+ * MDB2 MySQL driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Datatype_mysql extends MDB2_Driver_Datatype_Common
+{
+    // {{{ _getCharsetFieldDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
+     * of a field declaration to be used in statements like CREATE TABLE.
+     *
+     * @param string $charset   name of the charset
+     * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
+     *                 of a field declaration.
+     */
+    function _getCharsetFieldDeclaration($charset)
+    {
+        return 'CHARACTER SET '.$charset;
+    }
+
+    // }}}
+    // {{{ _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':
+            if (empty($field['length']) && array_key_exists('default', $field)) {
+                $field['length'] = $db->varchar_max_length;
+            }
+            $length = !empty($field['length']) ? $field['length'] : false;
+            $fixed = !empty($field['fixed']) ? $field['fixed'] : false;
+            return $fixed ? ($length ? 'CHAR('.$length.')' : 'CHAR(255)')
+                : ($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 <= 1) {
+                    return 'TINYINT';
+                } elseif ($length == 2) {
+                    return 'SMALLINT';
+                } elseif ($length == 3) {
+                    return 'MEDIUMINT';
+                } elseif ($length == 4) {
+                    return 'INT';
+                } elseif ($length > 4) {
+                    return 'BIGINT';
+                }
+            }
+            return 'INT';
+        case 'boolean':
+            return 'TINYINT(1)';
+        case 'date':
+            return 'DATE';
+        case 'time':
+            return 'TIME';
+        case 'timestamp':
+            return 'DATETIME';
+        case 'float':
+            return 'DOUBLE';
+        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 = ' AUTO_INCREMENT PRIMARY KEY';
+        } 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);
+        return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull.$autoinc;
+    }
+
+    // }}}
+    // {{{ _getFloatDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare an float 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 float if
+     *                        possible.
+     *
+     *                       default
+     *                        float 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 _getFloatDeclaration($name, $field)
+    {
+        // Since AUTO_INCREMENT can be used for integer or floating-point types,
+        // reuse the INTEGER declaration
+        // @see http://bugs.mysql.com/bug.php?id=31032
+        return $this->_getIntegerDeclaration($name, $field);
+    }
+
+    // }}}
+    // {{{ _getDecimalDeclaration()
+
+    /**
+     * Obtain DBMS specific SQL code portion needed to declare an decimal 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
+     *                        Decimal 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 _getDecimalDeclaration($name, $field)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $default = '';
+        if (array_key_exists('default', $field)) {
+            if ($field['default'] === '') {
+                $field['default'] = empty($field['notnull']) ? null : 0;
+            }
+            $default = ' DEFAULT '.$this->quote($field['default'], 'integer');
+        } elseif (empty($field['notnull'])) {
+            $default = ' DEFAULT NULL';
+        }
+
+        $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
+        $unsigned = empty($field['unsigned']) ? '' : ' UNSIGNED';
+        $name = $db->quoteIdentifier($name, true);
+        return $name.' '.$this->getTypeDeclaration($field).$unsigned.$default.$notnull;
+    }
+
+    // }}}
+    // {{{ 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 BINARY ';
+                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']);
+        $db_type = strtok($db_type, '(), ');
+        if ($db_type == 'national') {
+            $db_type = strtok('(), ');
+        }
+        if (!empty($field['length'])) {
+            $length = strtok($field['length'], ', ');
+            $decimal = strtok(', ');
+        } else {
+            $length = strtok('(), ');
+            $decimal = strtok('(), ');
+        }
+        $type = array();
+        $unsigned = $fixed = null;
+        switch ($db_type) {
+        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':
+            $type[] = 'integer';
+            $unsigned = preg_match('/ unsigned/i', $field['type']);
+            $length = 4;
+            break;
+        case 'bigint':
+            $type[] = 'integer';
+            $unsigned = preg_match('/ unsigned/i', $field['type']);
+            $length = 8;
+            break;
+        case 'tinytext':
+        case 'mediumtext':
+        case 'longtext':
+        case 'text':
+        case 'varchar':
+            $fixed = false;
+        case 'string':
+        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';
+                if ($decimal == 'binary') {
+                    $type[] = 'blob';
+                }
+                $type = array_reverse($type);
+            }
+            if ($fixed !== false) {
+                $fixed = true;
+            }
+            break;
+        case 'enum':
+            $type[] = 'text';
+            preg_match_all('/\'.+\'/U', $field['type'], $matches);
+            $length = 0;
+            $fixed = false;
+            if (is_array($matches)) {
+                foreach ($matches[0] as $value) {
+                    $length = max($length, strlen($value)-2);
+                }
+                if ($length == '1' && count($matches[0]) == 2) {
+                    $type[] = 'boolean';
+                    if (preg_match('/^(is|has)/', $field['name'])) {
+                        $type = array_reverse($type);
+                    }
+                }
+            }
+            $type[] = 'integer';
+        case 'set':
+            $fixed = false;
+            $type[] = 'text';
+            $type[] = 'integer';
+            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';
+            $unsigned = preg_match('/ unsigned/i', $field['type']);
+            break;
+        case 'unknown':
+        case 'decimal':
+        case 'numeric':
+            $type[] = 'decimal';
+            $unsigned = preg_match('/ unsigned/i', $field['type']);
+            if ($decimal !== false) {
+                $length = $length.','.$decimal;
+            }
+            break;
+        case 'tinyblob':
+        case 'mediumblob':
+        case 'longblob':
+        case 'blob':
+            $type[] = 'blob';
+            $length = null;
+            break;
+        case 'binary':
+        case 'varbinary':
+            $type[] = 'blob';
+            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/3rdparty/MDB2/Driver/Datatype/pgsql.php b/3rdparty/MDB2/Driver/Datatype/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..e72aa743b738cc722dab21b2bc3c81d7470ef138
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Datatype/pgsql.php
@@ -0,0 +1,554 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Paul Cooper <pgc@ucecom.com>                                 |
+// +----------------------------------------------------------------------+
+//
+// $Id: pgsql.php,v 1.93 2008/08/28 20:32:57 afz Exp $
+
+require_once('MDB2/Driver/Datatype/Common.php');
+
+/**
+ * MDB2 PostGreSQL driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_Driver_Datatype_pgsql extends MDB2_Driver_Datatype_Common
+{
+    // {{{ _baseConvertResult()
+
+    /**
+     * General type conversion method
+     *
+     * @param mixed   $value refernce to a value to be converted
+     * @param string  $type  specifies which type to convert to
+     * @param boolean $rtrim [optional] when TRUE [default], apply rtrim() to text
+     * @return object a MDB2 error on failure
+     * @access protected
+     */
+    function _baseConvertResult($value, $type, $rtrim = true)
+    {
+        if (is_null($value)) {
+            return null;
+        }
+        switch ($type) {
+        case 'boolean':
+            return $value == 't';
+        case 'float':
+            return doubleval($value);
+        case 'date':
+            return $value;
+        case 'time':
+            return substr($value, 0, strlen('HH:MM:SS'));
+        case 'timestamp':
+            return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS'));
+        case 'blob':
+            $value = pg_unescape_bytea($value);
+            return parent::_baseConvertResult($value, $type, $rtrim);
+        }
+        return parent::_baseConvertResult($value, $type, $rtrim);
+    }
+
+    // }}}
+    // {{{ 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':
+            return 'TEXT';
+        case 'blob':
+            return 'BYTEA';
+        case 'integer':
+            if (!empty($field['autoincrement'])) {
+                if (!empty($field['length'])) {
+                    $length = $field['length'];
+                    if ($length > 4) {
+                        return 'BIGSERIAL PRIMARY KEY';
+                    }
+                }
+                return 'SERIAL PRIMARY KEY';
+            }
+            if (!empty($field['length'])) {
+                $length = $field['length'];
+                if ($length <= 2) {
+                    return 'SMALLINT';
+                } elseif ($length == 3 || $length == 4) {
+                    return 'INT';
+                } elseif ($length > 4) {
+                    return 'BIGINT';
+                }
+            }
+            return 'INT';
+        case 'boolean':
+            return 'BOOLEAN';
+        case 'date':
+            return 'DATE';
+        case 'time':
+            return 'TIME without time zone';
+        case 'timestamp':
+            return 'TIMESTAMP without time zone';
+        case 'float':
+            return 'FLOAT8';
+        case 'decimal':
+            $length = !empty($field['length']) ? $field['length'] : 18;
+            $scale = !empty($field['scale']) ? $field['scale'] : $db->options['decimal_places'];
+            return 'NUMERIC('.$length.','.$scale.')';
+        }
+    }
+
+    // }}}
+    // {{{ _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 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:
+     *
+     *       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;
+        }
+
+        if (!empty($field['unsigned'])) {
+            $db->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
+        }
+        if (!empty($field['autoincrement'])) {
+            $name = $db->quoteIdentifier($name, true);
+            return $name.' '.$this->getTypeDeclaration($field);
+        }
+        $default = '';
+        if (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';
+        $name = $db->quoteIdentifier($name, true);
+        return $name.' '.$this->getTypeDeclaration($field).$default.$notnull;
+    }
+
+    // }}}
+    // {{{ _quoteCLOB()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *      a DBMS specific format.
+     * @access protected
+     */
+    function _quoteCLOB($value, $quote, $escape_wildcards)
+    {
+        return $this->_quoteText($value, $quote, $escape_wildcards);
+    }
+
+    // }}}
+    // {{{ _quoteBLOB()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *      a DBMS specific format.
+     * @access protected
+     */
+    function _quoteBLOB($value, $quote, $escape_wildcards)
+    {
+        if (!$quote) {
+            return $value;
+        }
+        if (version_compare(PHP_VERSION, '5.2.0RC6', '>=')) {
+            $db =$this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+            $connection = $db->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+            $value = @pg_escape_bytea($connection, $value);
+        } else {
+            $value = @pg_escape_bytea($value);
+        }
+        return "'".$value."'";
+    }
+
+    // }}}
+    // {{{ _quoteBoolean()
+
+    /**
+     * Convert a text value into a DBMS specific format that is suitable to
+     * compose query statements.
+     *
+     * @param string $value text string value that is intended to be converted.
+     * @param bool $quote determines if the value should be quoted and escaped
+     * @param bool $escape_wildcards if to escape escape wildcards
+     * @return string text string that represents the given argument value in
+     *       a DBMS specific format.
+     * @access protected
+     */
+    function _quoteBoolean($value, $quote, $escape_wildcards)
+    {
+        $value = $value ? 't' : 'f';
+        if (!$quote) {
+            return $value;
+        }
+        return "'".$value."'";
+    }
+
+    // }}}
+    // {{{ 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.'ILIKE ';
+                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;
+    }
+
+    // }}}
+    // {{{ patternEscapeString()
+
+    /**
+     * build string to define escape pattern string
+     *
+     * @access public
+     *
+     *
+     * @return string define escape pattern
+     */
+    function patternEscapeString()
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        return ' ESCAPE '.$this->quote($db->string_quoting['escape_pattern']);
+    }
+
+    // }}}
+    // {{{ _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 = $field['length'];
+        $type = array();
+        $unsigned = $fixed = null;
+        switch ($db_type) {
+        case 'smallint':
+        case 'int2':
+            $type[] = 'integer';
+            $unsigned = false;
+            $length = 2;
+            if ($length == '2') {
+                $type[] = 'boolean';
+                if (preg_match('/^(is|has)/', $field['name'])) {
+                    $type = array_reverse($type);
+                }
+            }
+            break;
+        case 'int':
+        case 'int4':
+        case 'integer':
+        case 'serial':
+        case 'serial4':
+            $type[] = 'integer';
+            $unsigned = false;
+            $length = 4;
+            break;
+        case 'bigint':
+        case 'int8':
+        case 'bigserial':
+        case 'serial8':
+            $type[] = 'integer';
+            $unsigned = false;
+            $length = 8;
+            break;
+        case 'bool':
+        case 'boolean':
+            $type[] = 'boolean';
+            $length = null;
+            break;
+        case 'text':
+        case 'varchar':
+            $fixed = false;
+        case 'unknown':
+        case 'char':
+        case 'bpchar':
+            $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':
+        case 'timestamptz':
+            $type[] = 'timestamp';
+            $length = null;
+            break;
+        case 'time':
+            $type[] = 'time';
+            $length = null;
+            break;
+        case 'float':
+        case 'float4':
+        case 'float8':
+        case 'double':
+        case 'real':
+            $type[] = 'float';
+            break;
+        case 'decimal':
+        case 'money':
+        case 'numeric':
+            $type[] = 'decimal';
+            if (isset($field['scale'])) {
+                $length = $length.','.$field['scale'];
+            }
+            break;
+        case 'tinyblob':
+        case 'mediumblob':
+        case 'longblob':
+        case 'blob':
+        case 'bytea':
+            $type[] = 'blob';
+            $length = null;
+            break;
+        case 'oid':
+            $type[] = 'blob';
+            $type[] = 'clob';
+            $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);
+    }
+
+    // }}}
+    // {{{ mapPrepareDatatype()
+
+    /**
+     * Maps an mdb2 datatype to native prepare type
+     *
+     * @param string $type
+     * @return string
+     * @access public
+     */
+    function mapPrepareDatatype($type)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (!empty($db->options['datatype_map'][$type])) {
+            $type = $db->options['datatype_map'][$type];
+            if (!empty($db->options['datatype_map_callback'][$type])) {
+                $parameter = array('type' => $type);
+                return call_user_func_array($db->options['datatype_map_callback'][$type], array(&$db, __FUNCTION__, $parameter));
+            }
+        }
+
+        switch ($type) {
+            case 'integer':
+                return 'int';
+            case 'boolean':
+                return 'bool';
+            case 'decimal':
+            case 'float':
+                return 'numeric';
+            case 'clob':
+                return 'text';
+            case 'blob':
+                return 'bytea';
+            default:
+                break;
+        }
+        return $type;
+    }
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Datatype/sqlite.php b/3rdparty/MDB2/Driver/Datatype/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e57e7e56783eb99714274209e0d26f7e41203ed
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Datatype/sqlite.php
@@ -0,0 +1,409 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $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_sqlite 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';
+        } 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);
+        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/3rdparty/MDB2/Driver/Function/Common.php b/3rdparty/MDB2/Driver/Function/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..731f06882ce52a603f1a7bd7ecef5b7c8c23a6f4
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Function/Common.php
@@ -0,0 +1,293 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.21 2008/02/17 18:51:39 quipo Exp $
+//
+
+/**
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+
+/**
+ * Base class for the function modules that is extended by each MDB2 driver
+ *
+ * To load this module in the MDB2 object:
+ * $mdb->loadModule('Function');
+ *
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Function_Common extends MDB2_Module_Common
+{
+    // {{{ executeStoredProc()
+
+    /**
+     * Execute a stored procedure and return any results
+     *
+     * @param string $name string that identifies the function to execute
+     * @param mixed  $params  array that contains the paramaters to pass the stored proc
+     * @param mixed   $types  array that contains the types of the columns in
+     *                        the result set
+     * @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 a result handle or MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+        return $error;
+    }
+
+    // }}}
+    // {{{ functionTable()
+
+    /**
+     * return string for internal table used when calling only a function
+     *
+     * @return string for internal table used when calling only a function
+     * @access public
+     */
+    function functionTable()
+    {
+        return '';
+    }
+
+    // }}}
+    // {{{ 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:
+     * - CURRENT_TIMESTAMP (date and time, TIMESTAMP type)
+     * - CURRENT_DATE (date, DATE type)
+     * - CURRENT_TIME (time, TIME type)
+     *
+     * @param string $type 'timestamp' | 'time' | 'date'
+     *
+     * @return string to call a variable with the current timestamp
+     * @access public
+     */
+    function now($type = 'timestamp')
+    {
+        switch ($type) {
+        case 'time':
+            return 'CURRENT_TIME';
+        case 'date':
+            return 'CURRENT_DATE';
+        case 'timestamp':
+        default:
+            return 'CURRENT_TIMESTAMP';
+        }
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+        return $error;
+    }
+
+    // }}}
+    // {{{ 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 "SUBSTRING($value FROM $position FOR $length)";
+        }
+        return "SUBSTRING($value FROM $position)";
+    }
+
+    // }}}
+    // {{{ replace()
+
+    /**
+     * return string to call a function to get replace inside an SQL statement.
+     *
+     * @return string to call a function to get a replace
+     * @access public
+     */
+    function replace($str, $from_str, $to_str)
+    {
+        return "REPLACE($str, $from_str , $to_str)";
+    }
+
+    // }}}
+    // {{{ concat()
+
+    /**
+     * Returns string to concatenate two or more string parameters
+     *
+     * @param string $value1
+     * @param string $value2
+     * @param string $values...
+     *
+     * @return string to concatenate two strings
+     * @access public
+     */
+    function concat($value1, $value2)
+    {
+        $args = func_get_args();
+        return "(".implode(' || ', $args).")";
+    }
+
+    // }}}
+    // {{{ 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 'RAND()';
+    }
+
+    // }}}
+    // {{{ lower()
+
+    /**
+     * return string to call a function to lower the case of an expression
+     *
+     * @param string $expression
+     *
+     * @return return string to lower case of an expression
+     * @access public
+     */
+    function lower($expression)
+    {
+        return "LOWER($expression)";
+    }
+
+    // }}}
+    // {{{ upper()
+
+    /**
+     * return string to call a function to upper the case of an expression
+     *
+     * @param string $expression
+     *
+     * @return return string to upper case of an expression
+     * @access public
+     */
+    function upper($expression)
+    {
+        return "UPPER($expression)";
+    }
+
+    // }}}
+    // {{{ length()
+
+    /**
+     * return string to call a function to get the length of a string expression
+     *
+     * @param string $expression
+     *
+     * @return return string to get the string expression length
+     * @access public
+     */
+    function length($expression)
+    {
+        return "LENGTH($expression)";
+    }
+
+    // }}}
+    // {{{ guid()
+
+    /**
+     * Returns global unique identifier
+     *
+     * @return string to get global unique identifier
+     * @access public
+     */
+    function guid()
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $error =& $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+        return $error;
+    }
+
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Function/mysql.php b/3rdparty/MDB2/Driver/Function/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..44183c3aa0674baf7f7b50f2f246cb571335bb3f
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Function/mysql.php
@@ -0,0 +1,136 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.12 2008/02/17 18:54:08 quipo Exp $
+//
+
+require_once('MDB2/Driver/Function/Common.php');
+
+/**
+ * MDB2 MySQL driver for the function modules
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Function_mysql extends MDB2_Driver_Function_Common
+{
+     // }}}
+    // {{{ executeStoredProc()
+
+    /**
+     * Execute a stored procedure and return any results
+     *
+     * @param string $name string that identifies the function to execute
+     * @param mixed  $params  array that contains the paramaters to pass the stored proc
+     * @param mixed   $types  array that contains the types of the columns in
+     *                        the result set
+     * @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 a result handle or MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = 'CALL '.$name;
+        $query .= $params ? '('.implode(', ', $params).')' : '()';
+        return $db->query($query, $types, $result_class, $result_wrap_class);
+    }
+
+    // }}}
+    // {{{ 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 'UNIX_TIMESTAMP('. $expression.')';
+    }
+
+    // }}}
+    // {{{ concat()
+
+    /**
+     * Returns string to concatenate two or more string parameters
+     *
+     * @param string $value1
+     * @param string $value2
+     * @param string $values...
+     * @return string to concatenate two strings
+     * @access public
+     **/
+    function concat($value1, $value2)
+    {
+        $args = func_get_args();
+        return "CONCAT(".implode(', ', $args).")";
+    }
+
+    // }}}
+    // {{{ guid()
+
+    /**
+     * Returns global unique identifier
+     *
+     * @return string to get global unique identifier
+     * @access public
+     */
+    function guid()
+    {
+        return 'UUID()';
+    }
+
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Function/pgsql.php b/3rdparty/MDB2/Driver/Function/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..173bfc9149473d57b3641d1ea134b5bc0f4eda68
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Function/pgsql.php
@@ -0,0 +1,115 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Paul Cooper <pgc@ucecom.com>                                 |
+// +----------------------------------------------------------------------+
+//
+// $Id: pgsql.php,v 1.11 2008/11/09 19:46:50 quipo Exp $
+
+require_once('MDB2/Driver/Function/Common.php');
+
+/**
+ * MDB2 MySQL driver for the function modules
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Function_pgsql extends MDB2_Driver_Function_Common
+{
+    // {{{ executeStoredProc()
+
+    /**
+     * Execute a stored procedure and return any results
+     *
+     * @param string $name string that identifies the function to execute
+     * @param mixed  $params  array that contains the paramaters to pass the stored proc
+     * @param mixed   $types  array that contains the types of the columns in
+     *                        the result set
+     * @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 a result handle or MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function &executeStoredProc($name, $params = null, $types = null, $result_class = true, $result_wrap_class = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = 'SELECT * FROM '.$name;
+        $query .= $params ? '('.implode(', ', $params).')' : '()';
+        return $db->query($query, $types, $result_class, $result_wrap_class);
+    }
+    // }}}
+    // {{{ 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 'EXTRACT(EPOCH FROM DATE_TRUNC(\'seconds\', CAST ((' . $expression . ') AS TIMESTAMP)))';
+    }
+
+    // }}}
+    // {{{ 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()';
+    }
+
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Function/sqlite.php b/3rdparty/MDB2/Driver/Function/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a5b7ec8fad628c4cfc9cdbe61e57c9dd309c871
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Function/sqlite.php
@@ -0,0 +1,162 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.10 2008/02/17 18:54:08 quipo Exp $
+//
+
+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_sqlite 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/3rdparty/MDB2/Driver/Manager/Common.php b/3rdparty/MDB2/Driver/Manager/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e16749bffc49b12d2c7ec296f40da06562b7bad
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Manager/Common.php
@@ -0,0 +1,1014 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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.                                          |
+// +----------------------------------------------------------------------+
+// | Authors: Lukas Smith <smith@pooteeweet.org>                          |
+// |          Lorenzo Alberton <l.alberton@quipo.it>                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.72 2009/01/14 15:00:40 quipo Exp $
+//
+
+/**
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ * @author   Lorenzo Alberton <l.alberton@quipo.it>
+ */
+
+/**
+ * Base class for the management modules that is extended by each MDB2 driver
+ *
+ * To load this module in the MDB2 object:
+ * $mdb->loadModule('Manager');
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Manager_Common extends MDB2_Module_Common
+{
+    // {{{ splitTableSchema()
+
+    /**
+     * Split the "[owner|schema].table" notation into an array
+     *
+     * @param string $table [schema and] table name
+     *
+     * @return array array(schema, table)
+     * @access private
+     */
+    function splitTableSchema($table)
+    {
+        $ret = array();
+        if (strpos($table, '.') !== false) {
+            return explode('.', $table);
+        }
+        return array(null, $table);
+    }
+
+    // }}}
+    // {{{ getFieldDeclarationList()
+
+    /**
+     * Get declaration of a number of field in bulk
+     *
+     * @param array $fields  a multidimensional associative array.
+     *      The first dimension determines the field name, while the second
+     *      dimension is keyed with the name of the properties
+     *      of the field being declared as array indexes. Currently, the types
+     *      of supported field properties are as follows:
+     *
+     *      default
+     *          Boolean 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 mixed string on success, a MDB2 error on failure
+     * @access public
+     */
+    function getFieldDeclarationList($fields)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (!is_array($fields) || empty($fields)) {
+            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                'missing any fields', __FUNCTION__);
+        }
+        foreach ($fields as $field_name => $field) {
+            $query = $db->getDeclaration($field['type'], $field_name, $field);
+            if (PEAR::isError($query)) {
+                return $query;
+            }
+            $query_fields[] = $query;
+        }
+        return implode(', ', $query_fields);
+    }
+
+    // }}}
+    // {{{ _fixSequenceName()
+
+    /**
+     * Removes any formatting in an sequence name using the 'seqname_format' option
+     *
+     * @param string $sqn string that containts name of a potential sequence
+     * @param bool $check if only formatted sequences should be returned
+     * @return string name of the sequence with possible formatting removed
+     * @access protected
+     */
+    function _fixSequenceName($sqn, $check = false)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $seq_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['seqname_format']).'$/i';
+        $seq_name = preg_replace($seq_pattern, '\\1', $sqn);
+        if ($seq_name && !strcasecmp($sqn, $db->getSequenceName($seq_name))) {
+            return $seq_name;
+        }
+        if ($check) {
+            return false;
+        }
+        return $sqn;
+    }
+
+    // }}}
+    // {{{ _fixIndexName()
+
+    /**
+     * Removes any formatting in an index name using the 'idxname_format' option
+     *
+     * @param string $idx string that containts name of anl index
+     * @return string name of the index with eventual formatting removed
+     * @access protected
+     */
+    function _fixIndexName($idx)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $idx_pattern = '/^'.preg_replace('/%s/', '([a-z0-9_]+)', $db->options['idxname_format']).'$/i';
+        $idx_name = preg_replace($idx_pattern, '\\1', $idx);
+        if ($idx_name && !strcasecmp($idx, $db->getIndexName($idx_name))) {
+            return $idx_name;
+        }
+        return $idx;
+    }
+
+    // }}}
+    // {{{ createDatabase()
+
+    /**
+     * create a new database
+     *
+     * @param string $name    name of the database that should be created
+     * @param array  $options array with charset, collation info
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function createDatabase($database, $options = array())
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ alterDatabase()
+
+    /**
+     * alter an existing database
+     *
+     * @param string $name    name of the database that should be created
+     * @param array  $options array with charset, collation info
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function alterDatabase($database, $options = array())
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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($database)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ _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'])).')';
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        $result = 'CREATE ';
+        if (!empty($options['temporary'])) {
+            $result .= $this->_getTemporaryTableQuery();
+        }
+        $result .= " TABLE $name ($query_fields)";
+        return $result;
+    }
+
+    // }}}
+    // {{{ _getTemporaryTableQuery()
+
+    /**
+     * A method to return the required SQL string that fits between CREATE ... TABLE
+     * to create the table as a temporary table.
+     *
+     * Should be overridden in driver classes to return the correct string for the
+     * specific database type.
+     *
+     * The default is to return the string "TEMPORARY" - this will result in a
+     * SQL error for any database that does not support temporary tables, or that
+     * requires a different SQL command from "CREATE TEMPORARY TABLE".
+     *
+     * @return string The string required to be placed between "CREATE" and "TABLE"
+     *                to generate a temporary table, if possible.
+     */
+    function _getTemporaryTableQuery()
+    {
+        return 'TEMPORARY';
+    }
+
+    // }}}
+    // {{{ 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
+     *                       The indexes of the array entries are the names of the fields of the table an
+     *                       the array entry values are associative arrays like those that are meant to be
+     *                       passed with the field definitions to get[Type]Declaration() functions.
+     *                          array(
+     *                              'id' => array(
+     *                                  'type' => 'integer',
+     *                                  'unsigned' => 1
+     *                                  'notnull' => 1
+     *                                  'default' => 0
+     *                              ),
+     *                              'name' => array(
+     *                                  'type' => 'text',
+     *                                  'length' => 12
+     *                              ),
+     *                              'password' => array(
+     *                                  'type' => 'text',
+     *                                  'length' => 12
+     *                              )
+     *                          );
+     * @param array $options  An associative array of table options:
+     *                          array(
+     *                              'comment' => 'Foo',
+     *                              'temporary' => true|false,
+     *                          );
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function createTable($name, $fields, $options = array())
+    {
+        $query = $this->_getCreateTableQuery($name, $fields, $options);
+        if (PEAR::isError($query)) {
+            return $query;
+        }
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        $result = $db->exec($query);
+        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;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        return $db->exec("DROP TABLE $name");
+    }
+
+    // }}}
+    // {{{ truncateTable()
+
+    /**
+     * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
+     * it falls back to a DELETE FROM TABLE query)
+     *
+     * @param string $name name of the table that should be truncated
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function truncateTable($name)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        return $db->exec("DELETE FROM $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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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,
+            'method not implementedd', __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,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ listViews()
+
+    /**
+     * list all views in the current database
+     *
+     * @param string database, the current is default
+     *               NB: not all the drivers can get the view names from
+     *               a database other than the current one
+     * @return mixed array of view names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listViews($database = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ listFunctions()
+
+    /**
+     * list all functions in the current database
+     *
+     * @return mixed array of function names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listFunctions()
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ listTables()
+
+    /**
+     * list all tables in the current database
+     *
+     * @param string database, the current is default.
+     *               NB: not all the drivers can get the table names from
+     *               a database other than the current one
+     * @return mixed array of table names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listTables($database = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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 supports() 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->quoteIdentifier($db->getIndexName($name), true);
+        $query = "CREATE INDEX $name ON $table";
+        $fields = array();
+        foreach (array_keys($definition['fields']) as $field) {
+            $fields[] = $db->quoteIdentifier($field, true);
+        }
+        $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->quoteIdentifier($db->getIndexName($name), true);
+        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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ _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)
+    {
+        return '';
+    }
+
+    // }}}
+    // {{{ 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.
+     *                               The full structure of the array looks like this:
+     *          <pre>
+     *          array (
+     *              [primary] => 0
+     *              [unique]  => 0
+     *              [foreign] => 1
+     *              [check]   => 0
+     *              [fields] => array (
+     *                  [field1name] => array() // one entry per each field covered
+     *                  [field2name] => array() // by the index
+     *                  [field3name] => array(
+     *                      [sorting]  => ascending
+     *                      [position] => 3
+     *                  )
+     *              )
+     *              [references] => array(
+     *                  [table] => name
+     *                  [fields] => array(
+     *                      [field1name] => array(  //one entry per each referenced field
+     *                           [position] => 1
+     *                      )
+     *                  )
+     *              )
+     *              [deferrable] => 0
+     *              [initiallydeferred] => 0
+     *              [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [match] => SIMPLE|PARTIAL|FULL
+     *          );
+     *          </pre>
+     * @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;
+        }
+        $table = $db->quoteIdentifier($table, true);
+        $name = $db->quoteIdentifier($db->getIndexName($name), true);
+        $query = "ALTER TABLE $table ADD CONSTRAINT $name";
+        if (!empty($definition['primary'])) {
+            $query.= ' PRIMARY KEY';
+        } elseif (!empty($definition['unique'])) {
+            $query.= ' UNIQUE';
+        } elseif (!empty($definition['foreign'])) {
+            $query.= ' FOREIGN KEY';
+        }
+        $fields = array();
+        foreach (array_keys($definition['fields']) as $field) {
+            $fields[] = $db->quoteIdentifier($field, true);
+        }
+        $query .= ' ('. implode(', ', $fields) . ')';
+        if (!empty($definition['foreign'])) {
+            $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true);
+            $referenced_fields = array();
+            foreach (array_keys($definition['references']['fields']) as $field) {
+                $referenced_fields[] = $db->quoteIdentifier($field, true);
+            }
+            $query .= ' ('. implode(', ', $referenced_fields) . ')';
+            $query .= $this->_getAdvancedFKOptions($definition);
+        }
+        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)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $table = $db->quoteIdentifier($table, true);
+        $name = $db->quoteIdentifier($db->getIndexName($name), true);
+        return $db->exec("ALTER TABLE $table DROP CONSTRAINT $name");
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __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($name)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ listSequences()
+
+    /**
+     * list all sequences in the current database
+     *
+     * @param string database, the current is default
+     *               NB: not all the drivers can get the sequence names from
+     *               a database other than the current one
+     * @return mixed array of sequence names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listSequences($database = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Manager/mysql.php b/3rdparty/MDB2/Driver/Manager/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..099ed48a84ffbb939face2c8c77f064daa99b486
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Manager/mysql.php
@@ -0,0 +1,1432 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.113 2008/11/23 20:30:29 quipo Exp $
+//
+
+require_once('MDB2/Driver/Manager/Common.php');
+
+/**
+ * MDB2 MySQL driver for the management modules
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Manager_mysql 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, collation info
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function createDatabase($name, $options = array())
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $name  = $db->quoteIdentifier($name, true);
+        $query = 'CREATE DATABASE ' . $name;
+        if (!empty($options['charset'])) {
+            $query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text');
+        }
+        if (!empty($options['collation'])) {
+            $query .= ' COLLATE ' . $db->quote($options['collation'], 'text');
+        }
+        return $db->standaloneQuery($query, null, true);
+    }
+
+    // }}}
+    // {{{ alterDatabase()
+
+    /**
+     * alter an existing database
+     *
+     * @param string $name    name of the database that is intended to be changed
+     * @param array  $options array with charset, collation info
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function alterDatabase($name, $options = array())
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true);
+        if (!empty($options['charset'])) {
+            $query .= ' DEFAULT CHARACTER SET ' . $db->quote($options['charset'], 'text');
+        }
+        if (!empty($options['collation'])) {
+            $query .= ' COLLATE ' . $db->quote($options['collation'], 'text');
+        }
+        return $db->standaloneQuery($query, null, true);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        $query = "DROP DATABASE $name";
+        return $db->standaloneQuery($query, null, true);
+    }
+
+    // }}}
+    // {{{ _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'])) {
+            $query .= ' ON UPDATE '.$definition['onupdate'];
+        }
+        if (!empty($definition['ondelete'])) {
+            $query .= ' ON DELETE '.$definition['ondelete'];
+        }
+        return $query;
+    }
+
+    // }}}
+    // {{{ 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
+     *                       The indexes of the array entries are the names of the fields of the table an
+     *                       the array entry values are associative arrays like those that are meant to be
+     *                       passed with the field definitions to get[Type]Declaration() functions.
+     *                          array(
+     *                              'id' => array(
+     *                                  'type' => 'integer',
+     *                                  'unsigned' => 1
+     *                                  'notnull' => 1
+     *                                  'default' => 0
+     *                              ),
+     *                              'name' => array(
+     *                                  'type' => 'text',
+     *                                  'length' => 12
+     *                              ),
+     *                              'password' => array(
+     *                                  'type' => 'text',
+     *                                  'length' => 12
+     *                              )
+     *                          );
+     * @param array $options  An associative array of table options:
+     *                          array(
+     *                              'comment' => 'Foo',
+     *                              'charset' => 'utf8',
+     *                              'collate' => 'utf8_unicode_ci',
+     *                              'type'    => 'innodb',
+     *                          );
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function createTable($name, $fields, $options = array())
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        // if we have an AUTO_INCREMENT column and a PK on more than one field,
+        // we have to handle it differently...
+        $autoincrement = null;
+        if (empty($options['primary'])) {
+            $pk_fields = array();
+            foreach ($fields as $fieldname => $def) {
+                if (!empty($def['primary'])) {
+                    $pk_fields[$fieldname] = true;
+                }
+                if (!empty($def['autoincrement'])) {
+                    $autoincrement = $fieldname;
+                }
+            }
+            if (!is_null($autoincrement) && count($pk_fields) > 1) {
+                $options['primary'] = $pk_fields;
+            } else {
+                // the PK constraint is on max one field => OK
+                $autoincrement = null;
+            }
+        }
+
+        $query = $this->_getCreateTableQuery($name, $fields, $options);
+        if (PEAR::isError($query)) {
+            return $query;
+        }
+
+        if (!is_null($autoincrement)) {
+            // we have to remove the PK clause added by _getIntegerDeclaration()
+            $query = str_replace('AUTO_INCREMENT PRIMARY KEY', 'AUTO_INCREMENT', $query);
+        }
+
+        $options_strings = array();
+
+        if (!empty($options['comment'])) {
+            $options_strings['comment'] = 'COMMENT = '.$db->quote($options['comment'], 'text');
+        }
+
+        if (!empty($options['charset'])) {
+            $options_strings['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
+            if (!empty($options['collate'])) {
+                $options_strings['charset'].= ' COLLATE '.$options['collate'];
+            }
+        }
+
+        $type = false;
+        if (!empty($options['type'])) {
+            $type = $options['type'];
+        } elseif ($db->options['default_table_type']) {
+            $type = $db->options['default_table_type'];
+        }
+        if ($type) {
+            $options_strings[] = "ENGINE = $type";
+        }
+
+        if (!empty($options_strings)) {
+            $query .= ' '.implode(' ', $options_strings);
+        }
+        $result = $db->exec($query);
+        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;
+                    }
+                }
+            }
+        }
+
+        return parent::dropTable($name);
+    }
+
+    // }}}
+    // {{{ truncateTable()
+
+    /**
+     * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
+     * it falls back to a DELETE FROM TABLE query)
+     *
+     * @param string $name name of the table that should be truncated
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function truncateTable($name)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        return $db->exec("TRUNCATE 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;
+        }
+
+        if (empty($table)) {
+            $table = $this->listTables();
+            if (PEAR::isError($table)) {
+                return $table;
+            }
+        }
+        if (is_array($table)) {
+            foreach (array_keys($table) as $k) {
+            	$table[$k] = $db->quoteIdentifier($table[$k], true);
+            }
+            $table = implode(', ', $table);
+        } else {
+            $table = $db->quoteIdentifier($table, true);
+        }
+        
+        $result = $db->exec('OPTIMIZE TABLE '.$table);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        if (!empty($options['analyze'])) {
+            return $db->exec('ANALYZE TABLE '.$table);
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        $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 'rename':
+            case 'name':
+                break;
+            default:
+                return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
+                    'change type "'.$change_name.'" not yet supported', __FUNCTION__);
+            }
+        }
+
+        if ($check) {
+            return MDB2_OK;
+        }
+
+        $query = '';
+        if (!empty($changes['name'])) {
+            $change_name = $db->quoteIdentifier($changes['name'], true);
+            $query .= 'RENAME TO ' . $change_name;
+        }
+
+        if (!empty($changes['add']) && is_array($changes['add'])) {
+            foreach ($changes['add'] as $field_name => $field) {
+                if ($query) {
+                    $query.= ', ';
+                }
+                $query.= 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
+            }
+        }
+
+        if (!empty($changes['remove']) && is_array($changes['remove'])) {
+            foreach ($changes['remove'] as $field_name => $field) {
+                if ($query) {
+                    $query.= ', ';
+                }
+                $field_name = $db->quoteIdentifier($field_name, true);
+                $query.= 'DROP ' . $field_name;
+            }
+        }
+
+        $rename = array();
+        if (!empty($changes['rename']) && is_array($changes['rename'])) {
+            foreach ($changes['rename'] as $field_name => $field) {
+                $rename[$field['name']] = $field_name;
+            }
+        }
+
+        if (!empty($changes['change']) && is_array($changes['change'])) {
+            foreach ($changes['change'] as $field_name => $field) {
+                if ($query) {
+                    $query.= ', ';
+                }
+                if (isset($rename[$field_name])) {
+                    $old_field_name = $rename[$field_name];
+                    unset($rename[$field_name]);
+                } else {
+                    $old_field_name = $field_name;
+                }
+                $old_field_name = $db->quoteIdentifier($old_field_name, true);
+                $query.= "CHANGE $old_field_name " . $db->getDeclaration($field['definition']['type'], $field_name, $field['definition']);
+            }
+        }
+
+        if (!empty($rename) && is_array($rename)) {
+            foreach ($rename as $rename_name => $renamed_field) {
+                if ($query) {
+                    $query.= ', ';
+                }
+                $field = $changes['rename'][$renamed_field];
+                $renamed_field = $db->quoteIdentifier($renamed_field, true);
+                $query.= 'CHANGE ' . $renamed_field . ' ' . $db->getDeclaration($field['definition']['type'], $field['name'], $field['definition']);
+            }
+        }
+
+        if (!$query) {
+            return MDB2_OK;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        return $db->exec("ALTER TABLE $name $query");
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        $result = $db->queryCol('SHOW DATABASES');
+        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;
+    }
+
+    // }}}
+    // {{{ 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->queryCol('SELECT DISTINCT USER FROM mysql.USER');
+    }
+
+    // }}}
+    // {{{ listFunctions()
+
+    /**
+     * list all functions in the current database
+     *
+     * @return mixed array of function names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listFunctions()
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "SELECT name FROM mysql.proc";
+        /*
+        SELECT ROUTINE_NAME
+          FROM INFORMATION_SCHEMA.ROUTINES
+         WHERE ROUTINE_TYPE = 'FUNCTION'
+        */
+        $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;
+    }
+
+    // }}}
+    // {{{ 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 = 'SHOW TRIGGERS';
+        if (!is_null($table)) {
+            $table = $db->quote($table, 'text');
+            $query .= " LIKE $table";
+        }
+        $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;
+    }
+
+    // }}}
+    // {{{ listTables()
+
+    /**
+     * list all tables in the current database
+     *
+     * @param string database, the current is default
+     * @return mixed array of table names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listTables($database = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "SHOW /*!50002 FULL*/ TABLES";
+        if (!is_null($database)) {
+            $query .= " FROM $database";
+        }
+        $query.= "/*!50002  WHERE Table_type = 'BASE TABLE'*/";
+
+        $table_names = $db->queryAll($query, null, MDB2_FETCHMODE_ORDERED);
+        if (PEAR::isError($table_names)) {
+            return $table_names;
+        }
+
+        $result = array();
+        foreach ($table_names as $table) {
+            if (!$this->_fixSequenceName($table[0], true)) {
+                $result[] = $table[0];
+            }
+        }
+        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+            $result = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $result);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ listViews()
+
+    /**
+     * list all views in the current database
+     *
+     * @param string database, the current is default
+     * @return mixed array of view names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listViews($database = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = 'SHOW FULL TABLES';
+        if (!is_null($database)) {
+            $query.= " FROM $database";
+        }
+        $query.= " WHERE Table_type = 'VIEW'";
+
+        $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;
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        $table = $db->quoteIdentifier($table, true);
+        $result = $db->queryCol("SHOW COLUMNS FROM $table");
+        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
+     *
+     * @author Leoncx
+     * @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 supports() to determine whether the DBMS driver can manage indexes.
+     *
+     *                           Example
+     *                               array(
+     *                                   'fields' => array(
+     *                                       'user_name' => array(
+     *                                           'sorting' => 'ascending'
+     *                                           'length' => 10
+     *                                       ),
+     *                                       '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->quoteIdentifier($db->getIndexName($name), true);
+        $query = "CREATE INDEX $name ON $table";
+        $fields = array();
+        foreach ($definition['fields'] as $field => $fieldinfo) {
+            if (!empty($fieldinfo['length'])) {
+                $fields[] = $db->quoteIdentifier($field, true) . '(' . $fieldinfo['length'] . ')';
+            } else {
+                $fields[] = $db->quoteIdentifier($field, true);
+            }
+        }
+        $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;
+        }
+
+        $table = $db->quoteIdentifier($table, true);
+        $name = $db->quoteIdentifier($db->getIndexName($name), true);
+        return $db->exec("DROP INDEX $name ON $table");
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        $key_name = 'Key_name';
+        $non_unique = 'Non_unique';
+        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+            if ($db->options['field_case'] == CASE_LOWER) {
+                $key_name = strtolower($key_name);
+                $non_unique = strtolower($non_unique);
+            } else {
+                $key_name = strtoupper($key_name);
+                $non_unique = strtoupper($non_unique);
+            }
+        }
+
+        $table = $db->quoteIdentifier($table, true);
+        $query = "SHOW INDEX FROM $table";
+        $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($indexes)) {
+            return $indexes;
+        }
+
+        $result = array();
+        foreach ($indexes as $index_data) {
+            if ($index_data[$non_unique] && ($index = $this->_fixIndexName($index_data[$key_name]))) {
+                $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;
+        }
+
+        $type = '';
+        $idx_name = $db->quoteIdentifier($db->getIndexName($name), true);
+        if (!empty($definition['primary'])) {
+            $type = 'PRIMARY';
+            $idx_name = 'KEY';
+        } elseif (!empty($definition['unique'])) {
+            $type = 'UNIQUE';
+        } elseif (!empty($definition['foreign'])) {
+            $type = 'CONSTRAINT';
+        }
+        if (empty($type)) {
+            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                'invalid definition, could not create constraint', __FUNCTION__);
+        }
+
+        $table_quoted = $db->quoteIdentifier($table, true);
+        $query = "ALTER TABLE $table_quoted ADD $type $idx_name";
+        if (!empty($definition['foreign'])) {
+            $query .= ' FOREIGN KEY';
+        }
+        $fields = array();
+        foreach ($definition['fields'] as $field => $fieldinfo) {
+            $quoted = $db->quoteIdentifier($field, true);
+            if (!empty($fieldinfo['length'])) {
+                $quoted .= '(' . $fieldinfo['length'] . ')';
+            }
+            $fields[] = $quoted;
+        }
+        $query .= ' ('. implode(', ', $fields) . ')';
+        if (!empty($definition['foreign'])) {
+            $query.= ' REFERENCES ' . $db->quoteIdentifier($definition['references']['table'], true);
+            $referenced_fields = array();
+            foreach (array_keys($definition['references']['fields']) as $field) {
+                $referenced_fields[] = $db->quoteIdentifier($field, true);
+            }
+            $query .= ' ('. implode(', ', $referenced_fields) . ')';
+            $query .= $this->_getAdvancedFKOptions($definition);
+
+            // add index on FK column(s) or we can't add a FK constraint
+            // @see http://forums.mysql.com/read.php?22,19755,226009
+            $result = $this->createIndex($table, $name.'_fkidx', $definition);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+        $res = $db->exec($query);
+        if (PEAR::isError($res)) {
+            return $res;
+        }
+        if (!empty($definition['foreign'])) {
+            return $this->_createFKTriggers($table, array($name => $definition));
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if ($primary || strtolower($name) == 'primary') {
+            $query = 'ALTER TABLE '. $db->quoteIdentifier($table, true) .' DROP PRIMARY KEY';
+            return $db->exec($query);
+        }
+
+        //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
+            $table = $db->quoteIdentifier($table, true);
+            $name = $db->quoteIdentifier($db->getIndexName($name), true);
+            $query = "ALTER TABLE $table DROP FOREIGN KEY $name";
+            return $db->exec($query);
+        }
+
+        $table = $db->quoteIdentifier($table, true);
+        $name = $db->quoteIdentifier($db->getIndexName($name), true);
+        $query = "ALTER TABLE $table DROP INDEX $name";
+        return $db->exec($query);
+    }
+
+    // }}}
+    // {{{ _createFKTriggers()
+
+    /**
+     * Create triggers to enforce the FOREIGN KEY constraint on the table
+     *
+     * NB: since there's no RAISE_APPLICATION_ERROR facility in mysql,
+     * we call a non-existent procedure to raise the FK violation message.
+     * @see http://forums.mysql.com/read.php?99,55108,71877#msg-71877
+     *
+     * @param string $table        table name
+     * @param array  $foreign_keys FOREIGN KEY definitions
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access private
+     */
+    function _createFKTriggers($table, $foreign_keys)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        // create triggers to enforce FOREIGN KEY constraints
+        if ($db->supports('triggers') && !empty($foreign_keys)) {
+            $table_quoted = $db->quoteIdentifier($table, true);
+            foreach ($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',
+                );
+                $table_fields = array_keys($fkdef['fields']);
+                $referenced_fields = array_keys($fkdef['references']['fields']);
+
+                //create the ON [UPDATE|DELETE] triggers on the primary table
+                $restrict_action = ' IF (SELECT ';
+                $aliased_fields = array();
+                foreach ($table_fields as $field) {
+                    $aliased_fields[] = $table_quoted .'.'.$field .' AS '.$field;
+                }
+                $restrict_action .= implode(',', $aliased_fields)
+                       .' FROM '.$table_quoted
+                       .' 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) .')'
+                                .' THEN CALL %s_ON_TABLE_'.$table.'_VIOLATES_FOREIGN_KEY_CONSTRAINT();'
+                                .' END IF;';
+
+                $cascade_action_update = 'UPDATE '.$table_quoted.' SET '.implode(', ', $new_values) .' WHERE '.implode(' AND ', $conditions). ';';
+                $cascade_action_delete = 'DELETE FROM '.$table_quoted.' WHERE '.implode(' AND ', $conditions). ';';
+                $setnull_action        = 'UPDATE '.$table_quoted.' 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($table, $field);
+                        if (PEAR::isError($field_definition)) {
+                            return $field_definition;
+                        }
+                        $default_values[] = $table_field .' = '. $field_definition[0]['default'];
+                    }
+                    $setdefault_action = 'UPDATE '.$table_quoted.' SET '.implode(', ', $default_values).' WHERE '.implode(' AND ', $conditions). ';';
+                }
+
+                $query = 'CREATE TRIGGER %s'
+                        .' %s ON '.$fkdef['references']['table']
+                        .' FOR EACH ROW BEGIN '
+                        .' SET FOREIGN_KEY_CHECKS = 0; ';  //only really needed for ON UPDATE CASCADE
+
+                if ('CASCADE' == $fkdef['onupdate']) {
+                    $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE',  'update') . $cascade_action_update;
+                } elseif ('SET NULL' == $fkdef['onupdate']) {
+                    $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setnull_action;
+                } elseif ('SET DEFAULT' == $fkdef['onupdate']) {
+                    $sql_update = sprintf($query, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update') . $setdefault_action;
+                } elseif ('NO ACTION' == $fkdef['onupdate']) {
+                    $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'AFTER UPDATE', 'update');
+                } elseif ('RESTRICT' == $fkdef['onupdate']) {
+                    $sql_update = sprintf($query.$restrict_action, $trigger_names['pk_update'], 'BEFORE UPDATE', 'update');
+                }
+                if ('CASCADE' == $fkdef['ondelete']) {
+                    $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE',  'delete') . $cascade_action_delete;
+                } elseif ('SET NULL' == $fkdef['ondelete']) {
+                    $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setnull_action;
+                } elseif ('SET DEFAULT' == $fkdef['ondelete']) {
+                    $sql_delete = sprintf($query, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete') . $setdefault_action;
+                } elseif ('NO ACTION' == $fkdef['ondelete']) {
+                    $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'AFTER DELETE', 'delete');
+                } elseif ('RESTRICT' == $fkdef['ondelete']) {
+                    $sql_delete = sprintf($query.$restrict_action, $trigger_names['pk_delete'], 'BEFORE DELETE', 'delete');
+                }
+                $sql_update .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
+                $sql_delete .= ' SET FOREIGN_KEY_CHECKS = 1; END;';
+
+                $db->pushErrorHandling(PEAR_ERROR_RETURN);
+                $db->expectError(MDB2_ERROR_CANNOT_CREATE);
+                $result = $db->exec($sql_delete);
+                $expected_errmsg = 'This MySQL version doesn\'t support multiple triggers with the same action time and event for one table';
+                $db->popExpect();
+                $db->popErrorHandling();
+                if (PEAR::isError($result)) {
+                    if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
+                        return $result;
+                    }
+                    $db->warnings[] = $expected_errmsg;
+                }
+                $db->pushErrorHandling(PEAR_ERROR_RETURN);
+                $db->expectError(MDB2_ERROR_CANNOT_CREATE);
+                $result = $db->exec($sql_update);
+                $db->popExpect();
+                $db->popErrorHandling();
+                if (PEAR::isError($result) && $result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
+                    if ($result->getCode() != MDB2_ERROR_CANNOT_CREATE) {
+                        return $result;
+                    }
+                    $db->warnings[] = $expected_errmsg;
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ _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;
+        }
+
+        $key_name = 'Key_name';
+        $non_unique = 'Non_unique';
+        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+            if ($db->options['field_case'] == CASE_LOWER) {
+                $key_name = strtolower($key_name);
+                $non_unique = strtolower($non_unique);
+            } else {
+                $key_name = strtoupper($key_name);
+                $non_unique = strtoupper($non_unique);
+            }
+        }
+
+        $query = 'SHOW INDEX FROM ' . $db->quoteIdentifier($table, true);
+        $indexes = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($indexes)) {
+            return $indexes;
+        }
+
+        $result = array();
+        foreach ($indexes as $index_data) {
+            if (!$index_data[$non_unique]) {
+                if ($index_data[$key_name] !== 'PRIMARY') {
+                    $index = $this->_fixIndexName($index_data[$key_name]);
+                } else {
+                    $index = 'PRIMARY';
+                }
+                if (!empty($index)) {
+                    $result[$index] = true;
+                }
+            }
+        }
+        
+        //list FOREIGN KEY constraints...
+        $query = 'SHOW CREATE TABLE '. $db->escape($table);
+        $definition = $db->queryOne($query, 'text', 1);
+        if (!PEAR::isError($definition) && !empty($definition)) {
+            $pattern = '/\bCONSTRAINT\b\s+([^\s]+)\s+\bFOREIGN KEY\b/Uims';
+            if (preg_match_all($pattern, str_replace('`', '', $definition), $matches) > 0) {
+                foreach ($matches[1] as $constraint) {
+                    $result[$constraint] = 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
+     * @param array     $options  An associative array of table options:
+     *                          array(
+     *                              'comment' => 'Foo',
+     *                              'charset' => 'utf8',
+     *                              'collate' => 'utf8_unicode_ci',
+     *                              'type'    => 'innodb',
+     *                          );
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function createSequence($seq_name, $start = 1, $options = array())
+    {
+        $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);
+        
+        $options_strings = array();
+
+        if (!empty($options['comment'])) {
+            $options_strings['comment'] = 'COMMENT = '.$db->quote($options['comment'], 'text');
+        }
+
+        if (!empty($options['charset'])) {
+            $options_strings['charset'] = 'DEFAULT CHARACTER SET '.$options['charset'];
+            if (!empty($options['collate'])) {
+                $options_strings['charset'].= ' COLLATE '.$options['collate'];
+            }
+        }
+
+        $type = false;
+        if (!empty($options['type'])) {
+            $type = $options['type'];
+        } elseif ($db->options['default_table_type']) {
+            $type = $db->options['default_table_type'];
+        }
+        if ($type) {
+            $options_strings[] = "ENGINE = $type";
+        }
+
+        $query = "CREATE TABLE $sequence_name ($seqcol_name INT NOT NULL AUTO_INCREMENT, PRIMARY KEY ($seqcol_name))";
+        if (!empty($options_strings)) {
+            $query .= ' '.implode(' ', $options_strings);
+        }
+        $res = $db->exec($query);
+        if (PEAR::isError($res)) {
+            return $res;
+        }
+
+        if ($start == 1) {
+            return MDB2_OK;
+        }
+
+        $query = "INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')';
+        $res = $db->exec($query);
+        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
+     *
+     * @param string database, the current is default
+     * @return mixed array of sequence names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listSequences($database = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "SHOW TABLES";
+        if (!is_null($database)) {
+            $query .= " FROM $database";
+        }
+        $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/3rdparty/MDB2/Driver/Manager/pgsql.php b/3rdparty/MDB2/Driver/Manager/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..490f697aa5be715a55bcecdad0f6207811f13d06
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Manager/pgsql.php
@@ -0,0 +1,948 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Paul Cooper <pgc@ucecom.com>                                 |
+// +----------------------------------------------------------------------+
+//
+// $Id: pgsql.php,v 1.87 2008/11/29 14:09:59 afz Exp $
+
+require_once('MDB2/Driver/Manager/Common.php');
+
+/**
+ * MDB2 MySQL driver for the management modules
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_Driver_Manager_pgsql 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())
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $name  = $db->quoteIdentifier($name, true);
+        $query = 'CREATE DATABASE ' . $name;
+        if (!empty($options['charset'])) {
+            $query .= ' WITH ENCODING ' . $db->quote($options['charset'], 'text');
+        }
+        return $db->standaloneQuery($query, null, true);
+    }
+
+    // }}}
+    // {{{ alterDatabase()
+
+    /**
+     * alter an existing database
+     *
+     * @param string $name    name of the database that is intended to be changed
+     * @param array  $options array with name, owner info
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function alterDatabase($name, $options = array())
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = 'ALTER DATABASE '. $db->quoteIdentifier($name, true);
+        if (!empty($options['name'])) {
+            $query .= ' RENAME TO ' . $options['name'];
+        }
+        if (!empty($options['owner'])) {
+            $query .= ' OWNER TO ' . $options['owner'];
+        }
+        return $db->standaloneQuery($query, null, true);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        $query = "DROP DATABASE $name";
+        return $db->standaloneQuery($query, null, true);
+    }
+
+    // }}}
+    // {{{ _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'])) {
+            $query .= ' ON UPDATE '.$definition['onupdate'];
+        }
+        if (!empty($definition['ondelete'])) {
+            $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;
+    }
+
+    // }}}
+    // {{{ truncateTable()
+
+    /**
+     * Truncate an existing table (if the TRUNCATE TABLE syntax is not supported,
+     * it falls back to a DELETE FROM TABLE query)
+     *
+     * @param string $name name of the table that should be truncated
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function truncateTable($name)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+        return $db->exec("TRUNCATE 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($options['full'])) {
+            $query .= ' FULL';
+        }
+        if (!empty($options['freeze'])) {
+            $query .= ' FREEZE';
+        }
+        if (!empty($options['analyze'])) {
+            $query .= ' ANALYZE';
+        }
+
+        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)
+    {
+        $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;
+        }
+
+        $name = $db->quoteIdentifier($name, true);
+
+        if (!empty($changes['remove']) && is_array($changes['remove'])) {
+            foreach ($changes['remove'] as $field_name => $field) {
+                $field_name = $db->quoteIdentifier($field_name, true);
+                $query = 'DROP ' . $field_name;
+                $result = $db->exec("ALTER TABLE $name $query");
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+
+        if (!empty($changes['rename']) && is_array($changes['rename'])) {
+            foreach ($changes['rename'] as $field_name => $field) {
+                $field_name = $db->quoteIdentifier($field_name, true);
+                $result = $db->exec("ALTER TABLE $name RENAME COLUMN $field_name TO ".$db->quoteIdentifier($field['name'], true));
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+
+        if (!empty($changes['add']) && is_array($changes['add'])) {
+            foreach ($changes['add'] as $field_name => $field) {
+                $query = 'ADD ' . $db->getDeclaration($field['type'], $field_name, $field);
+                $result = $db->exec("ALTER TABLE $name $query");
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+
+        if (!empty($changes['change']) && is_array($changes['change'])) {
+            foreach ($changes['change'] as $field_name => $field) {
+                $field_name = $db->quoteIdentifier($field_name, true);
+                if (!empty($field['definition']['type'])) {
+                    $server_info = $db->getServerVersion();
+                    if (PEAR::isError($server_info)) {
+                        return $server_info;
+                    }
+                    if (is_array($server_info) && $server_info['major'] < 8) {
+                        return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null,
+                            'changing column type for "'.$change_name.'\" requires PostgreSQL 8.0 or above', __FUNCTION__);
+                    }
+                    $db->loadModule('Datatype', null, true);
+                    $type = $db->datatype->getTypeDeclaration($field['definition']);
+                    $query = "ALTER $field_name TYPE $type USING CAST($field_name AS $type)";
+                    $result = $db->exec("ALTER TABLE $name $query");
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                }
+                if (array_key_exists('default', $field['definition'])) {
+                    $query = "ALTER $field_name SET DEFAULT ".$db->quote($field['definition']['default'], $field['definition']['type']);
+                    $result = $db->exec("ALTER TABLE $name $query");
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                }
+                if (!empty($field['definition']['notnull'])) {
+                    $query = "ALTER $field_name ".($field['definition']['notnull'] ? 'SET' : 'DROP').' NOT NULL';
+                    $result = $db->exec("ALTER TABLE $name $query");
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                }
+            }
+        }
+
+        if (!empty($changes['name'])) {
+            $change_name = $db->quoteIdentifier($changes['name'], true);
+            $result = $db->exec("ALTER TABLE $name RENAME TO ".$change_name);
+            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;
+        }
+
+        $query = 'SELECT datname FROM pg_database';
+        $result2 = $db->standaloneQuery($query, array('text'), false);
+        if (!MDB2::isResultCommon($result2)) {
+            return $result2;
+        }
+
+        $result = $result2->fetchCol();
+        $result2->free();
+        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;
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        $query = 'SELECT usename FROM pg_user';
+        $result2 = $db->standaloneQuery($query, array('text'), false);
+        if (!MDB2::isResultCommon($result2)) {
+            return $result2;
+        }
+
+        $result = $result2->fetchCol();
+        $result2->free();
+        return $result;
+    }
+
+    // }}}
+    // {{{ 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()
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "SELECT viewname
+                    FROM pg_views
+                   WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
+                     AND viewname !~ '^pg_'";
+        $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 viewname FROM pg_views NATURAL JOIN pg_tables';
+        $query.= ' WHERE tablename ='.$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;
+    }
+
+    // }}}
+    // {{{ listFunctions()
+
+    /**
+     * list all functions in the current database
+     *
+     * @return mixed array of function names on success, a MDB2 error on failure
+     * @access public
+     */
+    function listFunctions()
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "
+            SELECT
+                proname
+            FROM
+                pg_proc pr,
+                pg_type tp
+            WHERE
+                tp.oid = pr.prorettype
+                AND pr.proisagg = FALSE
+                AND tp.typname <> 'trigger'
+                AND pr.pronamespace IN
+                    (SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
+        $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;
+    }
+
+    // }}}
+    // {{{ 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 trg.tgname AS trigger_name
+                    FROM pg_trigger trg,
+                         pg_class tbl
+                   WHERE trg.tgrelid = tbl.oid';
+        if (!is_null($table)) {
+            $table = $db->quote(strtoupper($table), 'text');
+            $query .= " AND tbl.relname = $table";
+        }
+        $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;
+    }
+
+    // }}}
+    // {{{ 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()
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        // gratuitously stolen from PEAR DB _getSpecialQuery in pgsql.php
+        $query = 'SELECT c.relname AS "Name"'
+            . ' FROM pg_class c, pg_user u'
+            . ' WHERE c.relowner = u.usesysid'
+            . " AND c.relkind = 'r'"
+            . ' AND NOT EXISTS'
+            . ' (SELECT 1 FROM pg_views'
+            . '  WHERE viewname = c.relname)'
+            . " AND c.relname !~ '^(pg_|sql_)'"
+            . ' UNION'
+            . ' SELECT c.relname AS "Name"'
+            . ' FROM pg_class c'
+            . " WHERE c.relkind = 'r'"
+            . ' AND NOT EXISTS'
+            . ' (SELECT 1 FROM pg_views'
+            . '  WHERE viewname = c.relname)'
+            . ' AND NOT EXISTS'
+            . ' (SELECT 1 FROM pg_user'
+            . '  WHERE usesysid = c.relowner)'
+            . " AND c.relname !~ '^pg_'";
+        $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;
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        list($schema, $table) = $this->splitTableSchema($table);
+
+        $table = $db->quoteIdentifier($table, true);
+        if (!empty($schema)) {
+            $table = $db->quoteIdentifier($schema, true) . '.' .$table;
+        }
+        $db->setLimit(1);
+        $result2 = $db->query("SELECT * FROM $table");
+        if (PEAR::isError($result2)) {
+            return $result2;
+        }
+        $result = $result2->getColumnNames();
+        $result2->free();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        return array_flip($result);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        list($schema, $table) = $this->splitTableSchema($table);
+
+        $table = $db->quote($table, 'text');
+        $subquery = "SELECT indexrelid
+                       FROM pg_index
+                  LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
+                  LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
+                      WHERE pg_class.relname = $table
+                        AND indisunique != 't'
+                        AND indisprimary != 't'";
+        if (!empty($schema)) {
+            $subquery .= ' AND pg_namespace.nspname = '.$db->quote($schema, 'text');
+        }
+        $query = "SELECT relname FROM pg_class WHERE oid IN ($subquery)";
+        $indexes = $db->queryCol($query, 'text');
+        if (PEAR::isError($indexes)) {
+            return $indexes;
+        }
+
+        $result = array();
+        foreach ($indexes as $index) {
+            $index = $this->_fixIndexName($index);
+            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);
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        // is it an UNIQUE index?
+        $query = 'SELECT relname
+                    FROM pg_class
+                   WHERE oid IN (
+                         SELECT indexrelid
+                           FROM pg_index, pg_class
+                          WHERE pg_class.relname = '.$db->quote($table, 'text').'
+                            AND pg_class.oid = pg_index.indrelid
+                            AND indisunique = \'t\')
+                  EXCEPT
+                  SELECT conname
+                   FROM pg_constraint, pg_class
+                  WHERE pg_constraint.conrelid = pg_class.oid
+                    AND relname = '. $db->quote($table, 'text');
+        $unique = $db->queryCol($query, 'text');
+        if (PEAR::isError($unique) || empty($unique)) {
+            // not an UNIQUE index, maybe a CONSTRAINT
+            return parent::dropConstraint($table, $name, $primary);
+        }
+
+        if (in_array($name, $unique)) {
+            return $db->exec('DROP INDEX '.$db->quoteIdentifier($name, true));
+        }
+        $idxname = $db->getIndexName($name);
+        if (in_array($idxname, $unique)) {
+            return $db->exec('DROP INDEX '.$db->quoteIdentifier($idxname, true));
+        }
+        return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+            $name . ' is not an existing constraint for table ' . $table, __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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;
+        }
+
+        list($schema, $table) = $this->splitTableSchema($table);
+
+        $table = $db->quote($table, 'text');
+        $query = 'SELECT conname
+                    FROM pg_constraint
+               LEFT JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
+               LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
+                   WHERE relname = ' .$table;
+        if (!empty($schema)) {
+            $query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
+        }
+        $query .= '
+                   UNION DISTINCT
+                  SELECT relname
+                    FROM pg_class
+                   WHERE oid IN (
+                         SELECT indexrelid
+                           FROM pg_index
+                      LEFT JOIN pg_class ON pg_class.oid = pg_index.indrelid
+                      LEFT JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid
+                          WHERE pg_class.relname = '.$table.'
+                            AND indisunique = \'t\'';
+        if (!empty($schema)) {
+            $query .= ' AND pg_namespace.nspname = ' . $db->quote($schema, 'text');
+        }
+        $query .= ')';
+        $constraints = $db->queryCol($query);
+        if (PEAR::isError($constraints)) {
+            return $constraints;
+        }
+
+        $result = array();
+        foreach ($constraints as $constraint) {
+            $constraint = $this->_fixIndexName($constraint);
+            if (!empty($constraint)) {
+                $result[$constraint] = true;
+            }
+        }
+
+        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE
+            && $db->options['field_case'] == CASE_LOWER
+        ) {
+            $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);
+        return $db->exec("CREATE SEQUENCE $sequence_name INCREMENT 1".
+            ($start < 1 ? " MINVALUE $start" : '')." START $start");
+    }
+
+    // }}}
+    // {{{ 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 SEQUENCE $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()
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "SELECT relname FROM pg_class WHERE relkind = 'S' AND relnamespace IN";
+        $query.= "(SELECT oid FROM pg_namespace WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
+        $table_names = $db->queryCol($query);
+        if (PEAR::isError($table_names)) {
+            return $table_names;
+        }
+        $result = array();
+        foreach ($table_names as $table_name) {
+            $result[] = $this->_fixSequenceName($table_name);
+        }
+        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/3rdparty/MDB2/Driver/Manager/sqlite.php b/3rdparty/MDB2/Driver/Manager/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..64019669645a8dd31a3b07681eb0405e9c4fd83b
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Manager/sqlite.php
@@ -0,0 +1,1365 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton                       |
+// | 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.                                          |
+// +----------------------------------------------------------------------+
+// | Authors: Lukas Smith <smith@pooteeweet.org>                          |
+// |          Lorenzo Alberton <l.alberton@quipo.it>                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.76 2008/05/31 11:48:48 quipo Exp $
+//
+
+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_sqlite 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 = sqlite_open($database_file, $db->dsn['mode'], $php_errormsg);
+        if (!$handle) {
+            return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null,
+                (isset($php_errormsg) ? $php_errormsg : 'could not create the database file'), __FUNCTION__);
+        }
+        if (!empty($options['charset'])) {
+            $query = 'PRAGMA encoding = ' . $db->quote($options['charset'], 'text');
+            @sqlite_query($query, $handle);
+        }
+        @sqlite_close($handle);
+        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/3rdparty/MDB2/Driver/Native/Common.php b/3rdparty/MDB2/Driver/Native/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..c01caa35b716fce134664134956f8c635e3adc7a
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Native/Common.php
@@ -0,0 +1,61 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.2 2007/09/09 13:47:36 quipo Exp $
+//
+
+/**
+ * Base class for the natuve modules that is extended by each MDB2 driver
+ *
+ * To load this module in the MDB2 object:
+ * $mdb->loadModule('Native');
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Native_Common extends MDB2_Module_Common
+{
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Native/mysql.php b/3rdparty/MDB2/Driver/Native/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..90ff068e6ffb21d26ab3b5dc7517e6614f331e61
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Native/mysql.php
@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.9 2006/06/18 21:59:05 lsmith Exp $
+//
+
+require_once 'MDB2/Driver/Native/Common.php';
+
+/**
+ * MDB2 MySQL driver for the native module
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Native_mysql extends MDB2_Driver_Native_Common
+{
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Native/pgsql.php b/3rdparty/MDB2/Driver/Native/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..acab8389c892ec0f0c95289d7be63af8cd8dff2e
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Native/pgsql.php
@@ -0,0 +1,88 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Paul Cooper <pgc@ucecom.com>                                 |
+// +----------------------------------------------------------------------+
+//
+// $Id: pgsql.php,v 1.12 2006/07/15 13:07:15 lsmith Exp $
+
+require_once 'MDB2/Driver/Native/Common.php';
+
+/**
+ * MDB2 PostGreSQL driver for the native module
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_Driver_Native_pgsql extends MDB2_Driver_Native_Common
+{
+    // }}}
+    // {{{ deleteOID()
+
+    /**
+     * delete an OID
+     *
+     * @param integer    $OID
+     * @return mixed MDB2_OK on success or MDB2 Error Object on failure
+     * @access public
+     */
+    function deleteOID($OID)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $connection = $db->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        if (!@pg_lo_unlink($connection, $OID)) {
+            return $db->raiseError(null, null, null,
+                'Unable to unlink OID: '.$OID, __FUNCTION__);
+        }
+        return MDB2_OK;
+    }
+
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Native/sqlite.php b/3rdparty/MDB2/Driver/Native/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..0213293a50a6f351a173727faab816fa7c0b2252
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Native/sqlite.php
@@ -0,0 +1,60 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.9 2006/06/18 21:59:05 lsmith Exp $
+//
+
+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/3rdparty/MDB2/Driver/Reverse/Common.php b/3rdparty/MDB2/Driver/Reverse/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..c78d84f760a490a62ebcb858edbfe02c9b8f6b05
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Reverse/Common.php
@@ -0,0 +1,517 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.43 2009/01/14 15:01:21 quipo Exp $
+//
+
+/**
+ * @package MDB2
+ * @category Database
+ */
+
+/**
+ * These are constants for the tableInfo-function
+ * they are bitwised or'ed. so if there are more constants to be defined
+ * in the future, adjust MDB2_TABLEINFO_FULL accordingly
+ */
+
+define('MDB2_TABLEINFO_ORDER',      1);
+define('MDB2_TABLEINFO_ORDERTABLE', 2);
+define('MDB2_TABLEINFO_FULL',       3);
+
+/**
+ * Base class for the schema reverse engineering module that is extended by each MDB2 driver
+ *
+ * To load this module in the MDB2 object:
+ * $mdb->loadModule('Reverse');
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_Reverse_Common extends MDB2_Module_Common
+{
+    // {{{ splitTableSchema()
+
+    /**
+     * Split the "[owner|schema].table" notation into an array
+     *
+     * @param string $table [schema and] table name
+     *
+     * @return array array(schema, table)
+     * @access private
+     */
+    function splitTableSchema($table)
+    {
+        $ret = array();
+        if (strpos($table, '.') !== false) {
+            return explode('.', $table);
+        }
+        return array(null, $table);
+    }
+
+    // }}}
+    // {{{ getTableFieldDefinition()
+
+    /**
+     * Get the structure of a field into an array
+     *
+     * @param string    $table     name of table that should be used in method
+     * @param string    $field     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 all or some of these indices, depending on the field data type:
+     *          [notnull] [nativetype] [length] [fixed] [default] [type] [mdb2type]
+     * @access public
+     */
+    function getTableFieldDefinition($table, $field)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ getTableIndexDefinition()
+
+    /**
+     * Get the structure of an index into an array
+     *
+     * @param string    $table      name of table that should be used in method
+     * @param string    $index      name of index that should be used in method
+     * @return mixed data array on success, a MDB2 error on failure
+     *          The returned array has this structure:
+     *          </pre>
+     *          array (
+     *              [fields] => array (
+     *                  [field1name] => array() // one entry per each field covered
+     *                  [field2name] => array() // by the index
+     *                  [field3name] => array(
+     *                      [sorting] => ascending
+     *                  )
+     *              )
+     *          );
+     *          </pre>
+     * @access public
+     */
+    function getTableIndexDefinition($table, $index)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ getTableConstraintDefinition()
+
+    /**
+     * Get the structure of an constraints into an array
+     *
+     * @param string    $table      name of table that should be used in method
+     * @param string    $index      name of index that should be used in method
+     * @return mixed data array on success, a MDB2 error on failure
+     *          The returned array has this structure:
+     *          <pre>
+     *          array (
+     *              [primary] => 0
+     *              [unique]  => 0
+     *              [foreign] => 1
+     *              [check]   => 0
+     *              [fields] => array (
+     *                  [field1name] => array() // one entry per each field covered
+     *                  [field2name] => array() // by the index
+     *                  [field3name] => array(
+     *                      [sorting]  => ascending
+     *                      [position] => 3
+     *                  )
+     *              )
+     *              [references] => array(
+     *                  [table] => name
+     *                  [fields] => array(
+     *                      [field1name] => array(  //one entry per each referenced field
+     *                           [position] => 1
+     *                      )
+     *                  )
+     *              )
+     *              [deferrable] => 0
+     *              [initiallydeferred] => 0
+     *              [onupdate] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [ondelete] => CASCADE|RESTRICT|SET NULL|SET DEFAULT|NO ACTION
+     *              [match] => SIMPLE|PARTIAL|FULL
+     *          );
+     *          </pre>
+     * @access public
+     */
+    function getTableConstraintDefinition($table, $index)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ getSequenceDefinition()
+
+    /**
+     * Get the structure of a sequence into an array
+     *
+     * @param string    $sequence   name of sequence that should be used in method
+     * @return mixed data array on success, a MDB2 error on failure
+     *          The returned array has this structure:
+     *          <pre>
+     *          array (
+     *              [start] => n
+     *          );
+     *          </pre>
+     * @access public
+     */
+    function getSequenceDefinition($sequence)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $start = $db->currId($sequence);
+        if (PEAR::isError($start)) {
+            return $start;
+        }
+        if ($db->supports('current_id')) {
+            $start++;
+        } else {
+            $db->warnings[] = 'database does not support getting current
+                sequence value, the sequence value was incremented';
+        }
+        $definition = array();
+        if ($start != 1) {
+            $definition = array('start' => $start);
+        }
+        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
+     *          The returned array has this structure:
+     *          <pre>
+     *          array (
+     *              [trigger_name]    => 'trigger name',
+     *              [table_name]      => 'table name',
+     *              [trigger_body]    => 'trigger body definition',
+     *              [trigger_type]    => 'BEFORE' | 'AFTER',
+     *              [trigger_event]   => 'INSERT' | 'UPDATE' | 'DELETE'
+     *                  //or comma separated list of multiple events, when supported
+     *              [trigger_enabled] => true|false
+     *              [trigger_comment] => 'trigger comment',
+     *          );
+     *          </pre>
+     *          The oci8 driver also returns a [when_clause] index.
+     * @access public
+     */
+    function getTriggerDefinition($trigger)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+            'method not implemented', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * The format of the resulting array depends on which <var>$mode</var>
+     * you select.  The sample output below is based on this query:
+     * <pre>
+     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
+     *    FROM tblFoo
+     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
+     * </pre>
+     *
+     * <ul>
+     * <li>
+     *
+     * <kbd>null</kbd> (default)
+     *   <pre>
+     *   [0] => Array (
+     *       [table] => tblFoo
+     *       [name] => fldId
+     *       [type] => int
+     *       [len] => 11
+     *       [flags] => primary_key not_null
+     *   )
+     *   [1] => Array (
+     *       [table] => tblFoo
+     *       [name] => fldPhone
+     *       [type] => string
+     *       [len] => 20
+     *       [flags] =>
+     *   )
+     *   [2] => Array (
+     *       [table] => tblBar
+     *       [name] => fldId
+     *       [type] => int
+     *       [len] => 11
+     *       [flags] => primary_key not_null
+     *   )
+     *   </pre>
+     *
+     * </li><li>
+     *
+     * <kbd>MDB2_TABLEINFO_ORDER</kbd>
+     *
+     *   <p>In addition to the information found in the default output,
+     *   a notation of the number of columns is provided by the
+     *   <samp>num_fields</samp> element while the <samp>order</samp>
+     *   element provides an array with the column names as the keys and
+     *   their location index number (corresponding to the keys in the
+     *   the default output) as the values.</p>
+     *
+     *   <p>If a result set has identical field names, the last one is
+     *   used.</p>
+     *
+     *   <pre>
+     *   [num_fields] => 3
+     *   [order] => Array (
+     *       [fldId] => 2
+     *       [fldTrans] => 1
+     *   )
+     *   </pre>
+     *
+     * </li><li>
+     *
+     * <kbd>MDB2_TABLEINFO_ORDERTABLE</kbd>
+     *
+     *   <p>Similar to <kbd>MDB2_TABLEINFO_ORDER</kbd> but adds more
+     *   dimensions to the array in which the table names are keys and
+     *   the field names are sub-keys.  This is helpful for queries that
+     *   join tables which have identical field names.</p>
+     *
+     *   <pre>
+     *   [num_fields] => 3
+     *   [ordertable] => Array (
+     *       [tblFoo] => Array (
+     *           [fldId] => 0
+     *           [fldPhone] => 1
+     *       )
+     *       [tblBar] => Array (
+     *           [fldId] => 2
+     *       )
+     *   )
+     *   </pre>
+     *
+     * </li>
+     * </ul>
+     *
+     * The <samp>flags</samp> element contains a space separated list
+     * of extra information about the field.  This data is inconsistent
+     * between DBMS's due to the way each DBMS works.
+     *   + <samp>primary_key</samp>
+     *   + <samp>unique_key</samp>
+     *   + <samp>multiple_key</samp>
+     *   + <samp>not_null</samp>
+     *
+     * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
+     * elements if <var>$result</var> is a table name.  The following DBMS's
+     * provide full information from queries:
+     *   + fbsql
+     *   + mysql
+     *
+     * If the 'portability' option has <samp>MDB2_PORTABILITY_FIX_CASE</samp>
+     * turned on, the names of tables and fields will be lower or upper cased.
+     *
+     * @param object|string  $result  MDB2_result object from a query or a
+     *                                string containing the name of a table.
+     *                                While this also accepts a query result
+     *                                resource identifier, this behavior is
+     *                                deprecated.
+     * @param int  $mode   either unused or one of the tableInfo modes:
+     *                     <kbd>MDB2_TABLEINFO_ORDERTABLE</kbd>,
+     *                     <kbd>MDB2_TABLEINFO_ORDER</kbd> or
+     *                     <kbd>MDB2_TABLEINFO_FULL</kbd> (which does both).
+     *                     These are bitwise, so the first two can be
+     *                     combined using <kbd>|</kbd>.
+     *
+     * @return array  an associative array with the information requested.
+     *                 A MDB2_Error object on failure.
+     *
+     * @see MDB2_Driver_Common::setOption()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if (!is_string($result)) {
+            return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'method not implemented', __FUNCTION__);
+        }
+
+        $db->loadModule('Manager', null, true);
+        $fields = $db->manager->listTableFields($result);
+        if (PEAR::isError($fields)) {
+            return $fields;
+        }
+
+        $flags = array();
+
+        $idxname_format = $db->getOption('idxname_format');
+        $db->setOption('idxname_format', '%s');
+
+        $indexes = $db->manager->listTableIndexes($result);
+        if (PEAR::isError($indexes)) {
+            $db->setOption('idxname_format', $idxname_format);
+            return $indexes;
+        }
+
+        foreach ($indexes as $index) {
+            $definition = $this->getTableIndexDefinition($result, $index);
+            if (PEAR::isError($definition)) {
+                $db->setOption('idxname_format', $idxname_format);
+                return $definition;
+            }
+            if (count($definition['fields']) > 1) {
+                foreach ($definition['fields'] as $field => $sort) {
+                    $flags[$field] = 'multiple_key';
+                }
+            }
+        }
+
+        $constraints = $db->manager->listTableConstraints($result);
+        if (PEAR::isError($constraints)) {
+            return $constraints;
+        }
+
+        foreach ($constraints as $constraint) {
+            $definition = $this->getTableConstraintDefinition($result, $constraint);
+            if (PEAR::isError($definition)) {
+                $db->setOption('idxname_format', $idxname_format);
+                return $definition;
+            }
+            $flag = !empty($definition['primary'])
+                ? 'primary_key' : (!empty($definition['unique'])
+                    ? 'unique_key' : false);
+            if ($flag) {
+                foreach ($definition['fields'] as $field => $sort) {
+                    if (empty($flags[$field]) || $flags[$field] != 'primary_key') {
+                        $flags[$field] = $flag;
+                    }
+                }
+            }
+        }
+
+        $res = array();
+
+        if ($mode) {
+            $res['num_fields'] = count($fields);
+        }
+
+        foreach ($fields as $i => $field) {
+            $definition = $this->getTableFieldDefinition($result, $field);
+            if (PEAR::isError($definition)) {
+                $db->setOption('idxname_format', $idxname_format);
+                return $definition;
+            }
+            $res[$i] = $definition[0];
+            $res[$i]['name'] = $field;
+            $res[$i]['table'] = $result;
+            $res[$i]['type'] = preg_replace('/^([a-z]+).*$/i', '\\1', trim($definition[0]['nativetype']));
+            // 'primary_key', 'unique_key', 'multiple_key'
+            $res[$i]['flags'] = empty($flags[$field]) ? '' : $flags[$field];
+            // not_null', 'unsigned', 'auto_increment', 'default_[rawencodedvalue]'
+            if (!empty($res[$i]['notnull'])) {
+                $res[$i]['flags'].= ' not_null';
+            }
+            if (!empty($res[$i]['unsigned'])) {
+                $res[$i]['flags'].= ' unsigned';
+            }
+            if (!empty($res[$i]['auto_increment'])) {
+                $res[$i]['flags'].= ' autoincrement';
+            }
+            if (!empty($res[$i]['default'])) {
+                $res[$i]['flags'].= ' default_'.rawurlencode($res[$i]['default']);
+            }
+
+            if ($mode & MDB2_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        $db->setOption('idxname_format', $idxname_format);
+        return $res;
+    }
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Reverse/mysql.php b/3rdparty/MDB2/Driver/Reverse/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..6e366c22a5e09b78cbd351a3fd37e9953f8be290
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Reverse/mysql.php
@@ -0,0 +1,536 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.80 2008/03/26 21:15:37 quipo Exp $
+//
+
+require_once('MDB2/Driver/Reverse/Common.php');
+
+/**
+ * MDB2 MySQL driver for the schema reverse engineering module
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ * @author  Lorenzo Alberton <l.alberton@quipo.it>
+ */
+class MDB2_Driver_Reverse_mysql extends MDB2_Driver_Reverse_Common
+{
+    // {{{ getTableFieldDefinition()
+
+    /**
+     * Get the structure 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
+     * @access public
+     */
+    function getTableFieldDefinition($table_name, $field_name)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $result = $db->loadModule('Datatype', null, true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        list($schema, $table) = $this->splitTableSchema($table_name);
+
+        $table = $db->quoteIdentifier($table, true);
+        $query = "SHOW FULL COLUMNS FROM $table LIKE ".$db->quote($field_name);
+        $columns = $db->queryAll($query, null, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($columns)) {
+            return $columns;
+        }
+        foreach ($columns as $column) {
+            $column = array_change_key_case($column, CASE_LOWER);
+            $column['name'] = $column['field'];
+            unset($column['field']);
+            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['null']) || $column['null'] !== 'YES') {
+                    $notnull = true;
+                }
+                $default = false;
+                if (array_key_exists('default', $column)) {
+                    $default = $column['default'];
+                    if (is_null($default) && $notnull) {
+                        $default = '';
+                    }
+                }
+                $autoincrement = false;
+                if (!empty($column['extra']) && $column['extra'] == 'auto_increment') {
+                    $autoincrement = true;
+                }
+                $collate = null;
+                if (!empty($column['collation'])) {
+                    $collate = $column['collation'];
+                    $charset = preg_replace('/(.+?)(_.+)?/', '$1', $collate);
+                }
+
+                $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;
+                }
+                if (!is_null($collate)) {
+                    $definition[0]['collate'] = $collate;
+                    $definition[0]['charset'] = $charset;
+                }
+                foreach ($types as $key => $type) {
+                    $definition[$key] = $definition[0];
+                    if ($type == 'clob' || $type == 'blob') {
+                        unset($definition[$key]['default']);
+                    } elseif ($type == 'timestamp' && $notnull && empty($definition[$key]['default'])) {
+                        $definition[$key]['default'] = '0000-00-00 00:00:00';
+                    }
+                    $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 structure 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);
+
+        $table = $db->quoteIdentifier($table, true);
+        $query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */";
+        $index_name_mdb2 = $db->getIndexName($index_name);
+        $result = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2)));
+        if (!PEAR::isError($result) && !is_null($result)) {
+            // apply 'idxname_format' only if the query succeeded, otherwise
+            // fallback to the given $index_name, without transformation
+            $index_name = $index_name_mdb2;
+        }
+        $result = $db->query(sprintf($query, $db->quote($index_name)));
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        $colpos = 1;
+        $definition = array();
+        while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
+            $row = array_change_key_case($row, CASE_LOWER);
+            $key_name = $row['key_name'];
+            if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+                if ($db->options['field_case'] == CASE_LOWER) {
+                    $key_name = strtolower($key_name);
+                } else {
+                    $key_name = strtoupper($key_name);
+                }
+            }
+            if ($index_name == $key_name) {
+                if (!$row['non_unique']) {
+                    return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                        $index_name . ' is not an existing table index', __FUNCTION__);
+                }
+                $column_name = $row['column_name'];
+                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);
+                    }
+                }
+                $definition['fields'][$column_name] = array(
+                    'position' => $colpos++
+                );
+                if (!empty($row['collation'])) {
+                    $definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
+                        ? 'ascending' : 'descending');
+                }
+            }
+        }
+        $result->free();
+        if (empty($definition['fields'])) {
+            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                $index_name . ' is not an existing table index', __FUNCTION__);
+        }
+        return $definition;
+    }
+
+    // }}}
+    // {{{ getTableConstraintDefinition()
+
+    /**
+     * Get the structure 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);
+        $constraint_name_original = $constraint_name;
+
+        $table = $db->quoteIdentifier($table, true);
+        $query = "SHOW INDEX FROM $table /*!50002 WHERE Key_name = %s */";
+        if (strtolower($constraint_name) != 'primary') {
+            $constraint_name_mdb2 = $db->getIndexName($constraint_name);
+            $result = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2)));
+            if (!PEAR::isError($result) && !is_null($result)) {
+                // apply 'idxname_format' only if the query succeeded, otherwise
+                // fallback to the given $index_name, without transformation
+                $constraint_name = $constraint_name_mdb2;
+            }
+        }
+        $result = $db->query(sprintf($query, $db->quote($constraint_name)));
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        $colpos = 1;
+        //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,
+        );
+        while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) {
+            $row = array_change_key_case($row, CASE_LOWER);
+            $key_name = $row['key_name'];
+            if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+                if ($db->options['field_case'] == CASE_LOWER) {
+                    $key_name = strtolower($key_name);
+                } else {
+                    $key_name = strtoupper($key_name);
+                }
+            }
+            if ($constraint_name == $key_name) {
+                if ($row['non_unique']) {
+                    //FOREIGN KEY?
+                    return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition);
+                }
+                if ($row['key_name'] == 'PRIMARY') {
+                    $definition['primary'] = true;
+                } elseif (!$row['non_unique']) {
+                    $definition['unique'] = true;
+                }
+                $column_name = $row['column_name'];
+                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);
+                    }
+                }
+                $definition['fields'][$column_name] = array(
+                    'position' => $colpos++
+                );
+                if (!empty($row['collation'])) {
+                    $definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A'
+                        ? 'ascending' : 'descending');
+                }
+            }
+        }
+        $result->free();
+        if (empty($definition['fields'])) {
+            return $this->_getTableFKConstraintDefinition($table, $constraint_name_original, $definition);
+        }
+        return $definition;
+    }
+
+    // }}}
+    // {{{ _getTableFKConstraintDefinition()
+    
+    /**
+     * Get the FK definition from the CREATE TABLE statement
+     *
+     * @param string $table           table name
+     * @param string $constraint_name constraint name
+     * @param array  $definition      default values for constraint definition
+     *
+     * @return array|PEAR_Error
+     * @access private
+     */
+    function _getTableFKConstraintDefinition($table, $constraint_name, $definition)
+    {
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        $query = 'SHOW CREATE TABLE '. $db->escape($table);
+        $constraint = $db->queryOne($query, 'text', 1);
+        if (!PEAR::isError($constraint) && !empty($constraint)) {
+            if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+                if ($db->options['field_case'] == CASE_LOWER) {
+                    $constraint = strtolower($constraint);
+                } else {
+                    $constraint = strtoupper($constraint);
+                }
+            }
+            $constraint_name_original = $constraint_name;
+            $constraint_name = $db->getIndexName($constraint_name);
+            $pattern = '/\bCONSTRAINT\s+'.$constraint_name.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^ ]+) \(([^\)]+)\)/i';
+            if (!preg_match($pattern, str_replace('`', '', $constraint), $matches)) {
+                //fallback to original constraint name
+                $pattern = '/\bCONSTRAINT\s+'.$constraint_name_original.'\s+FOREIGN KEY\s+\(([^\)]+)\) \bREFERENCES\b ([^ ]+) \(([^\)]+)\)/i';
+            }
+            if (preg_match($pattern, str_replace('`', '', $constraint), $matches)) {
+                $definition['foreign'] = true;
+                $column_names = explode(',', $matches[1]);
+                $referenced_cols = explode(',', $matches[3]);
+                $definition['references'] = array(
+                    'table'  => $matches[2],
+                    'fields' => array(),
+                );
+                $colpos = 1;
+                foreach ($column_names as $column_name) {
+                    $definition['fields'][trim($column_name)] = array(
+                        'position' => $colpos++
+                    );
+                }
+                $colpos = 1;
+                foreach ($referenced_cols as $column_name) {
+                    $definition['references']['fields'][trim($column_name)] = array(
+                        'position' => $colpos++
+                    );
+                }
+                $definition['onupdate'] = 'NO ACTION';
+                $definition['ondelete'] = 'NO ACTION';
+                $definition['match']    = 'SIMPLE';
+                return $definition;
+            }
+        }
+        return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                $constraint_name . ' is not an existing table constraint', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ 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 trigger_name,
+                         event_object_table AS table_name,
+                         action_statement AS trigger_body,
+                         action_timing AS trigger_type,
+                         event_manipulation AS trigger_event
+                    FROM information_schema.triggers
+                   WHERE trigger_name = '. $db->quote($trigger, 'text');
+        $types = array(
+            'trigger_name'    => 'text',
+            'table_name'      => 'text',
+            'trigger_body'    => 'text',
+            'trigger_type'    => 'text',
+            'trigger_event'   => 'text',
+        );
+        $def = $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($def)) {
+            return $def;
+        }
+        $def['trigger_comment'] = '';
+        $def['trigger_enabled'] = true;
+        return $def;
+    }
+
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * @param object|string  $result  MDB2_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @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::setOption()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+           return parent::tableInfo($result, $mode);
+        }
+
+        $db =$this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
+        if (!is_resource($resource)) {
+            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                'Could not generate result resource', __FUNCTION__);
+        }
+
+        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+            if ($db->options['field_case'] == CASE_LOWER) {
+                $case_func = 'strtolower';
+            } else {
+                $case_func = 'strtoupper';
+            }
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = @mysql_num_fields($resource);
+        $res   = array();
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        $db->loadModule('Datatype', null, true);
+        for ($i = 0; $i < $count; $i++) {
+            $res[$i] = array(
+                'table' => $case_func(@mysql_field_table($resource, $i)),
+                'name'  => $case_func(@mysql_field_name($resource, $i)),
+                'type'  => @mysql_field_type($resource, $i),
+                'length'   => @mysql_field_len($resource, $i),
+                'flags' => @mysql_field_flags($resource, $i),
+            );
+            if ($res[$i]['type'] == 'string') {
+                $res[$i]['type'] = 'char';
+            } elseif ($res[$i]['type'] == 'unknown') {
+                $res[$i]['type'] = 'decimal';
+            }
+            $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
+            if (PEAR::isError($mdb2type_info)) {
+               return $mdb2type_info;
+            }
+            $res[$i]['mdb2type'] = $mdb2type_info[0][0];
+            if ($mode & MDB2_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        return $res;
+    }
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Reverse/pgsql.php b/3rdparty/MDB2/Driver/Reverse/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..649c1cad9ee629655faf00667937beae6e3cc287
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Reverse/pgsql.php
@@ -0,0 +1,573 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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.                                          |
+// +----------------------------------------------------------------------+
+// | Authors: Paul Cooper <pgc@ucecom.com>                                |
+// |          Lorenzo Alberton <l.alberton@quipo.it>                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: pgsql.php,v 1.75 2008/08/22 16:36:20 quipo Exp $
+
+require_once('MDB2/Driver/Reverse/Common.php');
+
+/**
+ * MDB2 PostGreSQL driver for the schema reverse engineering module
+ *
+ * @package  MDB2
+ * @category Database
+ * @author   Paul Cooper <pgc@ucecom.com>
+ * @author   Lorenzo Alberton <l.alberton@quipo.it>
+ */
+class MDB2_Driver_Reverse_pgsql extends MDB2_Driver_Reverse_Common
+{
+    // {{{ getTableFieldDefinition()
+
+    /**
+     * Get the structure 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
+     * @access public
+     */
+    function getTableFieldDefinition($table_name, $field_name)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $result = $db->loadModule('Datatype', null, true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        list($schema, $table) = $this->splitTableSchema($table_name);
+
+        $query = "SELECT a.attname AS name,
+                         t.typname AS type,
+                         CASE a.attlen
+                           WHEN -1 THEN
+	                         CASE t.typname
+	                           WHEN 'numeric' THEN (a.atttypmod / 65536)
+	                           WHEN 'decimal' THEN (a.atttypmod / 65536)
+	                           WHEN 'money'   THEN (a.atttypmod / 65536)
+	                           ELSE CASE a.atttypmod
+                                 WHEN -1 THEN NULL
+	                             ELSE a.atttypmod - 4
+	                           END
+                             END
+	                       ELSE a.attlen
+                         END AS length,
+	                     CASE t.typname
+	                       WHEN 'numeric' THEN (a.atttypmod % 65536) - 4
+	                       WHEN 'decimal' THEN (a.atttypmod % 65536) - 4
+	                       WHEN 'money'   THEN (a.atttypmod % 65536) - 4
+	                       ELSE 0
+                         END AS scale,
+                         a.attnotnull,
+                         a.atttypmod,
+                         a.atthasdef,
+                         (SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
+                            FROM pg_attrdef d
+                           WHERE d.adrelid = a.attrelid
+                             AND d.adnum = a.attnum
+                             AND a.atthasdef
+                         ) as default
+                    FROM pg_attribute a,
+                         pg_class c,
+                         pg_type t
+                   WHERE c.relname = ".$db->quote($table, 'text')."
+                     AND a.atttypid = t.oid
+                     AND c.oid = a.attrelid
+                     AND NOT a.attisdropped
+                     AND a.attnum > 0
+                     AND a.attname = ".$db->quote($field_name, 'text')."
+                ORDER BY a.attnum";
+        $column = $db->queryRow($query, null, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($column)) {
+            return $column;
+        }
+
+        if (empty($column)) {
+            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'it was not specified an existing table column', __FUNCTION__);
+        }
+
+        $column = array_change_key_case($column, CASE_LOWER);
+        $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['attnotnull']) && $column['attnotnull'] == 't') {
+            $notnull = true;
+        }
+        $default = null;
+        if ($column['atthasdef'] === 't'
+            && !preg_match("/nextval\('([^']+)'/", $column['default'])
+        ) {
+            $pattern = '/^\'(.*)\'::[\w ]+$/i';
+            $default = $column['default'];#substr($column['adsrc'], 1, -1);
+            if (is_null($default) && $notnull) {
+                $default = '';
+            } elseif (!empty($default) && preg_match($pattern, $default)) {
+                //remove data type cast
+                $default = preg_replace ($pattern, '\\1', $default);
+            }
+        }
+        $autoincrement = false;
+        if (preg_match("/nextval\('([^']+)'/", $column['default'], $nextvals)) {
+            $autoincrement = true;
+        }
+        $definition[0] = array('notnull' => $notnull, 'nativetype' => $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;
+    }
+
+    // }}}
+    // {{{ getTableIndexDefinition()
+
+    /**
+     * Get the structure 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 relname, indkey FROM pg_index, pg_class';
+        $query.= ' WHERE pg_class.oid = pg_index.indexrelid';
+        $query.= " AND indisunique != 't' AND indisprimary != 't'";
+        $query.= ' AND pg_class.relname = %s';
+        $index_name_mdb2 = $db->getIndexName($index_name);
+        $row = $db->queryRow(sprintf($query, $db->quote($index_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($row) || empty($row)) {
+            // fallback to the given $index_name, without transformation
+            $row = $db->queryRow(sprintf($query, $db->quote($index_name, 'text')), null, MDB2_FETCHMODE_ASSOC);
+        }
+        if (PEAR::isError($row)) {
+            return $row;
+        }
+
+        if (empty($row)) {
+            return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'it was not specified an existing table index', __FUNCTION__);
+        }
+
+        $row = array_change_key_case($row, CASE_LOWER);
+
+        $db->loadModule('Manager', null, true);
+        $columns = $db->manager->listTableFields($table_name);
+
+        $definition = array();
+
+        $index_column_numbers = explode(' ', $row['indkey']);
+
+        $colpos = 1;
+        foreach ($index_column_numbers as $number) {
+            $definition['fields'][$columns[($number - 1)]] = array(
+                'position' => $colpos++,
+                'sorting' => 'ascending',
+            );
+        }
+        return $definition;
+    }
+
+    // }}}
+    // {{{ getTableConstraintDefinition()
+
+    /**
+     * Get the structure 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 c.oid,
+                         c.conname AS constraint_name,
+                         CASE WHEN c.contype = 'c' THEN 1 ELSE 0 END AS \"check\",
+                         CASE WHEN c.contype = 'f' THEN 1 ELSE 0 END AS \"foreign\",
+                         CASE WHEN c.contype = 'p' THEN 1 ELSE 0 END AS \"primary\",
+                         CASE WHEN c.contype = 'u' THEN 1 ELSE 0 END AS \"unique\",
+                         CASE WHEN c.condeferrable = 'f' THEN 0 ELSE 1 END AS deferrable,
+                         CASE WHEN c.condeferred = 'f' THEN 0 ELSE 1 END AS initiallydeferred,
+                         --array_to_string(c.conkey, ' ') AS constraint_key,
+                         t.relname AS table_name,
+                         t2.relname AS references_table,
+                         CASE confupdtype
+                           WHEN 'a' THEN 'NO ACTION'
+                           WHEN 'r' THEN 'RESTRICT'
+                           WHEN 'c' THEN 'CASCADE'
+                           WHEN 'n' THEN 'SET NULL'
+                           WHEN 'd' THEN 'SET DEFAULT'
+                         END AS onupdate,
+                         CASE confdeltype
+                           WHEN 'a' THEN 'NO ACTION'
+                           WHEN 'r' THEN 'RESTRICT'
+                           WHEN 'c' THEN 'CASCADE'
+                           WHEN 'n' THEN 'SET NULL'
+                           WHEN 'd' THEN 'SET DEFAULT'
+                         END AS ondelete,
+                         CASE confmatchtype
+                           WHEN 'u' THEN 'UNSPECIFIED'
+                           WHEN 'f' THEN 'FULL'
+                           WHEN 'p' THEN 'PARTIAL'
+                         END AS match,
+                         --array_to_string(c.confkey, ' ') AS fk_constraint_key,
+                         consrc
+                    FROM pg_constraint c
+               LEFT JOIN pg_class t  ON c.conrelid  = t.oid
+               LEFT JOIN pg_class t2 ON c.confrelid = t2.oid
+                   WHERE c.conname = %s
+                     AND t.relname = " . $db->quote($table, 'text');
+        $constraint_name_mdb2 = $db->getIndexName($constraint_name);
+        $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
+        if (PEAR::isError($row) || empty($row)) {
+            // fallback to the given $index_name, without transformation
+            $constraint_name_mdb2 = $constraint_name;
+            $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
+        }
+        if (PEAR::isError($row)) {
+            return $row;
+        }
+        $uniqueIndex = false;
+        if (empty($row)) {
+            // We might be looking for a UNIQUE index that was not created
+            // as a constraint but should be treated as such.
+            $query = 'SELECT relname AS constraint_name,
+                             indkey,
+                             0 AS "check",
+                             0 AS "foreign",
+                             0 AS "primary",
+                             1 AS "unique",
+                             0 AS deferrable,
+                             0 AS initiallydeferred,
+                             NULL AS references_table,
+                             NULL AS onupdate,
+                             NULL AS ondelete,
+                             NULL AS match
+                        FROM pg_index, pg_class
+                       WHERE pg_class.oid = pg_index.indexrelid
+                         AND indisunique = \'t\'
+                         AND pg_class.relname = %s';
+            $constraint_name_mdb2 = $db->getIndexName($constraint_name);
+            $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
+            if (PEAR::isError($row) || empty($row)) {
+                // fallback to the given $index_name, without transformation
+                $constraint_name_mdb2 = $constraint_name;
+                $row = $db->queryRow(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null, MDB2_FETCHMODE_ASSOC);
+            }
+            if (PEAR::isError($row)) {
+                return $row;
+            }
+            if (empty($row)) {
+                return $db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                    $constraint_name . ' is not an existing table constraint', __FUNCTION__);
+            }
+            $uniqueIndex = true;
+        }
+
+        $row = array_change_key_case($row, CASE_LOWER);
+
+        $definition = array(
+            'primary' => (boolean)$row['primary'],
+            'unique'  => (boolean)$row['unique'],
+            'foreign' => (boolean)$row['foreign'],
+            'check'   => (boolean)$row['check'],
+            'fields'  => array(),
+            'references' => array(
+                'table'  => $row['references_table'],
+                'fields' => array(),
+            ),
+            'deferrable' => (boolean)$row['deferrable'],
+            'initiallydeferred' => (boolean)$row['initiallydeferred'],
+            'onupdate' => $row['onupdate'],
+            'ondelete' => $row['ondelete'],
+            'match'    => $row['match'],
+        );
+
+        if ($uniqueIndex) {
+            $db->loadModule('Manager', null, true);
+            $columns = $db->manager->listTableFields($table_name);
+            $index_column_numbers = explode(' ', $row['indkey']);
+            $colpos = 1;
+            foreach ($index_column_numbers as $number) {
+                $definition['fields'][$columns[($number - 1)]] = array(
+                    'position' => $colpos++,
+                    'sorting'  => 'ascending',
+                );
+            }
+            return $definition;
+        }
+
+        $query = 'SELECT a.attname
+                    FROM pg_constraint c
+               LEFT JOIN pg_class t  ON c.conrelid  = t.oid
+               LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.conkey)
+                   WHERE c.conname = %s
+                     AND t.relname = ' . $db->quote($table, 'text');
+        $fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
+        if (PEAR::isError($fields)) {
+            return $fields;
+        }
+        $colpos = 1;
+        foreach ($fields as $field) {
+            $definition['fields'][$field] = array(
+                'position' => $colpos++,
+                'sorting' => 'ascending',
+            );
+        }
+        
+        if ($definition['foreign']) {
+            $query = 'SELECT a.attname
+                        FROM pg_constraint c
+                   LEFT JOIN pg_class t  ON c.confrelid  = t.oid
+                   LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(c.confkey)
+                       WHERE c.conname = %s
+                         AND t.relname = ' . $db->quote($definition['references']['table'], 'text');
+            $foreign_fields = $db->queryCol(sprintf($query, $db->quote($constraint_name_mdb2, 'text')), null);
+            if (PEAR::isError($foreign_fields)) {
+                return $foreign_fields;
+            }
+            $colpos = 1;
+            foreach ($foreign_fields as $foreign_field) {
+                $definition['references']['fields'][$foreign_field] = array(
+                    'position' => $colpos++,
+                );
+            }
+        }
+        
+        if ($definition['check']) {
+            $check_def = $db->queryOne("SELECT pg_get_constraintdef(" . $row['oid'] . ", 't')");
+            // ...
+        }
+        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
+     *
+     * @TODO: add support for plsql functions and functions with args
+     */
+    function getTriggerDefinition($trigger)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $query = "SELECT trg.tgname AS trigger_name,
+                         tbl.relname AS table_name,
+                         CASE
+                            WHEN p.proname IS NOT NULL THEN 'EXECUTE PROCEDURE ' || p.proname || '();'
+                            ELSE ''
+                         END AS trigger_body,
+                         CASE trg.tgtype & cast(2 as int2)
+                            WHEN 0 THEN 'AFTER'
+                            ELSE 'BEFORE'
+                         END AS trigger_type,
+                         CASE trg.tgtype & cast(28 as int2)
+                            WHEN 16 THEN 'UPDATE'
+                            WHEN 8 THEN 'DELETE'
+                            WHEN 4 THEN 'INSERT'
+                            WHEN 20 THEN 'INSERT, UPDATE'
+                            WHEN 28 THEN 'INSERT, UPDATE, DELETE'
+                            WHEN 24 THEN 'UPDATE, DELETE'
+                            WHEN 12 THEN 'INSERT, DELETE'
+                         END AS trigger_event,
+                         CASE trg.tgenabled
+                            WHEN 'O' THEN 't'
+                            ELSE trg.tgenabled
+                         END AS trigger_enabled,
+                         obj_description(trg.oid, 'pg_trigger') AS trigger_comment
+                    FROM pg_trigger trg,
+                         pg_class tbl,
+                         pg_proc p
+                   WHERE trg.tgrelid = tbl.oid
+                     AND trg.tgfoid = p.oid
+                     AND trg.tgname = ". $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',
+        );
+        return $db->queryRow($query, $types, MDB2_FETCHMODE_ASSOC);
+    }
+    
+    // }}}
+    // {{{ tableInfo()
+
+    /**
+     * Returns information about a table or a result set
+     *
+     * NOTE: only supports 'table' and 'flags' if <var>$result</var>
+     * is a table name.
+     *
+     * @param object|string  $result  MDB2_result object from a query or a
+     *                                 string containing the name of a table.
+     *                                 While this also accepts a query result
+     *                                 resource identifier, this behavior is
+     *                                 deprecated.
+     * @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()
+     */
+    function tableInfo($result, $mode = null)
+    {
+        if (is_string($result)) {
+           return parent::tableInfo($result, $mode);
+        }
+
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $resource = MDB2::isResultCommon($result) ? $result->getResource() : $result;
+        if (!is_resource($resource)) {
+            return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                'Could not generate result resource', __FUNCTION__);
+        }
+
+        if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+            if ($db->options['field_case'] == CASE_LOWER) {
+                $case_func = 'strtolower';
+            } else {
+                $case_func = 'strtoupper';
+            }
+        } else {
+            $case_func = 'strval';
+        }
+
+        $count = @pg_num_fields($resource);
+        $res   = array();
+
+        if ($mode) {
+            $res['num_fields'] = $count;
+        }
+
+        $db->loadModule('Datatype', null, true);
+        for ($i = 0; $i < $count; $i++) {
+            $res[$i] = array(
+                'table' => function_exists('pg_field_table') ? @pg_field_table($resource, $i) : '',
+                'name'  => $case_func(@pg_field_name($resource, $i)),
+                'type'  => @pg_field_type($resource, $i),
+                'length' => @pg_field_size($resource, $i),
+                'flags' => '',
+            );
+            $mdb2type_info = $db->datatype->mapNativeDatatype($res[$i]);
+            if (PEAR::isError($mdb2type_info)) {
+               return $mdb2type_info;
+            }
+            $res[$i]['mdb2type'] = $mdb2type_info[0][0];
+            if ($mode & MDB2_TABLEINFO_ORDER) {
+                $res['order'][$res[$i]['name']] = $i;
+            }
+            if ($mode & MDB2_TABLEINFO_ORDERTABLE) {
+                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
+            }
+        }
+
+        return $res;
+    }
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/Reverse/sqlite.php b/3rdparty/MDB2/Driver/Reverse/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..60c686c418a1d54339897b08e101ea9f2e122889
--- /dev/null
+++ b/3rdparty/MDB2/Driver/Reverse/sqlite.php
@@ -0,0 +1,609 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith, Lorenzo Alberton                       |
+// | 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.                                          |
+// +----------------------------------------------------------------------+
+// | Authors: Lukas Smith <smith@pooteeweet.org>                          |
+// |          Lorenzo Alberton <l.alberton@quipo.it>                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.80 2008/05/03 10:30:14 quipo Exp $
+//
+
+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_sqlite 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/3rdparty/MDB2/Driver/mysql.php b/3rdparty/MDB2/Driver/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..b9b46c0d762f445a384320a9fd6bc9b3870fc40d
--- /dev/null
+++ b/3rdparty/MDB2/Driver/mysql.php
@@ -0,0 +1,1700 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.214 2008/11/16 21:45:08 quipo Exp $
+//
+
+/**
+ * MDB2 MySQL driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_mysql extends MDB2_Driver_Common
+{
+    // {{{ properties
+
+    var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => '\\', 'escape_pattern' => '\\');
+
+    var $identifier_quoting = array('start' => '`', 'end' => '`', 'escape' => '`');
+
+    var $sql_comments = array(
+        array('start' => '-- ', 'end' => "\n", 'escape' => false),
+        array('start' => '#', 'end' => "\n", 'escape' => false),
+        array('start' => '/*', 'end' => '*/', 'escape' => false),
+    );
+
+    var $server_capabilities_checked = false;
+
+    var $start_transaction = false;
+
+    var $varchar_max_length = 255;
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * Constructor
+     */
+    function __construct()
+    {
+        parent::__construct();
+
+        $this->phptype = 'mysql';
+        $this->dbsyntax = 'mysql';
+
+        $this->supported['sequences'] = 'emulated';
+        $this->supported['indexes'] = true;
+        $this->supported['affected_rows'] = true;
+        $this->supported['transactions'] = false;
+        $this->supported['savepoints'] = false;
+        $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['sub_selects'] = 'emulated';
+        $this->supported['triggers'] = false;
+        $this->supported['auto_increment'] = true;
+        $this->supported['primary_key'] = true;
+        $this->supported['result_introspection'] = true;
+        $this->supported['prepared_statements'] = 'emulated';
+        $this->supported['identifier_quoting'] = true;
+        $this->supported['pattern_escaping'] = true;
+        $this->supported['new_link'] = true;
+
+        $this->options['DBA_username'] = false;
+        $this->options['DBA_password'] = false;
+        $this->options['default_table_type'] = '';
+        $this->options['max_identifiers_length'] = 64;
+
+        $this->_reCheckSupportedOptions();
+    }
+
+    // }}}
+    // {{{ _reCheckSupportedOptions()
+    
+    /**
+     * If the user changes certain options, other capabilities may depend
+     * on the new settings, so we need to check them (again).
+     *
+     * @access private
+     */
+    function _reCheckSupportedOptions()
+    {
+        $this->supported['transactions'] = $this->options['use_transactions'];
+        $this->supported['savepoints']   = $this->options['use_transactions'];
+        if ($this->options['default_table_type']) {
+            switch (strtoupper($this->options['default_table_type'])) {
+            case 'BLACKHOLE':
+            case 'MEMORY':
+            case 'ARCHIVE':
+            case 'CSV':
+            case 'HEAP':
+            case 'ISAM':
+            case 'MERGE':
+            case 'MRG_ISAM':
+            case 'ISAM':
+            case 'MRG_MYISAM':
+            case 'MYISAM':
+                $this->supported['savepoints']   = false;
+                $this->supported['transactions'] = false;
+                $this->warnings[] = $this->options['default_table_type'] .
+                    ' is not a supported default table type';
+                break;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ function setOption($option, $value)
+
+    /**
+     * set the option for the db class
+     *
+     * @param   string  option name
+     * @param   mixed   value for the option
+     *
+     * @return  mixed   MDB2_OK or MDB2 Error Object
+     *
+     * @access  public
+     */
+    function setOption($option, $value)
+    {
+        $res = parent::setOption($option, $value);
+        $this->_reCheckSupportedOptions();
+    }
+
+    // }}}
+    // {{{ errorInfo()
+
+    /**
+     * This method is used to collect information about an error
+     *
+     * @param integer $error
+     * @return array
+     * @access public
+     */
+    function errorInfo($error = null)
+    {
+        if ($this->connection) {
+            $native_code = @mysql_errno($this->connection);
+            $native_msg  = @mysql_error($this->connection);
+        } else {
+            $native_code = @mysql_errno();
+            $native_msg  = @mysql_error();
+        }
+        if (is_null($error)) {
+            static $ecode_map;
+            if (empty($ecode_map)) {
+                $ecode_map = array(
+                    1000 => MDB2_ERROR_INVALID, //hashchk
+                    1001 => MDB2_ERROR_INVALID, //isamchk
+                    1004 => MDB2_ERROR_CANNOT_CREATE,
+                    1005 => MDB2_ERROR_CANNOT_CREATE,
+                    1006 => MDB2_ERROR_CANNOT_CREATE,
+                    1007 => MDB2_ERROR_ALREADY_EXISTS,
+                    1008 => MDB2_ERROR_CANNOT_DROP,
+                    1009 => MDB2_ERROR_CANNOT_DROP,
+                    1010 => MDB2_ERROR_CANNOT_DROP,
+                    1011 => MDB2_ERROR_CANNOT_DELETE,
+                    1022 => MDB2_ERROR_ALREADY_EXISTS,
+                    1029 => MDB2_ERROR_NOT_FOUND,
+                    1032 => MDB2_ERROR_NOT_FOUND,
+                    1044 => MDB2_ERROR_ACCESS_VIOLATION,
+                    1045 => MDB2_ERROR_ACCESS_VIOLATION,
+                    1046 => MDB2_ERROR_NODBSELECTED,
+                    1048 => MDB2_ERROR_CONSTRAINT,
+                    1049 => MDB2_ERROR_NOSUCHDB,
+                    1050 => MDB2_ERROR_ALREADY_EXISTS,
+                    1051 => MDB2_ERROR_NOSUCHTABLE,
+                    1054 => MDB2_ERROR_NOSUCHFIELD,
+                    1060 => MDB2_ERROR_ALREADY_EXISTS,
+                    1061 => MDB2_ERROR_ALREADY_EXISTS,
+                    1062 => MDB2_ERROR_ALREADY_EXISTS,
+                    1064 => MDB2_ERROR_SYNTAX,
+                    1067 => MDB2_ERROR_INVALID,
+                    1072 => MDB2_ERROR_NOT_FOUND,
+                    1086 => MDB2_ERROR_ALREADY_EXISTS,
+                    1091 => MDB2_ERROR_NOT_FOUND,
+                    1100 => MDB2_ERROR_NOT_LOCKED,
+                    1109 => MDB2_ERROR_NOT_FOUND,
+                    1125 => MDB2_ERROR_ALREADY_EXISTS,
+                    1136 => MDB2_ERROR_VALUE_COUNT_ON_ROW,
+                    1138 => MDB2_ERROR_INVALID,
+                    1142 => MDB2_ERROR_ACCESS_VIOLATION,
+                    1143 => MDB2_ERROR_ACCESS_VIOLATION,
+                    1146 => MDB2_ERROR_NOSUCHTABLE,
+                    1149 => MDB2_ERROR_SYNTAX,
+                    1169 => MDB2_ERROR_CONSTRAINT,
+                    1176 => MDB2_ERROR_NOT_FOUND,
+                    1177 => MDB2_ERROR_NOSUCHTABLE,
+                    1213 => MDB2_ERROR_DEADLOCK,
+                    1216 => MDB2_ERROR_CONSTRAINT,
+                    1217 => MDB2_ERROR_CONSTRAINT,
+                    1227 => MDB2_ERROR_ACCESS_VIOLATION,
+                    1235 => MDB2_ERROR_CANNOT_CREATE,
+                    1299 => MDB2_ERROR_INVALID_DATE,
+                    1300 => MDB2_ERROR_INVALID,
+                    1304 => MDB2_ERROR_ALREADY_EXISTS,
+                    1305 => MDB2_ERROR_NOT_FOUND,
+                    1306 => MDB2_ERROR_CANNOT_DROP,
+                    1307 => MDB2_ERROR_CANNOT_CREATE,
+                    1334 => MDB2_ERROR_CANNOT_ALTER,
+                    1339 => MDB2_ERROR_NOT_FOUND,
+                    1356 => MDB2_ERROR_INVALID,
+                    1359 => MDB2_ERROR_ALREADY_EXISTS,
+                    1360 => MDB2_ERROR_NOT_FOUND,
+                    1363 => MDB2_ERROR_NOT_FOUND,
+                    1365 => MDB2_ERROR_DIVZERO,
+                    1451 => MDB2_ERROR_CONSTRAINT,
+                    1452 => MDB2_ERROR_CONSTRAINT,
+                    1542 => MDB2_ERROR_CANNOT_DROP,
+                    1546 => MDB2_ERROR_CONSTRAINT,
+                    1582 => MDB2_ERROR_CONSTRAINT,
+                    2003 => MDB2_ERROR_CONNECT_FAILED,
+                    2019 => MDB2_ERROR_INVALID,
+                );
+            }
+            if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS) {
+                $ecode_map[1022] = MDB2_ERROR_CONSTRAINT;
+                $ecode_map[1048] = MDB2_ERROR_CONSTRAINT_NOT_NULL;
+                $ecode_map[1062] = MDB2_ERROR_CONSTRAINT;
+            } else {
+                // Doing this in case mode changes during runtime.
+                $ecode_map[1022] = MDB2_ERROR_ALREADY_EXISTS;
+                $ecode_map[1048] = MDB2_ERROR_CONSTRAINT;
+                $ecode_map[1062] = MDB2_ERROR_ALREADY_EXISTS;
+            }
+            if (isset($ecode_map[$native_code])) {
+                $error = $ecode_map[$native_code];
+            }
+        }
+        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
+     */
+    function escape($text, $escape_wildcards = false)
+    {
+        if ($escape_wildcards) {
+            $text = $this->escapePattern($text);
+        }
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+        $text = @mysql_real_escape_string($text, $connection);
+        return $text;
+    }
+
+    // }}}
+    // {{{ 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));
+        $this->_getServerCapabilities();
+        if (!is_null($savepoint)) {
+            if (!$this->supports('savepoints')) {
+                return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                    'savepoints are not supported', __FUNCTION__);
+            }
+            if (!$this->in_transaction) {
+                return $this->raiseError(MDB2_ERROR_INVALID, null, null,
+                    'savepoint cannot be released when changes are auto committed', __FUNCTION__);
+            }
+            $query = 'SAVEPOINT '.$savepoint;
+            return $this->_doQuery($query, true);
+        } 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 = $this->start_transaction ? 'START TRANSACTION' : 'SET AUTOCOMMIT = 0';
+        $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)) {
+            if (!$this->supports('savepoints')) {
+                return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                    'savepoints are not supported', __FUNCTION__);
+            }
+            $server_info = $this->getServerVersion();
+            if (version_compare($server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'], '5.0.3', '<')) {
+                return MDB2_OK;
+            }
+            $query = 'RELEASE SAVEPOINT '.$savepoint;
+            return $this->_doQuery($query, true);
+        }
+
+        if (!$this->supports('transactions')) {
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'transactions are not supported', __FUNCTION__);
+        }
+
+        $result =& $this->_doQuery('COMMIT', true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        if (!$this->start_transaction) {
+            $query = 'SET AUTOCOMMIT = 1';
+            $result =& $this->_doQuery($query, true);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+        $this->in_transaction = false;
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * 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)) {
+            if (!$this->supports('savepoints')) {
+                return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                    'savepoints are not supported', __FUNCTION__);
+            }
+            $query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
+            return $this->_doQuery($query, true);
+        }
+
+        $query = 'ROLLBACK';
+        $result =& $this->_doQuery($query, true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        if (!$this->start_transaction) {
+            $query = 'SET AUTOCOMMIT = 1';
+            $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));
+        if (!$this->supports('transactions')) {
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'transactions are not supported', __FUNCTION__);
+        }
+        switch ($isolation) {
+        case 'READ UNCOMMITTED':
+        case 'READ COMMITTED':
+        case 'REPEATABLE READ':
+        case 'SERIALIZABLE':
+            break;
+        default:
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'isolation level is not supported: '.$isolation, __FUNCTION__);
+        }
+
+        $query = "SET SESSION TRANSACTION ISOLATION LEVEL $isolation";
+        return $this->_doQuery($query, true);
+    }
+
+    // }}}
+    // {{{ _doConnect()
+
+    /**
+     * do the grunt work of the connect
+     *
+     * @return connection on success or MDB2 Error Object on failure
+     * @access protected
+     */
+    function _doConnect($username, $password, $persistent = false)
+    {
+        if (!PEAR::loadExtension($this->phptype)) {
+            return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                'extension '.$this->phptype.' is not compiled into PHP', __FUNCTION__);
+        }
+
+        $params = array();
+        if ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix') {
+            $params[0] = ':' . $this->dsn['socket'];
+        } else {
+            $params[0] = $this->dsn['hostspec'] ? $this->dsn['hostspec']
+                         : 'localhost';
+            if ($this->dsn['port']) {
+                $params[0].= ':' . $this->dsn['port'];
+            }
+        }
+        $params[] = $username ? $username : null;
+        $params[] = $password ? $password : null;
+        if (!$persistent) {
+            if ($this->_isNewLinkSet()) {
+                $params[] = true;
+            } else {
+                $params[] = false;
+            }
+        }
+        if (version_compare(phpversion(), '4.3.0', '>=')) {
+            $params[] = isset($this->dsn['client_flags'])
+                ? $this->dsn['client_flags'] : null;
+        }
+        $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
+
+        $connection = @call_user_func_array($connect_function, $params);
+        if (!$connection) {
+            if (($err = @mysql_error()) != '') {
+                return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
+                    $err, __FUNCTION__);
+            } else {
+                return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
+                    'unable to establish a connection', __FUNCTION__);
+            }
+        }
+
+        if (!empty($this->dsn['charset'])) {
+            $result = $this->setCharset($this->dsn['charset'], $connection);
+            if (PEAR::isError($result)) {
+                $this->disconnect(false);
+                return $result;
+            }
+        }
+
+        return $connection;
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database
+     *
+     * @return MDB2_OK on success, MDB2 Error Object on failure
+     * @access public
+     */
+    function connect()
+    {
+        if (is_resource($this->connection)) {
+            //if (count(array_diff($this->connected_dsn, $this->dsn)) == 0
+            if (MDB2::areEquals($this->connected_dsn, $this->dsn)
+                && $this->opened_persistent == $this->options['persistent']
+            ) {
+                return MDB2_OK;
+            }
+            $this->disconnect(false);
+        }
+
+        $connection = $this->_doConnect(
+            $this->dsn['username'],
+            $this->dsn['password'],
+            $this->options['persistent']
+        );
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $this->connection = $connection;
+        $this->connected_dsn = $this->dsn;
+        $this->connected_database_name = '';
+        $this->opened_persistent = $this->options['persistent'];
+        $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
+
+        if ($this->database_name) {
+            if ($this->database_name != $this->connected_database_name) {
+                if (!@mysql_select_db($this->database_name, $connection)) {
+                    $err = $this->raiseError(null, null, null,
+                        'Could not select the database: '.$this->database_name, __FUNCTION__);
+                    return $err;
+                }
+                $this->connected_database_name = $this->database_name;
+            }
+        }
+
+        $this->_getServerCapabilities();
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ setCharset()
+
+    /**
+     * Set the charset on the current connection
+     *
+     * @param string    charset (or array(charset, collation))
+     * @param resource  connection handle
+     *
+     * @return true on success, MDB2 Error Object on failure
+     */
+    function setCharset($charset, $connection = null)
+    {
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+        $collation = null;
+        if (is_array($charset) && 2 == count($charset)) {
+            $collation = array_pop($charset);
+            $charset   = array_pop($charset);
+        }
+        $client_info = mysql_get_client_info();
+        if (function_exists('mysql_set_charset') && version_compare($client_info, '5.0.6')) {
+            if (!$result = mysql_set_charset($charset, $connection)) {
+                $err =& $this->raiseError(null, null, null,
+                    'Could not set client character set', __FUNCTION__);
+                return $err;
+            }
+            return $result;
+        }
+        $query = "SET NAMES '".mysql_real_escape_string($charset, $connection)."'";
+        if (!is_null($collation)) {
+            $query .= " COLLATE '".mysqli_real_escape_string($connection, $collation)."'";
+        }
+        return $this->_doQuery($query, true, $connection);
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        $connection = $this->_doConnect($this->dsn['username'],
+                                        $this->dsn['password'],
+                                        $this->options['persistent']);
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $result = @mysql_select_db($name, $connection);
+        @mysql_close($connection);
+
+        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 (is_resource($this->connection)) {
+            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) {
+                $ok = @mysql_close($this->connection);
+                if (!$ok) {
+                    return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED,
+                           null, null, null, __FUNCTION__);
+                }
+            }
+        } else {
+            return false;
+        }
+        return parent::disconnect($force);
+    }
+
+    // }}}
+    // {{{ standaloneQuery()
+
+   /**
+     * execute a query as DBA
+     *
+     * @param string $query the SQL query
+     * @param mixed   $types  array that contains the types of the columns in
+     *                        the result set
+     * @param boolean $is_manip  if the query is a manipulation query
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function &standaloneQuery($query, $types = null, $is_manip = false)
+    {
+        $user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
+        $pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
+        $connection = $this->_doConnect($user, $pass, $this->options['persistent']);
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
+        
+        $result =& $this->_doQuery($query, $is_manip, $connection, $this->database_name);
+        if (!PEAR::isError($result)) {
+            $result = $this->_affectedRows($connection, $result);
+        }
+
+        @mysql_close($connection);
+        return $result;
+    }
+
+    // }}}
+    // {{{ _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;
+        }
+
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+        if (is_null($database_name)) {
+            $database_name = $this->database_name;
+        }
+
+        if ($database_name) {
+            if ($database_name != $this->connected_database_name) {
+                if (!@mysql_select_db($database_name, $connection)) {
+                    $err = $this->raiseError(null, null, null,
+                        'Could not select the database: '.$database_name, __FUNCTION__);
+                    return $err;
+                }
+                $this->connected_database_name = $database_name;
+            }
+        }
+
+        $function = $this->options['result_buffering']
+            ? 'mysql_query' : 'mysql_unbuffered_query';
+        $result = @$function($query, $connection);
+        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)
+    {
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+        return @mysql_affected_rows($connection);
+    }
+
+    // }}}
+    // {{{ _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) {
+            // "DELETE FROM table" gives 0 affected rows in MySQL.
+            // This little hack lets you know how many rows were deleted.
+            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);
+            }
+
+            // LIMIT doesn't always come last in the query
+            // @see http://dev.mysql.com/doc/refman/5.0/en/select.html
+            $after = '';
+            if (preg_match('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', $query, $matches)) {
+                $after = $matches[0];
+                $query = preg_replace('/(\s+INTO\s+(?:OUT|DUMP)FILE\s.*)$/ims', '', $query);
+            } elseif (preg_match('/(\s+FOR\s+UPDATE\s*)$/i', $query, $matches)) {
+               $after = $matches[0];
+               $query = preg_replace('/(\s+FOR\s+UPDATE\s*)$/im', '', $query);
+            } elseif (preg_match('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', $query, $matches)) {
+               $after = $matches[0];
+               $query = preg_replace('/(\s+LOCK\s+IN\s+SHARE\s+MODE\s*)$/im', '', $query);
+            }
+
+            if ($is_manip) {
+                return $query . " LIMIT $limit" . $after;
+            } else {
+                return $query . " LIMIT $offset, $limit" . $after;
+            }
+        }
+        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)
+    {
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+        if ($this->connected_server_info) {
+            $server_info = $this->connected_server_info;
+        } else {
+            $server_info = @mysql_get_server_info($connection);
+        }
+        if (!$server_info) {
+            return $this->raiseError(null, null, null,
+                'Could not get server information', __FUNCTION__);
+        }
+        // cache server_info
+        $this->connected_server_info = $server_info;
+        if (!$native) {
+            $tmp = explode('.', $server_info, 3);
+            if (isset($tmp[2]) && strpos($tmp[2], '-')) {
+                $tmp2 = explode('-', @$tmp[2], 2);
+            } else {
+                $tmp2[0] = isset($tmp[2]) ? $tmp[2] : null;
+                $tmp2[1] = null;
+            }
+            $server_info = array(
+                'major' => isset($tmp[0]) ? $tmp[0] : null,
+                'minor' => isset($tmp[1]) ? $tmp[1] : null,
+                'patch' => $tmp2[0],
+                'extra' => $tmp2[1],
+                'native' => $server_info,
+            );
+        }
+        return $server_info;
+    }
+
+    // }}}
+    // {{{ _getServerCapabilities()
+
+    /**
+     * Fetch some information about the server capabilities
+     * (transactions, subselects, prepared statements, etc).
+     *
+     * @access private
+     */
+    function _getServerCapabilities()
+    {
+        if (!$this->server_capabilities_checked) {
+            $this->server_capabilities_checked = true;
+
+            //set defaults
+            $this->supported['sub_selects'] = 'emulated';
+            $this->supported['prepared_statements'] = 'emulated';
+            $this->supported['triggers'] = false;
+            $this->start_transaction = false;
+            $this->varchar_max_length = 255;
+            
+            $server_info = $this->getServerVersion();
+            if (is_array($server_info)) {
+                $server_version = $server_info['major'].'.'.$server_info['minor'].'.'.$server_info['patch'];
+
+                if (!version_compare($server_version, '4.1.0', '<')) {
+                    $this->supported['sub_selects'] = true;
+                    $this->supported['prepared_statements'] = true;
+                }
+
+                // SAVEPOINTs were introduced in MySQL 4.0.14 and 4.1.1 (InnoDB)
+                if (version_compare($server_version, '4.1.0', '>=')) {
+                    if (version_compare($server_version, '4.1.1', '<')) {
+                        $this->supported['savepoints'] = false;
+                    }
+                } elseif (version_compare($server_version, '4.0.14', '<')) {
+                    $this->supported['savepoints'] = false;
+                }
+
+                if (!version_compare($server_version, '4.0.11', '<')) {
+                    $this->start_transaction = true;
+                }
+
+                if (!version_compare($server_version, '5.0.3', '<')) {
+                    $this->varchar_max_length = 65532;
+                }
+
+                if (!version_compare($server_version, '5.0.2', '<')) {
+                    $this->supported['triggers'] = true;
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ function _skipUserDefinedVariable($query, $position)
+
+    /**
+     * Utility method, used by prepare() to avoid misinterpreting MySQL user 
+     * defined variables (SELECT @x:=5) for placeholders.
+     * Check if the placeholder is a false positive, i.e. if it is an user defined
+     * variable instead. If so, skip it and advance the position, otherwise
+     * return the current position, which is valid
+     *
+     * @param string $query
+     * @param integer $position current string cursor position
+     * @return integer $new_position
+     * @access protected
+     */
+    function _skipUserDefinedVariable($query, $position)
+    {
+        $found = strpos(strrev(substr($query, 0, $position)), '@');
+        if ($found === false) {
+            return $position;
+        }
+        $pos = strlen($query) - strlen(substr($query, $position)) - $found - 1;
+        $substring = substr($query, $pos, $position - $pos + 2);
+        if (preg_match('/^@\w+\s*:=$/', $substring)) {
+            return $position + 1; //found an user defined variable: skip it
+        }
+        return $position;
+    }
+
+    // }}}
+    // {{{ prepare()
+
+    /**
+     * 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;
+        }
+        $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
+            }
+            
+            //make sure this is not part of an user defined variable
+            $new_pos = $this->_skipUserDefinedVariable($query, $position);
+            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;
+        }
+        static $prep_statement_counter = 1;
+        $statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter++ . sha1(microtime() + mt_rand()));
+        $statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']);
+        $query = "PREPARE $statement_name FROM ".$this->quote($query, 'text');
+        $statement =& $this->_doQuery($query, true, $connection);
+        if (PEAR::isError($statement)) {
+            return $statement;
+        }
+
+        $class_name = 'MDB2_Statement_'.$this->phptype;
+        $obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
+        $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
+        return $obj;
+    }
+
+    // }}}
+    // {{{ 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 MySQL 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
+     *
+     * @see http://dev.mysql.com/doc/refman/5.0/en/replace.html
+     * @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->quoteIdentifier($this->options['seqcol_name'], true);
+        $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)
+    {
+        // not using mysql_insert_id() due to http://pear.php.net/bugs/bug.php?id=8051
+        return $this->queryOne('SELECT LAST_INSERT_ID()', 'integer');
+    }
+
+    // }}}
+    // {{{ 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');
+    }
+}
+
+/**
+ * MDB2 MySQL result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Result_mysql 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 = @mysql_fetch_assoc($this->result);
+            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 = @mysql_fetch_row($this->result);
+        }
+
+        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 = @mysql_field_name($this->result, $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.
+     *
+     * @return mixed integer value with the number of columns, a MDB2 error
+     *                       on failure
+     * @access public
+     */
+    function numCols()
+    {
+        $cols = @mysql_num_fields($this->result);
+        if (is_null($cols)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return count($this->types);
+            }
+            return $this->db->raiseError(null, null, null,
+                'Could not get column count', __FUNCTION__);
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ free()
+
+    /**
+     * Free the internal resources associated with result.
+     *
+     * @return boolean true on success, false if result is invalid
+     * @access public
+     */
+    function free()
+    {
+        if (is_resource($this->result) && $this->db->connection) {
+            $free = @mysql_free_result($this->result);
+            if ($free === false) {
+                return $this->db->raiseError(null, null, null,
+                    'Could not free result', __FUNCTION__);
+            }
+        }
+        $this->result = false;
+        return MDB2_OK;
+    }
+}
+
+/**
+ * MDB2 MySQL buffered result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_BufferedResult_mysql extends MDB2_Result_mysql
+{
+    // }}}
+    // {{{ 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)
+    {
+        if ($this->rownum != ($rownum - 1) && !@mysql_data_seek($this->result, $rownum)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return MDB2_OK;
+            }
+            return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
+                'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
+        }
+        $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 = @mysql_num_rows($this->result);
+        if (false === $rows) {
+            if (false === $this->result) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return 0;
+            }
+            return $this->db->raiseError(null, null, null,
+                'Could not get row count', __FUNCTION__);
+        }
+        return $rows;
+    }
+}
+
+/**
+ * MDB2 MySQL statement driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Statement_mysql extends MDB2_Statement_Common
+{
+    // {{{ _execute()
+
+    /**
+     * 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;
+        }
+
+        $query = 'EXECUTE '.$this->statement;
+        if (!empty($this->positions)) {
+            $parameters = array();
+            foreach ($this->positions as $parameter) {
+                if (!array_key_exists($parameter, $this->values)) {
+                    return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                        'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
+                }
+                $value = $this->values[$parameter];
+                $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
+                if (is_resource($value) || $type == 'clob' || $type == 'blob' && $this->db->options['lob_allow_url_include']) {
+                    if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
+                        if ($match[1] == 'file://') {
+                            $value = $match[2];
+                        }
+                        $value = @fopen($value, 'r');
+                        $close = true;
+                    }
+                    if (is_resource($value)) {
+                        $data = '';
+                        while (!@feof($value)) {
+                            $data.= @fread($value, $this->db->options['lob_buffer_length']);
+                        }
+                        if ($close) {
+                            @fclose($value);
+                        }
+                        $value = $data;
+                    }
+                }
+                $quoted = $this->db->quote($value, $type);
+                if (PEAR::isError($quoted)) {
+                    return $quoted;
+                }
+                $param_query = 'SET @'.$parameter.' = '.$quoted;
+                $result = $this->db->_doQuery($param_query, true, $connection);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+            $query.= ' USING @'.implode(', @', array_values($this->positions));
+        }
+
+        $result = $this->db->_doQuery($query, $this->is_manip, $connection);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        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;
+    }
+
+    // }}}
+    // {{{ free()
+
+    /**
+     * Release resources allocated for the specified prepared query.
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function free()
+    {
+        if (is_null($this->positions)) {
+            return $this->db->raiseError(MDB2_ERROR, null, null,
+                'Prepared statement has already been freed', __FUNCTION__);
+        }
+        $result = MDB2_OK;
+
+        if (!is_null($this->statement)) {
+            $connection = $this->db->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+            $query = 'DEALLOCATE PREPARE '.$this->statement;
+            $result = $this->db->_doQuery($query, true, $connection);
+        }
+
+        parent::free();
+        return $result;
+    }
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/pgsql.php b/3rdparty/MDB2/Driver/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..6108f18dec16e69c4173531d90c6b357dc3e3d54
--- /dev/null
+++ b/3rdparty/MDB2/Driver/pgsql.php
@@ -0,0 +1,1519 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Paul Cooper <pgc@ucecom.com>                                 |
+// +----------------------------------------------------------------------+
+//
+// $Id: pgsql.php,v 1.203 2008/11/29 14:04:46 afz Exp $
+
+/**
+ * MDB2 PostGreSQL driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_Driver_pgsql extends MDB2_Driver_Common
+{
+    // {{{ properties
+    var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => '\\');
+
+    var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
+    // }}}
+    // {{{ constructor
+
+    /**
+     * Constructor
+     */
+    function __construct()
+    {
+        parent::__construct();
+
+        $this->phptype = 'pgsql';
+        $this->dbsyntax = 'pgsql';
+
+        $this->supported['sequences'] = true;
+        $this->supported['indexes'] = true;
+        $this->supported['affected_rows'] = true;
+        $this->supported['summary_functions'] = true;
+        $this->supported['order_by_text'] = true;
+        $this->supported['transactions'] = true;
+        $this->supported['savepoints'] = true;
+        $this->supported['current_id'] = true;
+        $this->supported['limit_queries'] = true;
+        $this->supported['LOBs'] = true;
+        $this->supported['replace'] = 'emulated';
+        $this->supported['sub_selects'] = true;
+        $this->supported['triggers'] = true;
+        $this->supported['auto_increment'] = 'emulated';
+        $this->supported['primary_key'] = true;
+        $this->supported['result_introspection'] = true;
+        $this->supported['prepared_statements'] = true;
+        $this->supported['identifier_quoting'] = true;
+        $this->supported['pattern_escaping'] = true;
+        $this->supported['new_link'] = true;
+
+        $this->options['DBA_username'] = false;
+        $this->options['DBA_password'] = false;
+        $this->options['multi_query'] = false;
+        $this->options['disable_smart_seqname'] = true;
+        $this->options['max_identifiers_length'] = 63;
+    }
+
+    // }}}
+    // {{{ errorInfo()
+
+    /**
+     * This method is used to collect information about an error
+     *
+     * @param integer $error
+     * @return array
+     * @access public
+     */
+    function errorInfo($error = null)
+    {
+        // Fall back to MDB2_ERROR if there was no mapping.
+        $error_code = MDB2_ERROR;
+
+        $native_msg = '';
+        if (is_resource($error)) {
+            $native_msg = @pg_result_error($error);
+        } elseif ($this->connection) {
+            $native_msg = @pg_last_error($this->connection);
+            if (!$native_msg && @pg_connection_status($this->connection) === PGSQL_CONNECTION_BAD) {
+                $native_msg = 'Database connection has been lost.';
+                $error_code = MDB2_ERROR_CONNECT_FAILED;
+            }
+        } else {
+            $native_msg = @pg_last_error();
+        }
+
+        static $error_regexps;
+        if (empty($error_regexps)) {
+            $error_regexps = array(
+                '/column .* (of relation .*)?does not exist/i'
+                    => MDB2_ERROR_NOSUCHFIELD,
+                '/(relation|sequence|table).*does not exist|class .* not found/i'
+                    => MDB2_ERROR_NOSUCHTABLE,
+                '/database .* does not exist/'
+                    => MDB2_ERROR_NOT_FOUND,
+                '/constraint .* does not exist/'
+                    => MDB2_ERROR_NOT_FOUND,
+                '/index .* does not exist/'
+                    => MDB2_ERROR_NOT_FOUND,
+                '/database .* already exists/i'
+                    => MDB2_ERROR_ALREADY_EXISTS,
+                '/relation .* already exists/i'
+                    => MDB2_ERROR_ALREADY_EXISTS,
+                '/(divide|division) by zero$/i'
+                    => MDB2_ERROR_DIVZERO,
+                '/pg_atoi: error in .*: can\'t parse /i'
+                    => MDB2_ERROR_INVALID_NUMBER,
+                '/invalid input syntax for( type)? (integer|numeric)/i'
+                    => MDB2_ERROR_INVALID_NUMBER,
+                '/value .* is out of range for type \w*int/i'
+                    => MDB2_ERROR_INVALID_NUMBER,
+                '/integer out of range/i'
+                    => MDB2_ERROR_INVALID_NUMBER,
+                '/value too long for type character/i'
+                    => MDB2_ERROR_INVALID,
+                '/attribute .* not found|relation .* does not have attribute/i'
+                    => MDB2_ERROR_NOSUCHFIELD,
+                '/column .* specified in USING clause does not exist in (left|right) table/i'
+                    => MDB2_ERROR_NOSUCHFIELD,
+                '/parser: parse error at or near/i'
+                    => MDB2_ERROR_SYNTAX,
+                '/syntax error at/'
+                    => MDB2_ERROR_SYNTAX,
+                '/column reference .* is ambiguous/i'
+                    => MDB2_ERROR_SYNTAX,
+                '/permission denied/'
+                    => MDB2_ERROR_ACCESS_VIOLATION,
+                '/violates not-null constraint/'
+                    => MDB2_ERROR_CONSTRAINT_NOT_NULL,
+                '/violates [\w ]+ constraint/'
+                    => MDB2_ERROR_CONSTRAINT,
+                '/referential integrity violation/'
+                    => MDB2_ERROR_CONSTRAINT,
+                '/more expressions than target columns/i'
+                    => MDB2_ERROR_VALUE_COUNT_ON_ROW,
+            );
+        }
+        if (is_numeric($error) && $error < 0) {
+            $error_code = $error;
+        } else {
+            foreach ($error_regexps as $regexp => $code) {
+                if (preg_match($regexp, $native_msg)) {
+                    $error_code = $code;
+                    break;
+                }
+            }
+        }
+        return array($error_code, null, $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
+     */
+    function escape($text, $escape_wildcards = false)
+    {
+        if ($escape_wildcards) {
+            $text = $this->escapePattern($text);
+        }
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+        if (is_resource($connection) && version_compare(PHP_VERSION, '5.2.0RC5', '>=')) {
+            $text = @pg_escape_string($connection, $text);
+        } else {
+            $text = @pg_escape_string($text);
+        }
+        return $text;
+    }
+
+    // }}}
+    // {{{ 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)) {
+            if (!$this->in_transaction) {
+                return $this->raiseError(MDB2_ERROR_INVALID, null, null,
+                    'savepoint cannot be released when changes are auto committed', __FUNCTION__);
+            }
+            $query = 'SAVEPOINT '.$savepoint;
+            return $this->_doQuery($query, true);
+        } 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');
+        }
+        $result =& $this->_doQuery('BEGIN', 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)) {
+            $query = 'RELEASE SAVEPOINT '.$savepoint;
+            return $this->_doQuery($query, true);
+        }
+
+        $result =& $this->_doQuery('COMMIT', true);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        $this->in_transaction = false;
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ rollback()
+
+    /**
+     * 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)) {
+            $query = 'ROLLBACK TO SAVEPOINT '.$savepoint;
+            return $this->_doQuery($query, true);
+        }
+
+        $query = 'ROLLBACK';
+        $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':
+        case 'READ COMMITTED':
+        case 'REPEATABLE READ':
+        case 'SERIALIZABLE':
+            break;
+        default:
+            return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
+                'isolation level is not supported: '.$isolation, __FUNCTION__);
+        }
+
+        $query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL $isolation";
+        return $this->_doQuery($query, true);
+    }
+
+    // }}}
+    // {{{ _doConnect()
+
+    /**
+     * Do the grunt work of connecting to the database
+     *
+     * @return mixed connection resource on success, MDB2 Error Object on failure
+     * @access protected
+     */
+    function _doConnect($username, $password, $database_name, $persistent = 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 ($database_name == '') {
+            $database_name = 'template1';
+        }
+
+        $protocol = $this->dsn['protocol'] ? $this->dsn['protocol'] : 'tcp';
+
+        $params = array('');
+        if ($protocol == 'tcp') {
+            if ($this->dsn['hostspec']) {
+                $params[0].= 'host=' . $this->dsn['hostspec'];
+            }
+            if ($this->dsn['port']) {
+                $params[0].= ' port=' . $this->dsn['port'];
+            }
+        } elseif ($protocol == 'unix') {
+            // Allow for pg socket in non-standard locations.
+            if ($this->dsn['socket']) {
+                $params[0].= 'host=' . $this->dsn['socket'];
+            }
+            if ($this->dsn['port']) {
+                $params[0].= ' port=' . $this->dsn['port'];
+            }
+        }
+        if ($database_name) {
+            $params[0].= ' dbname=\'' . addslashes($database_name) . '\'';
+        }
+        if ($username) {
+            $params[0].= ' user=\'' . addslashes($username) . '\'';
+        }
+        if ($password) {
+            $params[0].= ' password=\'' . addslashes($password) . '\'';
+        }
+        if (!empty($this->dsn['options'])) {
+            $params[0].= ' options=' . $this->dsn['options'];
+        }
+        if (!empty($this->dsn['tty'])) {
+            $params[0].= ' tty=' . $this->dsn['tty'];
+        }
+        if (!empty($this->dsn['connect_timeout'])) {
+            $params[0].= ' connect_timeout=' . $this->dsn['connect_timeout'];
+        }
+        if (!empty($this->dsn['sslmode'])) {
+            $params[0].= ' sslmode=' . $this->dsn['sslmode'];
+        }
+        if (!empty($this->dsn['service'])) {
+            $params[0].= ' service=' . $this->dsn['service'];
+        }
+
+        if ($this->_isNewLinkSet()) {
+            if (version_compare(phpversion(), '4.3.0', '>=')) {
+                $params[] = PGSQL_CONNECT_FORCE_NEW;
+            }
+        }
+
+        $connect_function = $persistent ? 'pg_pconnect' : 'pg_connect';
+        $connection = @call_user_func_array($connect_function, $params);
+        if (!$connection) {
+            return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null,
+                'unable to establish a connection', __FUNCTION__);
+        }
+
+       if (empty($this->dsn['disable_iso_date'])) {
+            if (!@pg_query($connection, "SET SESSION DATESTYLE = 'ISO'")) {
+                return $this->raiseError(null, null, null,
+                    'Unable to set date style to iso', __FUNCTION__);
+            }
+       }
+
+        if (!empty($this->dsn['charset'])) {
+            $result = $this->setCharset($this->dsn['charset'], $connection);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        return $connection;
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Connect to the database
+     *
+     * @return true on success, MDB2 Error Object on failure
+     * @access public
+     */
+    function connect()
+    {
+        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 == $this->database_name
+                && ($this->opened_persistent == $this->options['persistent'])
+            ) {
+                return MDB2_OK;
+            }
+            $this->disconnect(false);
+        }
+
+        if ($this->database_name) {
+            $connection = $this->_doConnect($this->dsn['username'],
+                                            $this->dsn['password'],
+                                            $this->database_name,
+                                            $this->options['persistent']);
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+
+            $this->connection = $connection;
+            $this->connected_dsn = $this->dsn;
+            $this->connected_database_name = $this->database_name;
+            $this->opened_persistent = $this->options['persistent'];
+            $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype;
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ setCharset()
+
+    /**
+     * Set the charset on the current connection
+     *
+     * @param string    charset
+     * @param resource  connection handle
+     *
+     * @return true on success, MDB2 Error Object on failure
+     */
+    function setCharset($charset, $connection = null)
+    {
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+        if (is_array($charset)) {
+            $charset   = array_shift($charset);
+            $this->warnings[] = 'postgresql does not support setting client collation';
+        }
+        $result = @pg_set_client_encoding($connection, $charset);
+        if ($result == -1) {
+            return $this->raiseError(null, null, null,
+                'Unable to set client charset: '.$charset, __FUNCTION__);
+        }
+        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)
+    {
+        $res = $this->_doConnect($this->dsn['username'],
+                                 $this->dsn['password'],
+                                 $this->escape($name),
+                                 $this->options['persistent']);
+        if (!PEAR::isError($res)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    // }}}
+    // {{{ 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 (is_resource($this->connection)) {
+            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) {
+                $ok = @pg_close($this->connection);
+                if (!$ok) {
+                    return $this->raiseError(MDB2_ERROR_DISCONNECT_FAILED,
+                           null, null, null, __FUNCTION__);
+                }
+            }
+        } else {
+            return false;
+        }
+        return parent::disconnect($force);
+    }
+
+    // }}}
+    // {{{ standaloneQuery()
+
+   /**
+     * execute a query as DBA
+     *
+     * @param string $query the SQL query
+     * @param mixed   $types  array that contains the types of the columns in
+     *                        the result set
+     * @param boolean $is_manip  if the query is a manipulation query
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function &standaloneQuery($query, $types = null, $is_manip = false)
+    {
+        $user = $this->options['DBA_username']? $this->options['DBA_username'] : $this->dsn['username'];
+        $pass = $this->options['DBA_password']? $this->options['DBA_password'] : $this->dsn['password'];
+        $connection = $this->_doConnect($user, $pass, $this->database_name, $this->options['persistent']);
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
+
+        $result =& $this->_doQuery($query, $is_manip, $connection, $this->database_name);
+        if (!PEAR::isError($result)) {
+            if ($is_manip) {
+                $result =  $this->_affectedRows($connection, $result);
+            } else {
+                $result =& $this->_wrapResult($result, $types, true, false, $limit, $offset);
+            }
+        }
+
+        @pg_close($connection);
+        return $result;
+    }
+
+    // }}}
+    // {{{ _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;
+        }
+
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+
+        $function = $this->options['multi_query'] ? 'pg_send_query' : 'pg_query';
+        $result = @$function($connection, $query);
+        if (!$result) {
+            $err =& $this->raiseError(null, null, null,
+                'Could not execute statement', __FUNCTION__);
+            return $err;
+        } elseif ($this->options['multi_query']) {
+            if (!($result = @pg_get_result($connection))) {
+                $err =& $this->raiseError(null, null, null,
+                        'Could not get the first result from a multi query', __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)
+    {
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+        return @pg_affected_rows($result);
+    }
+
+    // }}}
+    // {{{ _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 ($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 = $this->_modifyManipQuery($query, $limit);
+            } else {
+                $query.= " LIMIT $limit OFFSET $offset";
+            }
+        }
+        return $query;
+    }
+    
+    // }}}
+    // {{{ _modifyManipQuery()
+    
+    /**
+     * Changes a manip query string for various DBMS specific reasons
+     *
+     * @param string $query  query to modify
+     * @param integer $limit  limit the number of rows
+     * @return string modified query
+     * @access protected
+     */
+    function _modifyManipQuery($query, $limit)
+    {
+        $pos = strpos(strtolower($query), 'where');
+        $where = $pos ? substr($query, $pos) : '';
+
+        $manip_clause = '(\bDELETE\b\s+(?:\*\s+)?\bFROM\b|\bUPDATE\b)';
+        $from_clause  = '([\w\.]+)';
+        $where_clause = '(?:(.*)\bWHERE\b\s+(.*))|(.*)';
+        $pattern = '/^'. $manip_clause . '\s+' . $from_clause .'(?:\s)*(?:'. $where_clause .')?$/i';
+        $matches = preg_match($pattern, $query, $match);
+        if ($matches) {
+            $manip = $match[1];
+            $from  = $match[2];
+            $what  = (count($matches) == 6) ? $match[5] : $match[3];
+            return $manip.' '.$from.' '.$what.' WHERE ctid=(SELECT ctid FROM '.$from.' '.$where.' LIMIT '.$limit.')';
+        }
+        //return error?
+        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)
+    {
+        $query = 'SHOW SERVER_VERSION';
+        if ($this->connected_server_info) {
+            $server_info = $this->connected_server_info;
+        } else {
+            $server_info = $this->queryOne($query, 'text');
+            if (PEAR::isError($server_info)) {
+                return $server_info;
+            }
+        }
+        // cache server_info
+        $this->connected_server_info = $server_info;
+        if (!$native && !PEAR::isError($server_info)) {
+            $tmp = explode('.', $server_info, 3);
+            if (empty($tmp[2])
+                && isset($tmp[1])
+                && preg_match('/(\d+)(.*)/', $tmp[1], $tmp2)
+            ) {
+                $server_info = array(
+                    'major' => $tmp[0],
+                    'minor' => $tmp2[1],
+                    'patch' => null,
+                    'extra' => $tmp2[2],
+                    'native' => $server_info,
+                );
+            } else {
+                $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;
+    }
+
+    // }}}
+    // {{{ prepare()
+
+    /**
+     * 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']) {
+            $obj =& parent::prepare($query, $types, $result_types, $lobs);
+            return $obj;
+        }
+        $is_manip = ($result_types === MDB2_PREPARE_MANIP);
+        $offset = $this->offset;
+        $limit = $this->limit;
+        $this->offset = $this->limit = 0;
+        $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
+        if ($result) {
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+            $query = $result;
+        }
+        $pgtypes = function_exists('pg_prepare') ? false : array();
+        if ($pgtypes !== false && !empty($types)) {
+            $this->loadModule('Datatype', null, true);
+        }
+        $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
+        $placeholder_type_guess = $placeholder_type = null;
+        $question = '?';
+        $colon = ':';
+        $positions = array();
+        $position = $parameter = 0;
+        while ($position < strlen($query)) {
+            $q_position = strpos($query, $question, $position);
+            $c_position = strpos($query, $colon, $position);
+            //skip "::type" cast ("select id::varchar(20) from sometable where name=?")
+            $doublecolon_position = strpos($query, '::', $position);
+            if ($doublecolon_position !== false && $doublecolon_position == $c_position) {
+                $c_position = strpos($query, $colon, $position+2);
+            }
+            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 (!empty($types) && is_array($types)) {
+                        if ($placeholder_type == ':') {
+                        } else {
+                            $types = array_values($types);
+                        }
+                    }
+                }
+                if ($placeholder_type_guess == '?') {
+                    $length = 1;
+                    $name = $parameter;
+                } else {
+                    $regexp = '/^.{'.($position+1).'}('.$this->options['bindname_format'].').*$/s';
+                    $param = preg_replace($regexp, '\\1', $query);
+                    if ($param === '') {
+                        $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
+                            'named parameter name must match "bindname_format" option', __FUNCTION__);
+                        return $err;
+                    }
+                    $length = strlen($param) + 1;
+                    $name = $param;
+                }
+                if ($pgtypes !== false) {
+                    if (is_array($types) && array_key_exists($name, $types)) {
+                        $pgtypes[] = $this->datatype->mapPrepareDatatype($types[$name]);
+                    } elseif (is_array($types) && array_key_exists($parameter, $types)) {
+                        $pgtypes[] = $this->datatype->mapPrepareDatatype($types[$parameter]);
+                    } else {
+                        $pgtypes[] = 'text';
+                    }
+                }
+                if (($key_parameter = array_search($name, $positions))) {
+                    $next_parameter = 1;
+                    foreach ($positions as $key => $value) {
+                        if ($key_parameter == $key) {
+                            break;
+                        }
+                        ++$next_parameter;
+                    }
+                } else {
+                    ++$parameter;
+                    $next_parameter = $parameter;
+                    $positions[] = $name;
+                }
+                $query = substr_replace($query, '$'.$parameter, $position, $length);
+                $position = $p_position + strlen($parameter);
+            } else {
+                $position = $p_position;
+            }
+        }
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+        static $prep_statement_counter = 1;
+        $statement_name = sprintf($this->options['statement_format'], $this->phptype, $prep_statement_counter++ . sha1(microtime() + mt_rand()));
+        $statement_name = substr(strtolower($statement_name), 0, $this->options['max_identifiers_length']);
+        if ($pgtypes === false) {
+            $result = @pg_prepare($connection, $statement_name, $query);
+            if (!$result) {
+                $err =& $this->raiseError(null, null, null,
+                    'Unable to create prepared statement handle', __FUNCTION__);
+                return $err;
+            }
+        } else {
+            $types_string = '';
+            if ($pgtypes) {
+                $types_string = ' ('.implode(', ', $pgtypes).') ';
+            }
+            $query = 'PREPARE '.$statement_name.$types_string.' AS '.$query;
+            $statement =& $this->_doQuery($query, true, $connection);
+            if (PEAR::isError($statement)) {
+                return $statement;
+            }
+        }
+
+        $class_name = 'MDB2_Statement_'.$this->phptype;
+        $obj = new $class_name($this, $statement_name, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
+        $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
+        return $obj;
+    }
+
+    // }}}
+    // {{{ function getSequenceName($sqn)
+
+    /**
+     * adds sequence name formatting to a sequence name
+     *
+     * @param   string  name of the sequence
+     *
+     * @return  string  formatted sequence name
+     *
+     * @access  public
+     */
+    function getSequenceName($sqn)
+    {
+        if (false === $this->options['disable_smart_seqname']) {
+            if (strpos($sqn, '_') !== false) {
+                list($table, $field) = explode('_', $sqn, 2);
+            }
+            $schema_list = $this->queryOne("SELECT array_to_string(current_schemas(false), ',')");
+            if (PEAR::isError($schema_list) || empty($schema_list) || count($schema_list) < 2) {
+                $order_by = ' a.attnum';
+                $schema_clause = ' AND n.nspname=current_schema()';
+            } else {
+                $schemas = explode(',', $schema_list);
+                $schema_clause = ' AND n.nspname IN ('.$schema_list.')';
+                $counter = 1;
+                $order_by = ' CASE ';
+                foreach ($schemas as $schema) {
+                    $order_by .= ' WHEN n.nspname='.$schema.' THEN '.$counter++;
+                }
+                $order_by .= ' ELSE '.$counter.' END, a.attnum';
+            }
+
+            $query = "SELECT substring((SELECT substring(pg_get_expr(d.adbin, d.adrelid) for 128)
+                    	    FROM pg_attrdef d
+                    	   WHERE d.adrelid = a.attrelid
+                    	     AND d.adnum = a.attnum
+                    	     AND a.atthasdef
+                    	 ) FROM 'nextval[^'']*''([^'']*)')
+                        FROM pg_attribute a
+                    LEFT JOIN pg_class c ON c.oid = a.attrelid
+                    LEFT JOIN pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef
+                    LEFT JOIN pg_namespace n ON c.relnamespace = n.oid
+                       WHERE (c.relname = ".$this->quote($sqn, 'text');
+            if (!empty($field)) {
+                $query .= " OR (c.relname = ".$this->quote($table, 'text')." AND a.attname = ".$this->quote($field, 'text').")";
+            }
+            $query .= "      )"
+                         .$schema_clause."
+                         AND NOT a.attisdropped
+                         AND a.attnum > 0
+                         AND pg_get_expr(d.adbin, d.adrelid) LIKE 'nextval%'
+                    ORDER BY ".$order_by;
+            $seqname = $this->queryOne($query);
+            if (!PEAR::isError($seqname) && !empty($seqname) && is_string($seqname)) {
+                return $seqname;
+            }
+        }
+
+        return parent::getSequenceName($sqn);
+    }
+
+    // }}}
+    // {{{ 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);
+        $query = "SELECT NEXTVAL('$sequence_name')";
+        $this->pushErrorHandling(PEAR_ERROR_RETURN);
+        $this->expectError(MDB2_ERROR_NOSUCHTABLE);
+        $result = $this->queryOne($query, 'integer');
+        $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 could not be created', __FUNCTION__);
+                }
+                return $this->nextId($seq_name, false);
+            }
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ 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)
+    {
+        if (empty($table) && empty($field)) {
+            return $this->queryOne('SELECT lastval()', 'integer');
+        }
+        $seq = $table.(empty($field) ? '' : '_'.$field);
+        $sequence_name = $this->quoteIdentifier($this->getSequenceName($seq), true);
+        return $this->queryOne("SELECT currval('$sequence_name')", 'integer');
+    }
+
+    // }}}
+    // {{{ 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);
+        return $this->queryOne("SELECT last_value FROM $sequence_name", 'integer');
+    }
+}
+
+/**
+ * MDB2 PostGreSQL result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_Result_pgsql 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 = @pg_fetch_array($this->result, null, PGSQL_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 = @pg_fetch_row($this->result);
+        }
+        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 = @pg_field_name($this->result, $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()
+    {
+        $cols = @pg_num_fields($this->result);
+        if (is_null($cols)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return count($this->types);
+            }
+            return $this->db->raiseError(null, null, null,
+                'Could not get column count', __FUNCTION__);
+        }
+        return $cols;
+    }
+
+    // }}}
+    // {{{ nextResult()
+
+    /**
+     * Move the internal result pointer to the next available result
+     *
+     * @return true on success, false if there is no more result set or an error object on failure
+     * @access public
+     */
+    function nextResult()
+    {
+        $connection = $this->db->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+
+        if (!($this->result = @pg_get_result($connection))) {
+            return false;
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ free()
+
+    /**
+     * Free the internal resources associated with result.
+     *
+     * @return boolean true on success, false if result is invalid
+     * @access public
+     */
+    function free()
+    {
+        if (is_resource($this->result) && $this->db->connection) {
+            $free = @pg_free_result($this->result);
+            if ($free === false) {
+                return $this->db->raiseError(null, null, null,
+                    'Could not free result', __FUNCTION__);
+            }
+        }
+        $this->result = false;
+        return MDB2_OK;
+    }
+}
+
+/**
+ * MDB2 PostGreSQL buffered result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_BufferedResult_pgsql extends MDB2_Result_pgsql
+{
+    // {{{ 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)
+    {
+        if ($this->rownum != ($rownum - 1) && !@pg_result_seek($this->result, $rownum)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return MDB2_OK;
+            }
+            return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
+                'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
+        }
+        $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 = @pg_num_rows($this->result);
+        if (is_null($rows)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return 0;
+            }
+            return $this->db->raiseError(null, null, null,
+                'Could not get row count', __FUNCTION__);
+        }
+        return $rows;
+    }
+}
+
+/**
+ * MDB2 PostGreSQL statement driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Paul Cooper <pgc@ucecom.com>
+ */
+class MDB2_Statement_pgsql extends MDB2_Statement_Common
+{
+    // {{{ _execute()
+
+    /**
+     * 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;
+        }
+
+        $query = false;
+        $parameters = array();
+        // todo: disabled until pg_execute() bytea issues are cleared up
+        if (true || !function_exists('pg_execute')) {
+            $query = 'EXECUTE '.$this->statement;
+        }
+        if (!empty($this->positions)) {
+            foreach ($this->positions as $parameter) {
+                if (!array_key_exists($parameter, $this->values)) {
+                    return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
+                        'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
+                }
+                $value = $this->values[$parameter];
+                $type = array_key_exists($parameter, $this->types) ? $this->types[$parameter] : null;
+                if (is_resource($value) || $type == 'clob' || $type == 'blob' || $this->db->options['lob_allow_url_include']) {
+                    if (!is_resource($value) && preg_match('/^(\w+:\/\/)(.*)$/', $value, $match)) {
+                        if ($match[1] == 'file://') {
+                            $value = $match[2];
+                        }
+                        $value = @fopen($value, 'r');
+                        $close = true;
+                    }
+                    if (is_resource($value)) {
+                        $data = '';
+                        while (!@feof($value)) {
+                            $data.= @fread($value, $this->db->options['lob_buffer_length']);
+                        }
+                        if ($close) {
+                            @fclose($value);
+                        }
+                        $value = $data;
+                    }
+                }
+                $quoted = $this->db->quote($value, $type, $query);
+                if (PEAR::isError($quoted)) {
+                    return $quoted;
+                }
+                $parameters[] = $quoted;
+            }
+            if ($query) {
+                $query.= ' ('.implode(', ', $parameters).')';
+            }
+        }
+
+        if (!$query) {
+            $result = @pg_execute($connection, $this->statement, $parameters);
+            if (!$result) {
+                $err =& $this->db->raiseError(null, null, null,
+                    'Unable to execute statement', __FUNCTION__);
+                return $err;
+            }
+        } else {
+            $result = $this->db->_doQuery($query, $this->is_manip, $connection);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        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;
+    }
+
+    // }}}
+    // {{{ free()
+
+    /**
+     * Release resources allocated for the specified prepared query.
+     *
+     * @return mixed MDB2_OK on success, a MDB2 error on failure
+     * @access public
+     */
+    function free()
+    {
+        if (is_null($this->positions)) {
+            return $this->db->raiseError(MDB2_ERROR, null, null,
+                'Prepared statement has already been freed', __FUNCTION__);
+        }
+        $result = MDB2_OK;
+
+        if (!is_null($this->statement)) {
+            $connection = $this->db->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+            $query = 'DEALLOCATE PREPARE '.$this->statement;
+            $result = $this->db->_doQuery($query, true, $connection);
+        }
+
+        parent::free();
+        return $result;
+    }
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Driver/sqlite.php b/3rdparty/MDB2/Driver/sqlite.php
new file mode 100644
index 0000000000000000000000000000000000000000..63db241b86386f83742ee19e4dd83d3ec4552d8f
--- /dev/null
+++ b/3rdparty/MDB2/Driver/sqlite.php
@@ -0,0 +1,1089 @@
+<?php
+// vim: set et ts=4 sw=4 fdm=marker:
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: sqlite.php,v 1.165 2008/11/30 14:28:01 afz Exp $
+//
+
+/**
+ * MDB2 SQLite driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Driver_sqlite extends MDB2_Driver_Common
+{
+    // {{{ properties
+    var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => "'", 'escape_pattern' => false);
+
+    var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
+
+    var $_lasterror = '';
+
+    var $fix_assoc_fields_names = false;
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * Constructor
+     */
+    function __construct()
+    {
+        parent::__construct();
+
+        $this->phptype = 'sqlite';
+        $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'] = true;
+        $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'] = 'emulated';
+        $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 = @sqlite_last_error($this->connection);
+        }
+        $native_msg = $this->_lasterror
+            ? html_entity_decode($this->_lasterror) : @sqlite_error_string($native_code);
+            
+        // 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
+     */
+    function escape($text, $escape_wildcards = false)
+    {
+        $text = @sqlite_escape_string($text);
+        return $text;
+    }
+
+    // }}}
+    // {{{ 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()
+    {
+		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__);
+            }
+        }
+
+        $connect_function = ($this->options['persistent'] ? 'sqlite_popen' : 'sqlite_open');
+        $php_errormsg = '';
+        if (version_compare('5.1.0', PHP_VERSION, '>')) {
+            @ini_set('track_errors', true);
+            echo 1;
+            $connection = @$connect_function($database_file);
+            echo 2;
+            @ini_restore('track_errors');
+        } else {
+            $connection = @$connect_function($database_file, 0666, $php_errormsg);
+        }
+        $this->_lasterror = $php_errormsg;
+        if (!$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)
+        {
+            @sqlite_query("PRAGMA short_column_names = 1", $connection);
+            $this->fix_assoc_fields_names = true;
+        }
+
+        $this->connection = $connection;
+        $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 (is_resource($this->connection)) {
+            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) {
+                @sqlite_close($this->connection);
+            }
+        } 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;
+        }
+
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+
+        $function = $this->options['result_buffering']
+            ? 'sqlite_query' : 'sqlite_unbuffered_query';
+        $php_errormsg = '';
+        if (version_compare('5.1.0', PHP_VERSION, '>')) {
+            @ini_set('track_errors', true);
+            do {
+                $result = @$function($query.';', $connection);
+            } while (sqlite_last_error($connection) == SQLITE_SCHEMA);
+            @ini_restore('track_errors');
+        } else {
+            do {
+                $result = @$function($query.';', $connection, SQLITE_BOTH, $php_errormsg);
+            } while (sqlite_last_error($connection) == SQLITE_SCHEMA);
+        }
+        $this->_lasterror = $php_errormsg;
+
+        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)
+    {
+        if (is_null($connection)) {
+            $connection = $this->getConnection();
+            if (PEAR::isError($connection)) {
+                return $connection;
+            }
+        }
+        return @sqlite_changes($connection);
+    }
+
+    // }}}
+    // {{{ _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)
+    {
+        $connection = $this->getConnection();
+        if (PEAR::isError($connection)) {
+            return $connection;
+        }
+        $value = @sqlite_last_insert_rowid($connection);
+        if (!$value) {
+            return $this->raiseError(null, null, null,
+                'Could not get last insert ID', __FUNCTION__);
+        }
+        return $value;
+    }
+
+    // }}}
+    // {{{ 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');
+    }
+}
+
+/**
+ * MDB2 SQLite result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Result_sqlite 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);
+            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 = @sqlite_fetch_array($this->result, SQLITE_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 = @sqlite_field_name($this->result, $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()
+    {
+        $cols = @sqlite_num_fields($this->result);
+        if (is_null($cols)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return count($this->types);
+            }
+            return $this->db->raiseError(null, null, null,
+                'Could not get column count', __FUNCTION__);
+        }
+        return $cols;
+    }
+}
+
+/**
+ * MDB2 SQLite buffered result driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_BufferedResult_sqlite extends MDB2_Result_sqlite
+{
+    // {{{ 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)
+    {
+        if (!@sqlite_seek($this->result, $rownum)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return MDB2_OK;
+            }
+            return $this->db->raiseError(MDB2_ERROR_INVALID, null, null,
+                'tried to seek to an invalid row number ('.$rownum.')', __FUNCTION__);
+        }
+        $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 = @sqlite_num_rows($this->result);
+        if (is_null($rows)) {
+            if ($this->result === false) {
+                return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                    'resultset has already been freed', __FUNCTION__);
+            } elseif (is_null($this->result)) {
+                return 0;
+            }
+            return $this->db->raiseError(null, null, null,
+                'Could not get row count', __FUNCTION__);
+        }
+        return $rows;
+    }
+}
+
+/**
+ * MDB2 SQLite statement driver
+ *
+ * @package MDB2
+ * @category Database
+ * @author  Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Statement_sqlite extends MDB2_Statement_Common
+{
+
+}
+
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Extended.php b/3rdparty/MDB2/Extended.php
new file mode 100644
index 0000000000000000000000000000000000000000..864ab9235432bd0e839d6093269b627062d27f8d
--- /dev/null
+++ b/3rdparty/MDB2/Extended.php
@@ -0,0 +1,721 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Extended.php,v 1.60 2007/11/28 19:49:34 quipo Exp $
+
+/**
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+
+/**
+ * Used by autoPrepare()
+ */
+define('MDB2_AUTOQUERY_INSERT', 1);
+define('MDB2_AUTOQUERY_UPDATE', 2);
+define('MDB2_AUTOQUERY_DELETE', 3);
+define('MDB2_AUTOQUERY_SELECT', 4);
+
+/**
+ * MDB2_Extended: class which adds several high level methods to MDB2
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Extended extends MDB2_Module_Common
+{
+    // {{{ autoPrepare()
+
+    /**
+     * Generate an insert, update or delete query and call prepare() on it
+     *
+     * @param string table
+     * @param array the fields names
+     * @param int type of query to build
+     *                          MDB2_AUTOQUERY_INSERT
+     *                          MDB2_AUTOQUERY_UPDATE
+     *                          MDB2_AUTOQUERY_DELETE
+     *                          MDB2_AUTOQUERY_SELECT
+     * @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
+     * @param array that contains the types of the placeholders
+     * @param mixed 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
+     *
+     * @return resource handle for the query
+     * @see buildManipSQL
+     * @access public
+     */
+    function autoPrepare($table, $table_fields, $mode = MDB2_AUTOQUERY_INSERT,
+        $where = false, $types = null, $result_types = MDB2_PREPARE_MANIP)
+    {
+        $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
+        if (PEAR::isError($query)) {
+            return $query;
+        }
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        $lobs = array();
+        foreach ((array)$types as $param => $type) {
+            if (($type == 'clob') || ($type == 'blob')) {
+                $lobs[$param] = $table_fields[$param];
+            }
+        }
+        return $db->prepare($query, $types, $result_types, $lobs);
+    }
+
+    // }}}
+    // {{{ autoExecute()
+
+    /**
+     * Generate an insert, update or delete query and call prepare() and execute() on it
+     *
+     * @param string name of the table
+     * @param array assoc ($key=>$value) where $key is a field name and $value its value
+     * @param int type of query to build
+     *                          MDB2_AUTOQUERY_INSERT
+     *                          MDB2_AUTOQUERY_UPDATE
+     *                          MDB2_AUTOQUERY_DELETE
+     *                          MDB2_AUTOQUERY_SELECT
+     * @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
+     * @param array that contains the types of the placeholders
+     * @param string which specifies which result class to use
+     * @param mixed  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
+     *
+     * @return bool|MDB2_Error true on success, a MDB2 error on failure
+     * @see buildManipSQL
+     * @see autoPrepare
+     * @access public
+    */
+    function &autoExecute($table, $fields_values, $mode = MDB2_AUTOQUERY_INSERT,
+        $where = false, $types = null, $result_class = true, $result_types = MDB2_PREPARE_MANIP)
+    {
+        $fields_values = (array)$fields_values;
+        if ($mode == MDB2_AUTOQUERY_SELECT) {
+            if (is_array($result_types)) {
+                $keys = array_keys($result_types);
+            } elseif (!empty($fields_values)) {
+                $keys = $fields_values;
+            } else {
+                $keys = array();
+            }
+        } else {
+            $keys = array_keys($fields_values);
+        }
+        $params = array_values($fields_values);
+        if (empty($params)) {
+            $query = $this->buildManipSQL($table, $keys, $mode, $where);
+
+            $db =& $this->getDBInstance();
+            if (PEAR::isError($db)) {
+                return $db;
+            }
+            if ($mode == MDB2_AUTOQUERY_SELECT) {
+                $result =& $db->query($query, $result_types, $result_class);
+            } else {
+                $result = $db->exec($query);
+            }
+        } else {
+            $stmt = $this->autoPrepare($table, $keys, $mode, $where, $types, $result_types);
+            if (PEAR::isError($stmt)) {
+                return $stmt;
+            }
+            $result =& $stmt->execute($params, $result_class);
+            $stmt->free();
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ buildManipSQL()
+
+    /**
+     * Make automaticaly an sql query for prepare()
+     *
+     * Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), MDB2_AUTOQUERY_INSERT)
+     *           will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
+     * NB : - This belongs more to a SQL Builder class, but this is a simple facility
+     *      - Be carefull ! If you don't give a $where param with an UPDATE/DELETE query, all
+     *        the records of the table will be updated/deleted !
+     *
+     * @param string name of the table
+     * @param ordered array containing the fields names
+     * @param int type of query to build
+     *                          MDB2_AUTOQUERY_INSERT
+     *                          MDB2_AUTOQUERY_UPDATE
+     *                          MDB2_AUTOQUERY_DELETE
+     *                          MDB2_AUTOQUERY_SELECT
+     * @param string (in case of update and delete queries, this string will be put after the sql WHERE statement)
+     *
+     * @return string sql query for prepare()
+     * @access public
+     */
+    function buildManipSQL($table, $table_fields, $mode, $where = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if ($db->options['quote_identifier']) {
+            $table = $db->quoteIdentifier($table);
+        }
+
+        if (!empty($table_fields) && $db->options['quote_identifier']) {
+            foreach ($table_fields as $key => $field) {
+                $table_fields[$key] = $db->quoteIdentifier($field);
+            }
+        }
+
+        if ($where !== false && !is_null($where)) {
+            if (is_array($where)) {
+                $where = implode(' AND ', $where);
+            }
+            $where = ' WHERE '.$where;
+        }
+
+        switch ($mode) {
+        case MDB2_AUTOQUERY_INSERT:
+            if (empty($table_fields)) {
+                return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                'Insert requires table fields', __FUNCTION__);
+            }
+            $cols = implode(', ', $table_fields);
+            $values = '?'.str_repeat(', ?', (count($table_fields) - 1));
+            return 'INSERT INTO '.$table.' ('.$cols.') VALUES ('.$values.')';
+            break;
+        case MDB2_AUTOQUERY_UPDATE:
+            if (empty($table_fields)) {
+                return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null,
+                'Update requires table fields', __FUNCTION__);
+            }
+            $set = implode(' = ?, ', $table_fields).' = ?';
+            $sql = 'UPDATE '.$table.' SET '.$set.$where;
+            return $sql;
+            break;
+        case MDB2_AUTOQUERY_DELETE:
+            $sql = 'DELETE FROM '.$table.$where;
+            return $sql;
+            break;
+        case MDB2_AUTOQUERY_SELECT:
+            $cols = !empty($table_fields) ? implode(', ', $table_fields) : '*';
+            $sql = 'SELECT '.$cols.' FROM '.$table.$where;
+            return $sql;
+            break;
+        }
+        return $db->raiseError(MDB2_ERROR_SYNTAX, null, null,
+                'Non existant mode', __FUNCTION__);
+    }
+
+    // }}}
+    // {{{ limitQuery()
+
+    /**
+     * Generates a limited query
+     *
+     * @param string query
+     * @param array that contains the types of the columns in the result set
+     * @param integer the numbers of rows to fetch
+     * @param integer the row to start to fetching
+     * @param string which specifies which result class to use
+     * @param mixed   string which specifies which class to wrap results in
+     *
+     * @return MDB2_Result|MDB2_Error result set on success, a MDB2 error on failure
+     * @access public
+     */
+    function &limitQuery($query, $types, $limit, $offset = 0, $result_class = true,
+        $result_wrap_class = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        $result = $db->setLimit($limit, $offset);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        $result =& $db->query($query, $types, $result_class, $result_wrap_class);
+        return $result;
+    }
+
+    // }}}
+    // {{{ execParam()
+
+    /**
+     * Execute a parameterized DML statement.
+     *
+     * @param string the SQL query
+     * @param array if supplied, prepare/execute will be used
+     *       with this array as execute parameters
+     * @param array that contains the types of the values defined in $params
+     *
+     * @return int|MDB2_Error affected rows on success, a MDB2 error on failure
+     * @access public
+     */
+    function execParam($query, $params = array(), $param_types = null)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        settype($params, 'array');
+        if (empty($params)) {
+            return $db->exec($query);
+        }
+
+        $stmt = $db->prepare($query, $param_types, MDB2_PREPARE_MANIP);
+        if (PEAR::isError($stmt)) {
+            return $stmt;
+        }
+
+        $result = $stmt->execute($params);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $stmt->free();
+        return $result;
+    }
+
+    // }}}
+    // {{{ getOne()
+
+    /**
+     * Fetch the first column of the first row of data returned from a query.
+     * Takes care of doing the query and freeing the results when finished.
+     *
+     * @param string the SQL query
+     * @param string that contains the type of the column in the result set
+     * @param array if supplied, prepare/execute will be used
+     *       with this array as execute parameters
+     * @param array that contains the types of the values defined in $params
+     * @param int|string which column to return
+     *
+     * @return scalar|MDB2_Error data on success, a MDB2 error on failure
+     * @access public
+     */
+    function getOne($query, $type = null, $params = array(),
+        $param_types = null, $colnum = 0)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        settype($params, 'array');
+        settype($type, 'array');
+        if (empty($params)) {
+            return $db->queryOne($query, $type, $colnum);
+        }
+
+        $stmt = $db->prepare($query, $param_types, $type);
+        if (PEAR::isError($stmt)) {
+            return $stmt;
+        }
+
+        $result = $stmt->execute($params);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $one = $result->fetchOne($colnum);
+        $stmt->free();
+        $result->free();
+        return $one;
+    }
+
+    // }}}
+    // {{{ getRow()
+
+    /**
+     * Fetch the first row of data returned from a query.  Takes care
+     * of doing the query and freeing the results when finished.
+     *
+     * @param string the SQL query
+     * @param array that contains the types of the columns in the result set
+     * @param array if supplied, prepare/execute will be used
+     *       with this array as execute parameters
+     * @param array that contains the types of the values defined in $params
+     * @param int the fetch mode to use
+     *
+     * @return array|MDB2_Error data on success, a MDB2 error on failure
+     * @access public
+     */
+    function getRow($query, $types = null, $params = array(),
+        $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        settype($params, 'array');
+        if (empty($params)) {
+            return $db->queryRow($query, $types, $fetchmode);
+        }
+
+        $stmt = $db->prepare($query, $param_types, $types);
+        if (PEAR::isError($stmt)) {
+            return $stmt;
+        }
+
+        $result = $stmt->execute($params);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $row = $result->fetchRow($fetchmode);
+        $stmt->free();
+        $result->free();
+        return $row;
+    }
+
+    // }}}
+    // {{{ getCol()
+
+    /**
+     * Fetch a single column from a result set and return it as an
+     * indexed array.
+     *
+     * @param string the SQL query
+     * @param string that contains the type of the column in the result set
+     * @param array if supplied, prepare/execute will be used
+     *       with this array as execute parameters
+     * @param array that contains the types of the values defined in $params
+     * @param int|string which column to return
+     *
+     * @return array|MDB2_Error data on success, a MDB2 error on failure
+     * @access public
+     */
+    function getCol($query, $type = null, $params = array(),
+        $param_types = null, $colnum = 0)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        settype($params, 'array');
+        settype($type, 'array');
+        if (empty($params)) {
+            return $db->queryCol($query, $type, $colnum);
+        }
+
+        $stmt = $db->prepare($query, $param_types, $type);
+        if (PEAR::isError($stmt)) {
+            return $stmt;
+        }
+
+        $result = $stmt->execute($params);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $col = $result->fetchCol($colnum);
+        $stmt->free();
+        $result->free();
+        return $col;
+    }
+
+    // }}}
+    // {{{ getAll()
+
+    /**
+     * Fetch all the rows returned from a query.
+     *
+     * @param string the SQL query
+     * @param array that contains the types of the columns in the result set
+     * @param array if supplied, prepare/execute will be used
+     *       with this array as execute parameters
+     * @param array that contains the types of the values defined in $params
+     * @param int the fetch mode to use
+     * @param bool if set to true, the $all will have the first
+     *       column as its first dimension
+     * @param bool $force_array used only when the query returns exactly
+     *       two columns. If true, the values of the returned array will be
+     *       one-element arrays instead of scalars.
+     * @param bool $group if true, the values of the returned array is
+     *       wrapped in another array.  If the same key value (in the first
+     *       column) repeats itself, the values will be appended to this array
+     *       instead of overwriting the existing values.
+     *
+     * @return array|MDB2_Error data on success, a MDB2 error on failure
+     * @access public
+     */
+    function getAll($query, $types = null, $params = array(),
+        $param_types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
+        $rekey = false, $force_array = false, $group = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        settype($params, 'array');
+        if (empty($params)) {
+            return $db->queryAll($query, $types, $fetchmode, $rekey, $force_array, $group);
+        }
+
+        $stmt = $db->prepare($query, $param_types, $types);
+        if (PEAR::isError($stmt)) {
+            return $stmt;
+        }
+
+        $result = $stmt->execute($params);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
+        $stmt->free();
+        $result->free();
+        return $all;
+    }
+
+    // }}}
+    // {{{ getAssoc()
+
+    /**
+     * Fetch the entire result set of a query and return it as an
+     * associative array using the first column as the key.
+     *
+     * If the result set contains more than two columns, the value
+     * will be an array of the values from column 2-n.  If the result
+     * set contains only two columns, the returned value will be a
+     * scalar with the value of the second column (unless forced to an
+     * array with the $force_array parameter).  A MDB2 error code is
+     * returned on errors.  If the result set contains fewer than two
+     * columns, a MDB2_ERROR_TRUNCATED error is returned.
+     *
+     * For example, if the table 'mytable' contains:
+     * <pre>
+     *   ID      TEXT       DATE
+     * --------------------------------
+     *   1       'one'      944679408
+     *   2       'two'      944679408
+     *   3       'three'    944679408
+     * </pre>
+     * Then the call getAssoc('SELECT id,text FROM mytable') returns:
+     * <pre>
+     *    array(
+     *      '1' => 'one',
+     *      '2' => 'two',
+     *      '3' => 'three',
+     *    )
+     * </pre>
+     * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
+     * <pre>
+     *    array(
+     *      '1' => array('one', '944679408'),
+     *      '2' => array('two', '944679408'),
+     *      '3' => array('three', '944679408')
+     *    )
+     * </pre>
+     *
+     * If the more than one row occurs with the same value in the
+     * first column, the last row overwrites all previous ones by
+     * default.  Use the $group parameter if you don't want to
+     * overwrite like this.  Example:
+     * <pre>
+     * getAssoc('SELECT category,id,name FROM mytable', null, null
+     *           MDB2_FETCHMODE_ASSOC, false, true) returns:
+     *    array(
+     *      '1' => array(array('id' => '4', 'name' => 'number four'),
+     *                   array('id' => '6', 'name' => 'number six')
+     *             ),
+     *      '9' => array(array('id' => '4', 'name' => 'number four'),
+     *                   array('id' => '6', 'name' => 'number six')
+     *             )
+     *    )
+     * </pre>
+     *
+     * Keep in mind that database functions in PHP usually return string
+     * values for results regardless of the database's internal type.
+     *
+     * @param string the SQL query
+     * @param array that contains the types of the columns in the result set
+     * @param array if supplied, prepare/execute will be used
+     *       with this array as execute parameters
+     * @param array that contains the types of the values defined in $params
+     * @param bool $force_array used only when the query returns
+     * exactly two columns.  If TRUE, the values of the returned array
+     * will be one-element arrays instead of scalars.
+     * @param bool $group if TRUE, the values of the returned array
+     *       is wrapped in another array.  If the same key value (in the first
+     *       column) repeats itself, the values will be appended to this array
+     *       instead of overwriting the existing values.
+     *
+     * @return array|MDB2_Error data on success, a MDB2 error on failure
+     * @access public
+     */
+    function getAssoc($query, $types = null, $params = array(), $param_types = null,
+        $fetchmode = MDB2_FETCHMODE_DEFAULT, $force_array = false, $group = false)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        settype($params, 'array');
+        if (empty($params)) {
+            return $db->queryAll($query, $types, $fetchmode, true, $force_array, $group);
+        }
+
+        $stmt = $db->prepare($query, $param_types, $types);
+        if (PEAR::isError($stmt)) {
+            return $stmt;
+        }
+
+        $result = $stmt->execute($params);
+        if (!MDB2::isResultCommon($result)) {
+            return $result;
+        }
+
+        $all = $result->fetchAll($fetchmode, true, $force_array, $group);
+        $stmt->free();
+        $result->free();
+        return $all;
+    }
+
+    // }}}
+    // {{{ executeMultiple()
+
+    /**
+     * This function does several execute() calls on the same statement handle.
+     * $params must be an array indexed numerically from 0, one execute call is
+     * done for every 'row' in the array.
+     *
+     * If an error occurs during execute(), executeMultiple() does not execute
+     * the unfinished rows, but rather returns that error.
+     *
+     * @param resource query handle from prepare()
+     * @param array numeric array containing the data to insert into the query
+     *
+     * @return bool|MDB2_Error true on success, a MDB2 error on failure
+     * @access public
+     * @see prepare(), execute()
+     */
+    function executeMultiple(&$stmt, $params = null)
+    {
+        for ($i = 0, $j = count($params); $i < $j; $i++) {
+            $result = $stmt->execute($params[$i]);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ getBeforeID()
+
+    /**
+     * Returns the next free id of a sequence if the RDBMS
+     * does not support auto increment
+     *
+     * @param string name of the table into which a new row was inserted
+     * @param string name of the field into which a new row was inserted
+     * @param bool when true the sequence is automatic created, if it not exists
+     * @param bool if the returned value should be quoted
+     *
+     * @return int|MDB2_Error id on success, a MDB2 error on failure
+     * @access public
+     */
+    function getBeforeID($table, $field = null, $ondemand = true, $quote = true)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if ($db->supports('auto_increment') !== true) {
+            $seq = $table.(empty($field) ? '' : '_'.$field);
+            $id = $db->nextID($seq, $ondemand);
+            if (!$quote || PEAR::isError($id)) {
+                return $id;
+            }
+            return $db->quote($id, 'integer');
+        } elseif (!$quote) {
+            return null;
+        }
+        return 'NULL';
+    }
+
+    // }}}
+    // {{{ getAfterID()
+
+    /**
+     * Returns the autoincrement ID if supported or $id
+     *
+     * @param mixed value as returned by getBeforeId()
+     * @param string name of the table into which a new row was inserted
+     * @param string name of the field into which a new row was inserted
+     *
+     * @return int|MDB2_Error id on success, a MDB2 error on failure
+     * @access public
+     */
+    function getAfterID($id, $table, $field = null)
+    {
+        $db =& $this->getDBInstance();
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+
+        if ($db->supports('auto_increment') !== true) {
+            return $id;
+        }
+        return $db->lastInsertID($table, $field);
+    }
+
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Iterator.php b/3rdparty/MDB2/Iterator.php
new file mode 100644
index 0000000000000000000000000000000000000000..ca5e7b69c27df7091c4b01483ada57d2435f2440
--- /dev/null
+++ b/3rdparty/MDB2/Iterator.php
@@ -0,0 +1,259 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: Iterator.php,v 1.22 2006/05/06 14:03:41 lsmith Exp $
+
+/**
+ * PHP5 Iterator
+ *
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_Iterator implements Iterator
+{
+    protected $fetchmode;
+    protected $result;
+    protected $row;
+
+    // {{{ constructor
+
+    /**
+     * Constructor
+     */
+    public function __construct($result, $fetchmode = MDB2_FETCHMODE_DEFAULT)
+    {
+        $this->result = $result;
+        $this->fetchmode = $fetchmode;
+    }
+    // }}}
+
+    // {{{ seek()
+
+    /**
+     * Seek forward to a specific row in a result set
+     *
+     * @param int number of the row where the data can be found
+     *
+     * @return void
+     * @access public
+     */
+    public function seek($rownum)
+    {
+        $this->row = null;
+        if ($this->result) {
+            $this->result->seek($rownum);
+        }
+    }
+    // }}}
+
+    // {{{ next()
+
+    /**
+     * Fetch next row of data
+     *
+     * @return void
+     * @access public
+     */
+    public function next()
+    {
+        $this->row = null;
+    }
+    // }}}
+
+    // {{{ current()
+
+    /**
+     * return a row of data
+     *
+     * @return void
+     * @access public
+     */
+    public function current()
+    {
+        if (is_null($this->row)) {
+            $row = $this->result->fetchRow($this->fetchmode);
+            if (PEAR::isError($row)) {
+                $row = false;
+            }
+            $this->row = $row;
+        }
+        return $this->row;
+    }
+    // }}}
+
+    // {{{ valid()
+
+    /**
+     * Check if the end of the result set has been reached
+     *
+     * @return bool true/false, false is also returned on failure
+     * @access public
+     */
+    public function valid()
+    {
+        return (bool)$this->current();
+    }
+    // }}}
+
+    // {{{ free()
+
+    /**
+     * Free the internal resources associated with result.
+     *
+     * @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
+     * @access public
+     */
+    public function free()
+    {
+        if ($this->result) {
+            return $this->result->free();
+        }
+        $this->result = false;
+        $this->row = null;
+        return false;
+    }
+    // }}}
+
+    // {{{ key()
+
+    /**
+     * Returns the row number
+     *
+     * @return int|bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
+     * @access public
+     */
+    public function key()
+    {
+        if ($this->result) {
+            return $this->result->rowCount();
+        }
+        return false;
+    }
+    // }}}
+
+    // {{{ rewind()
+
+    /**
+     * Seek to the first row in a result set
+     *
+     * @return void
+     * @access public
+     */
+    public function rewind()
+    {
+    }
+    // }}}
+
+    // {{{ destructor
+
+    /**
+     * Destructor
+     */
+    public function __destruct()
+    {
+        $this->free();
+    }
+    // }}}
+}
+
+/**
+ * PHP5 buffered Iterator
+ *
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_BufferedIterator extends MDB2_Iterator implements SeekableIterator
+{
+    // {{{ valid()
+
+    /**
+     * Check if the end of the result set has been reached
+     *
+     * @return bool|MDB2_Error true on success, false|MDB2_Error if result is invalid
+     * @access public
+     */
+    public function valid()
+    {
+        if ($this->result) {
+            return $this->result->valid();
+        }
+        return false;
+    }
+    // }}}
+
+    // {{{count()
+
+    /**
+     * Returns the number of rows in a result object
+     *
+     * @return int|MDB2_Error number of rows, false|MDB2_Error if result is invalid
+     * @access public
+     */
+    public function count()
+    {
+        if ($this->result) {
+            return $this->result->numRows();
+        }
+        return false;
+    }
+    // }}}
+
+    // {{{ rewind()
+
+    /**
+     * Seek to the first row in a result set
+     *
+     * @return void
+     * @access public
+     */
+    public function rewind()
+    {
+        $this->seek(0);
+    }
+    // }}}
+}
+
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/LOB.php b/3rdparty/MDB2/LOB.php
new file mode 100644
index 0000000000000000000000000000000000000000..ae67224020e84d944a450b5e1c6be5010d1c3893
--- /dev/null
+++ b/3rdparty/MDB2/LOB.php
@@ -0,0 +1,264 @@
+<?php
+// +----------------------------------------------------------------------+
+// | PHP version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lukas Smith <smith@pooteeweet.org>                           |
+// +----------------------------------------------------------------------+
+//
+// $Id: LOB.php,v 1.34 2006/10/25 11:52:21 lsmith Exp $
+
+/**
+ * @package  MDB2
+ * @category Database
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ */
+
+require_once('MDB2.php');
+
+/**
+ * MDB2_LOB: user land stream wrapper implementation for LOB support
+ *
+ * @package MDB2
+ * @category Database
+ * @author Lukas Smith <smith@pooteeweet.org>
+ */
+class MDB2_LOB
+{
+    /**
+     * contains the key to the global MDB2 instance array of the associated
+     * MDB2 instance
+     *
+     * @var integer
+     * @access protected
+     */
+    var $db_index;
+
+    /**
+     * contains the key to the global MDB2_LOB instance array of the associated
+     * MDB2_LOB instance
+     *
+     * @var integer
+     * @access protected
+     */
+    var $lob_index;
+
+    // {{{ stream_open()
+
+    /**
+     * open stream
+     *
+     * @param string specifies the URL that was passed to fopen()
+     * @param string the mode used to open the file
+     * @param int holds additional flags set by the streams API
+     * @param string not used
+     *
+     * @return bool
+     * @access public
+     */
+    function stream_open($path, $mode, $options, &$opened_path)
+    {
+        if (!preg_match('/^rb?\+?$/', $mode)) {
+            return false;
+        }
+        $url = parse_url($path);
+        if (empty($url['host'])) {
+            return false;
+        }
+        $this->db_index = (int)$url['host'];
+        if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            return false;
+        }
+        $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
+        $this->lob_index = (int)$url['user'];
+        if (!isset($db->datatype->lobs[$this->lob_index])) {
+            return false;
+        }
+        return true;
+    }
+    // }}}
+
+    // {{{ stream_read()
+
+    /**
+     * read stream
+     *
+     * @param int number of bytes to read
+     *
+     * @return string
+     * @access public
+     */
+    function stream_read($count)
+    {
+        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
+            $db->datatype->_retrieveLOB($db->datatype->lobs[$this->lob_index]);
+
+            $data = $db->datatype->_readLOB($db->datatype->lobs[$this->lob_index], $count);
+            $length = strlen($data);
+            if ($length == 0) {
+                $db->datatype->lobs[$this->lob_index]['endOfLOB'] = true;
+            }
+            $db->datatype->lobs[$this->lob_index]['position'] += $length;
+            return $data;
+        }
+    }
+    // }}}
+
+    // {{{ stream_write()
+
+    /**
+     * write stream, note implemented
+     *
+     * @param string data
+     *
+     * @return int
+     * @access public
+     */
+    function stream_write($data)
+    {
+        return 0;
+    }
+    // }}}
+
+    // {{{ stream_tell()
+
+    /**
+     * return the current position
+     *
+     * @return int current position
+     * @access public
+     */
+    function stream_tell()
+    {
+        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
+            return $db->datatype->lobs[$this->lob_index]['position'];
+        }
+    }
+    // }}}
+
+    // {{{ stream_eof()
+
+    /**
+     * Check if stream reaches EOF
+     *
+     * @return bool
+     * @access public
+     */
+    function stream_eof()
+    {
+        if (!isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            return true;
+        }
+
+        $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
+        $result = $db->datatype->_endOfLOB($db->datatype->lobs[$this->lob_index]);
+        if (version_compare(phpversion(), "5.0", ">=")
+            && version_compare(phpversion(), "5.1", "<")
+        ) {
+            return !$result;
+        }
+        return $result;
+    }
+    // }}}
+
+    // {{{ stream_seek()
+
+    /**
+     * Seek stream, not implemented
+     *
+     * @param int offset
+     * @param int whence
+     *
+     * @return bool
+     * @access public
+     */
+    function stream_seek($offset, $whence)
+    {
+        return false;
+    }
+    // }}}
+
+    // {{{ stream_stat()
+
+    /**
+     * return information about stream
+     *
+     * @access public
+     */
+    function stream_stat()
+    {
+        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
+            return array(
+              'db_index' => $this->db_index,
+              'lob_index' => $this->lob_index,
+            );
+        }
+    }
+    // }}}
+
+    // {{{ stream_close()
+
+    /**
+     * close stream
+     *
+     * @access public
+     */
+    function stream_close()
+    {
+        if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
+            $db =& $GLOBALS['_MDB2_databases'][$this->db_index];
+            if (isset($db->datatype->lobs[$this->lob_index])) {
+                $db->datatype->_destroyLOB($db->datatype->lobs[$this->lob_index]);
+                unset($db->datatype->lobs[$this->lob_index]);
+            }
+        }
+    }
+    // }}}
+}
+
+// register streams wrapper
+if (!stream_wrapper_register("MDB2LOB", "MDB2_LOB")) {
+    MDB2::raiseError();
+    return false;
+}
+
+?>
diff --git a/3rdparty/MDB2/Schema.php b/3rdparty/MDB2/Schema.php
new file mode 100644
index 0000000000000000000000000000000000000000..25818460a629b6a37f9d5c884429c2bbbb08d34b
--- /dev/null
+++ b/3rdparty/MDB2/Schema.php
@@ -0,0 +1,2763 @@
+<?php
+/**
+ * PHP version 4, 5
+ *
+ * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
+ * Stig. S. Bakken, Lukas Smith, Igor Feghali
+ * All rights reserved.
+ *
+ * MDB2_Schema enables users to maintain RDBMS independant schema files
+ * in XML that can be used to manipulate both data and database schemas
+ * 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, Igor Feghali 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: Lukas Smith <smith@pooteeweet.org>
+ * Author: Igor Feghali <ifeghali@php.net>
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @version  CVS: $Id: Schema.php,v 1.132 2009/02/22 21:43:22 ifeghali Exp $
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+
+// require_once('MDB2.php');
+
+define('MDB2_SCHEMA_DUMP_ALL',       0);
+define('MDB2_SCHEMA_DUMP_STRUCTURE', 1);
+define('MDB2_SCHEMA_DUMP_CONTENT',   2);
+
+/**
+ * If you add an error code here, make sure you also add a textual
+ * version of it in MDB2_Schema::errorMessage().
+ */
+
+define('MDB2_SCHEMA_ERROR',             -1);
+define('MDB2_SCHEMA_ERROR_PARSE',       -2);
+define('MDB2_SCHEMA_ERROR_VALIDATE',    -3);
+define('MDB2_SCHEMA_ERROR_UNSUPPORTED', -4);    // Driver does not support this function
+define('MDB2_SCHEMA_ERROR_INVALID',     -5);    // Invalid attribute value
+define('MDB2_SCHEMA_ERROR_WRITER',      -6);
+
+/**
+ * The database manager is a class that provides a set of database
+ * management services like installing, altering and dumping the data
+ * structures of databases.
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema extends PEAR
+{
+    // {{{ properties
+
+    var $db;
+
+    var $warnings = array();
+
+    var $options = array(
+        'fail_on_invalid_names' => true,
+        'dtd_file'              => false,
+        'valid_types'           => array(),
+        'force_defaults'        => true,
+        'parser'                => 'MDB2_Schema_Parser',
+        'writer'                => 'MDB2_Schema_Writer',
+        'validate'              => 'MDB2_Schema_Validate',
+        'drop_missing_tables'   => false
+    );
+
+    // }}}
+    // {{{ apiVersion()
+
+    /**
+     * Return the MDB2 API version
+     *
+     * @return string  the MDB2 API version number
+     * @access public
+     */
+    function apiVersion()
+    {
+        return '0.4.3';
+    }
+
+    // }}}
+    // {{{ arrayMergeClobber()
+
+    /**
+     * Clobbers two arrays together
+     *
+     * @param array $a1 array that should be clobbered
+     * @param array $a2 array that should be clobbered
+     *
+     * @return array|false  array on success and false on error
+     *
+     * @access public
+     * @author kc@hireability.com
+     */
+    function arrayMergeClobber($a1, $a2)
+    {
+        if (!is_array($a1) || !is_array($a2)) {
+            return false;
+        }
+        foreach ($a2 as $key => $val) {
+            if (is_array($val) && array_key_exists($key, $a1) && is_array($a1[$key])) {
+                $a1[$key] = MDB2_Schema::arrayMergeClobber($a1[$key], $val);
+            } else {
+                $a1[$key] = $val;
+            }
+        }
+        return $a1;
+    }
+
+    // }}}
+    // {{{ resetWarnings()
+
+    /**
+     * reset the warning array
+     *
+     * @access public
+     * @return void
+     */
+    function resetWarnings()
+    {
+        $this->warnings = array();
+    }
+
+    // }}}
+    // {{{ getWarnings()
+
+    /**
+     * Get all warnings in reverse order
+     *
+     * This means that the last warning is the first element in the array
+     *
+     * @return array with warnings
+     * @access public
+     * @see resetWarnings()
+     */
+    function getWarnings()
+    {
+        return array_reverse($this->warnings);
+    }
+
+    // }}}
+    // {{{ setOption()
+
+    /**
+     * Sets the option for the db class
+     *
+     * @param string $option option name
+     * @param mixed  $value  value for the option
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function setOption($option, $value)
+    {
+        if (isset($this->options[$option])) {
+            if (is_null($value)) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                    'may not set an option to value null');
+            }
+            $this->options[$option] = $value;
+            return MDB2_OK;
+        }
+        return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
+            "unknown option $option");
+    }
+
+    // }}}
+    // {{{ getOption()
+
+    /**
+     * returns the value of an option
+     *
+     * @param string $option option name
+     *
+     * @return mixed the option value or error object
+     * @access public
+     */
+    function getOption($option)
+    {
+        if (isset($this->options[$option])) {
+            return $this->options[$option];
+        }
+        return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED,
+            null, null, "unknown option $option");
+    }
+
+    // }}}
+    // {{{ factory()
+
+    /**
+     * Create a new MDB2 object for the specified database type
+     * type
+     *
+     * @param string|array|MDB2_Driver_Common &$db     'data source name', see the
+     *                                                 MDB2::parseDSN method for a description of the dsn format.
+     *                                                 Can also be specified as an array of the
+     *                                                 format returned by @see MDB2::parseDSN.
+     *                                                 Finally you can also pass an existing db object to be used.
+     * @param array                           $options An associative array of option names and their values.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     * @see     MDB2::parseDSN
+     */
+    static function factory(&$db, $options = array())
+    {
+        $obj =new MDB2_Schema();
+        $result = $obj->connect($db, $options);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        return $obj;
+    }
+
+    // }}}
+    // {{{ connect()
+
+    /**
+     * Create a new MDB2 connection object and connect to the specified
+     * database
+     *
+     * @param string|array|MDB2_Driver_Common &$db     'data source name', see the
+     *              MDB2::parseDSN method for a description of the dsn format.
+     *              Can also be specified as an array of the
+     *              format returned by MDB2::parseDSN.
+     *              Finally you can also pass an existing db object to be used.
+     * @param array                           $options An associative array of option names and their values.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     * @see    MDB2::parseDSN
+     */
+    function connect(&$db, $options = array())
+    {
+        $db_options = array();
+        if (is_array($options)) {
+            foreach ($options as $option => $value) {
+                if (array_key_exists($option, $this->options)) {
+                    $result = $this->setOption($option, $value);
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                } else {
+                    $db_options[$option] = $value;
+                }
+            }
+        }
+        $this->disconnect();
+        if (!MDB2::isConnection($db)) {
+            $db =MDB2::factory($db, $db_options);
+        }
+
+        if (PEAR::isError($db)) {
+            return $db;
+        }
+        $this->db =& $db;
+        $this->db->loadModule('Datatype');
+        $this->db->loadModule('Manager');
+        $this->db->loadModule('Reverse');
+        $this->db->loadModule('Function');
+        if (empty($this->options['valid_types'])) {
+            $this->options['valid_types'] = $this->db->datatype->getValidTypes();
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ disconnect()
+
+    /**
+     * Log out and disconnect from the database.
+     *
+     * @access public
+     * @return void
+     */
+    function disconnect()
+    {
+        if (MDB2::isConnection($this->db)) {
+            $this->db->disconnect();
+            unset($this->db);
+        }
+    }
+
+    // }}}
+    // {{{ parseDatabaseDefinition()
+
+    /**
+     * Parse a database definition from a file or an array
+     *
+     * @param string|array $schema                the database schema array or file name
+     * @param bool         $skip_unreadable       if non readable files should be skipped
+     * @param array        $variables             associative array that the defines the text string values
+     *                                            that are meant to be used to replace the variables that are
+     *                                            used in the schema description.
+     * @param bool         $fail_on_invalid_names make function fail on invalid names
+     * @param array        $structure             database structure definition
+     *
+     * @access public
+     * @return array
+     */
+    function parseDatabaseDefinition($schema, $skip_unreadable = false, $variables = array(),
+        $fail_on_invalid_names = true, $structure = false)
+    {
+        $database_definition = false;
+        if (is_string($schema)) {
+            // if $schema is not readable then we just skip it
+            // and simply copy the $current_schema file to that file name
+            if (is_readable($schema)) {
+                $database_definition = $this->parseDatabaseDefinitionFile($schema, $variables, $fail_on_invalid_names, $structure);
+            }
+        } elseif (is_array($schema)) {
+            $database_definition = $schema;
+        }
+        if (!$database_definition && !$skip_unreadable) {
+            $database_definition = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                'invalid data type of schema or unreadable data source');
+        }
+        return $database_definition;
+    }
+
+    // }}}
+    // {{{ parseDatabaseDefinitionFile()
+
+    /**
+     * Parse a database definition file by creating a schema format
+     * parser object and passing the file contents as parser input data stream.
+     *
+     * @param string $input_file            the database schema file.
+     * @param array  $variables             associative array that the defines the text string values
+     *                                      that are meant to be used to replace the variables that are
+     *                                      used in the schema description.
+     * @param bool   $fail_on_invalid_names make function fail on invalid names
+     * @param array  $structure             database structure definition
+     *
+     * @access public
+     * @return array
+     */
+    function parseDatabaseDefinitionFile($input_file, $variables = array(),
+        $fail_on_invalid_names = true, $structure = false)
+    {
+        $dtd_file = $this->options['dtd_file'];
+        if ($dtd_file) {
+            include_once 'XML/DTD/XmlValidator.php';
+            $dtd =new XML_DTD_XmlValidator;
+            if (!$dtd->isValid($dtd_file, $input_file)) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_PARSE, null, null, $dtd->getMessage());
+            }
+        }
+
+        $class_name = $this->options['parser'];
+
+        $result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $parser =new $class_name($variables, $fail_on_invalid_names, $structure, $this->options['valid_types'], $this->options['force_defaults']);
+        $result = $parser->setInputFile($input_file);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $result = $parser->parse();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+        if (PEAR::isError($parser->error)) {
+            return $parser->error;
+        }
+
+        return $parser->database_definition;
+    }
+
+    // }}}
+    // {{{ getDefinitionFromDatabase()
+
+    /**
+     * Attempt to reverse engineer a schema structure from an existing MDB2
+     * This method can be used if no xml schema file exists yet.
+     * The resulting xml schema file may need some manual adjustments.
+     *
+     * @return array|MDB2_Error array with definition or error object
+     * @access public
+     */
+    function getDefinitionFromDatabase()
+    {
+        $database = $this->db->database_name;
+        if (empty($database)) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                'it was not specified a valid database name');
+        }
+        $class_name = $this->options['validate'];
+
+        $result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $val =new $class_name($this->options['fail_on_invalid_names'], $this->options['valid_types'], $this->options['force_defaults']);
+
+        $database_definition = array(
+            'name' => $database,
+            'create' => true,
+            'overwrite' => false,
+            'charset' => 'utf8',
+            'description' => '',
+            'comments' => '',
+            'tables' => array(),
+            'sequences' => array(),
+        );
+
+        $tables = $this->db->manager->listTables();
+        if (PEAR::isError($tables)) {
+            return $tables;
+        }
+
+        foreach ($tables as $table_name) {
+            $fields = $this->db->manager->listTableFields($table_name);
+            if (PEAR::isError($fields)) {
+                return $fields;
+            }
+
+            $database_definition['tables'][$table_name] = array(
+                'was' => '',
+                'description' => '',
+                'comments' => '',
+                'fields' => array(),
+                'indexes' => array(),
+                'constraints' => array(),
+                'initialization' => array()
+            );
+
+            $table_definition =& $database_definition['tables'][$table_name];
+            foreach ($fields as $field_name) {
+                $definition = $this->db->reverse->getTableFieldDefinition($table_name, $field_name);
+                if (PEAR::isError($definition)) {
+                    return $definition;
+                }
+
+                if (!empty($definition[0]['autoincrement'])) {
+                    $definition[0]['default'] = '0';
+                }
+
+                $table_definition['fields'][$field_name] = $definition[0];
+
+                $field_choices = count($definition);
+                if ($field_choices > 1) {
+                    $warning = "There are $field_choices type choices in the table $table_name field $field_name (#1 is the default): ";
+
+                    $field_choice_cnt = 1;
+
+                    $table_definition['fields'][$field_name]['choices'] = array();
+                    foreach ($definition as $field_choice) {
+                        $table_definition['fields'][$field_name]['choices'][] = $field_choice;
+
+                        $warning .= 'choice #'.($field_choice_cnt).': '.serialize($field_choice);
+                        $field_choice_cnt++;
+                    }
+                    $this->warnings[] = $warning;
+                }
+
+                /**
+                 * The first parameter is used to verify if there are duplicated
+                 * fields which we can guarantee that won't happen when reverse engineering
+                 */
+                $result = $val->validateField(array(), $table_definition['fields'][$field_name], $field_name);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+
+            $keys = array();
+
+            $indexes = $this->db->manager->listTableIndexes($table_name);
+            if (PEAR::isError($indexes)) {
+                return $indexes;
+            }
+
+            if (is_array($indexes)) {
+                foreach ($indexes as $index_name) {
+                    $this->db->expectError(MDB2_ERROR_NOT_FOUND);
+                    $definition = $this->db->reverse->getTableIndexDefinition($table_name, $index_name);
+                    $this->db->popExpect();
+                    if (PEAR::isError($definition)) {
+                        if (PEAR::isError($definition, MDB2_ERROR_NOT_FOUND)) {
+                            continue;
+                        }
+                        return $definition;
+                    }
+
+                    $keys[$index_name] = $definition;
+                }
+            }
+
+            $constraints = $this->db->manager->listTableConstraints($table_name);
+            if (PEAR::isError($constraints)) {
+                return $constraints;
+            }
+
+            if (is_array($constraints)) {
+                foreach ($constraints as $constraint_name) {
+                    $this->db->expectError(MDB2_ERROR_NOT_FOUND);
+                    $definition = $this->db->reverse->getTableConstraintDefinition($table_name, $constraint_name);
+                    $this->db->popExpect();
+                    if (PEAR::isError($definition)) {
+                        if (PEAR::isError($definition, MDB2_ERROR_NOT_FOUND)) {
+                            continue;
+                        }
+                        return $definition;
+                    }
+
+                    $keys[$constraint_name] = $definition;
+                }
+            }
+
+            foreach ($keys as $key_name => $definition) {
+                if (array_key_exists('foreign', $definition)
+                    && $definition['foreign']
+                ) {
+                    /**
+                     * The first parameter is used to verify if there are duplicated
+                     * foreign keys which we can guarantee that won't happen when reverse engineering
+                     */
+                    $result = $val->validateConstraint(array(), $definition, $key_name);
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+
+                    foreach ($definition['fields'] as $field_name => $field) {
+                        /**
+                         * The first parameter is used to verify if there are duplicated
+                         * referencing fields which we can guarantee that won't happen when reverse engineering
+                         */
+                        $result = $val->validateConstraintField(array(), $field_name);
+                        if (PEAR::isError($result)) {
+                            return $result;
+                        }
+
+                        $definition['fields'][$field_name] = '';
+                    }
+
+                    foreach ($definition['references']['fields'] as $field_name => $field) {
+                        /**
+                         * The first parameter is used to verify if there are duplicated
+                         * referenced fields which we can guarantee that won't happen when reverse engineering
+                         */
+                        $result = $val->validateConstraintReferencedField(array(), $field_name);
+                        if (PEAR::isError($result)) {
+                            return $result;
+                        }
+
+                        $definition['references']['fields'][$field_name] = '';
+                    }
+
+                    $table_definition['constraints'][$key_name] = $definition;
+                } else {
+                    /**
+                     * The first parameter is used to verify if there are duplicated
+                     * indices which we can guarantee that won't happen when reverse engineering
+                     */
+                    $result = $val->validateIndex(array(), $definition, $key_name);
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+
+                    foreach ($definition['fields'] as $field_name => $field) {
+                        /**
+                         * The first parameter is used to verify if there are duplicated
+                         * index fields which we can guarantee that won't happen when reverse engineering
+                         */
+                        $result = $val->validateIndexField(array(), $field, $field_name);
+                        if (PEAR::isError($result)) {
+                            return $result;
+                        }
+
+                        $definition['fields'][$field_name] = $field;
+                    }
+
+                    $table_definition['indexes'][$key_name] = $definition;
+                }
+            }
+
+            /**
+             * The first parameter is used to verify if there are duplicated
+             * tables which we can guarantee that won't happen when reverse engineering
+             */
+            $result = $val->validateTable(array(), $table_definition, $table_name);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+
+        }
+
+        $sequences = $this->db->manager->listSequences();
+        if (PEAR::isError($sequences)) {
+            return $sequences;
+        }
+
+        if (is_array($sequences)) {
+            foreach ($sequences as $sequence_name) {
+                $definition = $this->db->reverse->getSequenceDefinition($sequence_name);
+                if (PEAR::isError($definition)) {
+                    return $definition;
+                }
+                if (isset($database_definition['tables'][$sequence_name])
+                    && isset($database_definition['tables'][$sequence_name]['indexes'])
+                ) {
+                    foreach ($database_definition['tables'][$sequence_name]['indexes'] as $index) {
+                        if (isset($index['primary']) && $index['primary']
+                            && count($index['fields'] == 1)
+                        ) {
+                            $definition['on'] = array(
+                                'table' => $sequence_name,
+                                'field' => key($index['fields']),
+                            );
+                            break;
+                        }
+                    }
+                }
+
+                /**
+                 * The first parameter is used to verify if there are duplicated
+                 * sequences which we can guarantee that won't happen when reverse engineering
+                 */
+                $result = $val->validateSequence(array(), $definition, $sequence_name);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+
+                $database_definition['sequences'][$sequence_name] = $definition;
+            }
+        }
+
+        $result = $val->validateDatabase($database_definition);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        return $database_definition;
+    }
+
+    // }}}
+    // {{{ createTableIndexes()
+
+    /**
+     * A method to create indexes for an existing table
+     *
+     * @param string  $table_name Name of the table
+     * @param array   $indexes    An array of indexes to be created
+     * @param boolean $overwrite  If the table/index should be overwritten if it already exists
+     *
+     * @return mixed  MDB2_Error if there is an error creating an index, MDB2_OK otherwise
+     * @access public
+     */
+    function createTableIndexes($table_name, $indexes, $overwrite = false)
+    {
+        if (!$this->db->supports('indexes')) {
+            $this->db->debug('Indexes are not supported', __FUNCTION__);
+            return MDB2_OK;
+        }
+
+        $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
+        foreach ($indexes as $index_name => $index) {
+
+            // Does the index already exist, and if so, should it be overwritten?
+            $create_index = true;
+            $this->db->expectError($errorcodes);
+            if (!empty($index['primary']) || !empty($index['unique'])) {
+                $current_indexes = $this->db->manager->listTableConstraints($table_name);
+            } else {
+                $current_indexes = $this->db->manager->listTableIndexes($table_name);
+            }
+
+            $this->db->popExpect();
+            if (PEAR::isError($current_indexes)) {
+                if (!MDB2::isError($current_indexes, $errorcodes)) {
+                    return $current_indexes;
+                }
+            } elseif (is_array($current_indexes) && in_array($index_name, $current_indexes)) {
+                if (!$overwrite) {
+                    $this->db->debug('Index already exists: '.$index_name, __FUNCTION__);
+                    $create_index = false;
+                } else {
+                    $this->db->debug('Preparing to overwrite index: '.$index_name, __FUNCTION__);
+
+                    $this->db->expectError(MDB2_ERROR_NOT_FOUND);
+                    if (!empty($index['primary']) || !empty($index['unique'])) {
+                        $result = $this->db->manager->dropConstraint($table_name, $index_name);
+                    } else {
+                        $result = $this->db->manager->dropIndex($table_name, $index_name);
+                    }
+                    $this->db->popExpect();
+                    if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_NOT_FOUND)) {
+                        return $result;
+                    }
+                }
+            }
+
+            // Check if primary is being used and if it's supported
+            if (!empty($index['primary']) && !$this->db->supports('primary_key')) {
+
+                // Primary not supported so we fallback to UNIQUE and making the field NOT NULL
+                $index['unique'] = true;
+
+                $changes = array();
+
+                foreach ($index['fields'] as $field => $empty) {
+                    $field_info = $this->db->reverse->getTableFieldDefinition($table_name, $field);
+                    if (PEAR::isError($field_info)) {
+                        return $field_info;
+                    }
+                    if (!$field_info[0]['notnull']) {
+                        $changes['change'][$field] = $field_info[0];
+
+                        $changes['change'][$field]['notnull'] = true;
+                    }
+                }
+                if (!empty($changes)) {
+                    $this->db->manager->alterTable($table_name, $changes, false);
+                }
+            }
+
+            // Should the index be created?
+            if ($create_index) {
+                if (!empty($index['primary']) || !empty($index['unique'])) {
+                    $result = $this->db->manager->createConstraint($table_name, $index_name, $index);
+                } else {
+                    $result = $this->db->manager->createIndex($table_name, $index_name, $index);
+                }
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ createTableConstraints()
+
+    /**
+     * A method to create foreign keys for an existing table
+     *
+     * @param string  $table_name  Name of the table
+     * @param array   $constraints An array of foreign keys to be created
+     * @param boolean $overwrite   If the foreign key should be overwritten if it already exists
+     *
+     * @return mixed  MDB2_Error if there is an error creating a foreign key, MDB2_OK otherwise
+     * @access public
+     */
+    function createTableConstraints($table_name, $constraints, $overwrite = false)
+    {
+        if (!$this->db->supports('indexes')) {
+            $this->db->debug('Indexes are not supported', __FUNCTION__);
+            return MDB2_OK;
+        }
+
+        $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
+        foreach ($constraints as $constraint_name => $constraint) {
+
+            // Does the foreign key already exist, and if so, should it be overwritten?
+            $create_constraint = true;
+            $this->db->expectError($errorcodes);
+            $current_constraints = $this->db->manager->listTableConstraints($table_name);
+            $this->db->popExpect();
+            if (PEAR::isError($current_constraints)) {
+                if (!MDB2::isError($current_constraints, $errorcodes)) {
+                    return $current_constraints;
+                }
+            } elseif (is_array($current_constraints) && in_array($constraint_name, $current_constraints)) {
+                if (!$overwrite) {
+                    $this->db->debug('Foreign key already exists: '.$constraint_name, __FUNCTION__);
+                    $create_constraint = false;
+                } else {
+                    $this->db->debug('Preparing to overwrite foreign key: '.$constraint_name, __FUNCTION__);
+                    $result = $this->db->manager->dropConstraint($table_name, $constraint_name);
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                }
+            }
+
+            // Should the foreign key be created?
+            if ($create_constraint) {
+                $result = $this->db->manager->createConstraint($table_name, $constraint_name, $constraint);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ createTable()
+
+    /**
+     * Create a table and inititialize the table if data is available
+     *
+     * @param string $table_name name of the table to be created
+     * @param array  $table      multi dimensional array that contains the
+     *                           structure and optional data of the table
+     * @param bool   $overwrite  if the table/index should be overwritten if it already exists
+     * @param array  $options    an array of options to be passed to the database specific driver
+     *                           version of MDB2_Driver_Manager_Common::createTable().
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function createTable($table_name, $table, $overwrite = false, $options = array())
+    {
+        $create = true;
+
+        $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
+
+        $this->db->expectError($errorcodes);
+
+        $tables = $this->db->manager->listTables();
+
+        $this->db->popExpect();
+        if (PEAR::isError($tables)) {
+            if (!MDB2::isError($tables, $errorcodes)) {
+                return $tables;
+            }
+        } elseif (is_array($tables) && in_array($table_name, $tables)) {
+            if (!$overwrite) {
+                $create = false;
+                $this->db->debug('Table already exists: '.$table_name, __FUNCTION__);
+            } else {
+                $result = $this->db->manager->dropTable($table_name);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $this->db->debug('Overwritting table: '.$table_name, __FUNCTION__);
+            }
+        }
+
+        if ($create) {
+            $result = $this->db->manager->createTable($table_name, $table['fields'], $options);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        if (!empty($table['initialization']) && is_array($table['initialization'])) {
+            $result = $this->initializeTable($table_name, $table);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        if (!empty($table['indexes']) && is_array($table['indexes'])) {
+            $result = $this->createTableIndexes($table_name, $table['indexes'], $overwrite);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        if (!empty($table['constraints']) && is_array($table['constraints'])) {
+            $result = $this->createTableConstraints($table_name, $table['constraints'], $overwrite);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ initializeTable()
+
+    /**
+     * Inititialize the table with data
+     *
+     * @param string $table_name name of the table
+     * @param array  $table      multi dimensional array that contains the
+     *                           structure and optional data of the table
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function initializeTable($table_name, $table)
+    {
+        $query_insertselect = 'INSERT INTO %s (%s) (SELECT %s FROM %s %s)';
+
+        $query_insert = 'INSERT INTO %s (%s) VALUES (%s)';
+        $query_update = 'UPDATE %s SET %s %s';
+        $query_delete = 'DELETE FROM %s %s';
+
+        $table_name = $this->db->quoteIdentifier($table_name, true);
+
+        $result = MDB2_OK;
+
+        $support_transactions = $this->db->supports('transactions');
+
+        foreach ($table['initialization'] as $instruction) {
+            $query = '';
+            switch ($instruction['type']) {
+            case 'insert':
+                if (!isset($instruction['data']['select'])) {
+                    $data = $this->getInstructionFields($instruction['data'], $table['fields']);
+                    if (!empty($data)) {
+                        $fields = implode(', ', array_keys($data));
+                        $values = implode(', ', array_values($data));
+
+                        $query = sprintf($query_insert, $table_name, $fields, $values);
+                    }
+                } else {
+                    $data  = $this->getInstructionFields($instruction['data']['select'], $table['fields']);
+                    $where = $this->getInstructionWhere($instruction['data']['select'], $table['fields']);
+
+                    $select_table_name = $this->db->quoteIdentifier($instruction['data']['select']['table'], true);
+                    if (!empty($data)) {
+                        $fields = implode(', ', array_keys($data));
+                        $values = implode(', ', array_values($data));
+
+                        $query = sprintf($query_insertselect, $table_name, $fields, $values, $select_table_name, $where);
+                    }
+                }
+                break;
+            case 'update':
+                $data  = $this->getInstructionFields($instruction['data'], $table['fields']);
+                $where = $this->getInstructionWhere($instruction['data'], $table['fields']);
+                if (!empty($data)) {
+                    array_walk($data, array($this, 'buildFieldValue'));
+                    $fields_values = implode(', ', $data);
+
+                    $query = sprintf($query_update, $table_name, $fields_values, $where);
+                }
+                break;
+            case 'delete':
+                $where = $this->getInstructionWhere($instruction['data'], $table['fields']);
+                $query = sprintf($query_delete, $table_name, $where);
+                break;
+            }
+            if ($query) {
+                if ($support_transactions && PEAR::isError($res = $this->db->beginNestedTransaction())) {
+                    return $res;
+                }
+
+                $result = $this->db->exec($query);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+
+                if ($support_transactions && PEAR::isError($res = $this->db->completeNestedTransaction())) {
+                    return $res;
+                }
+            }
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ buildFieldValue()
+
+    /**
+     * Appends the contents of second argument + '=' to the beginning of first
+     * argument.
+     *
+     * Used with array_walk() in initializeTable() for UPDATEs.
+     *
+     * @param string &$element value of array's element
+     * @param string $key      key of array's element
+     *
+     * @return void
+     *
+     * @access public
+     * @see MDB2_Schema::initializeTable()
+     */
+    function buildFieldValue(&$element, $key)
+    {
+        $element = $key."=$element";
+    }
+
+    // }}}
+    // {{{ getExpression()
+
+    /**
+     * Generates a string that represents a value that would be associated
+     * with a column in a DML instruction.
+     *
+     * @param array  $element           multi dimensional array that contains the
+     *                                  structure of the current DML instruction.
+     * @param array  $fields_definition multi dimensional array that contains the
+     *                                  definition for current table's fields
+     * @param string $type              type of given field
+     *
+     * @return string
+     *
+     * @access public
+     * @see MDB2_Schema::getInstructionFields(), MDB2_Schema::getInstructionWhere()
+     */
+    function getExpression($element, $fields_definition = array(), $type = null)
+    {
+        $str = '';
+        switch ($element['type']) {
+        case 'null':
+            $str .= 'NULL';
+            break;
+        case 'value':
+            $str .= $this->db->quote($element['data'], $type);
+            break;
+        case 'column':
+            $str .= $this->db->quoteIdentifier($element['data'], true);
+            break;
+        case 'function':
+            $arguments = array();
+            if (!empty($element['data']['arguments'])
+                && is_array($element['data']['arguments'])
+            ) {
+                foreach ($element['data']['arguments'] as $v) {
+                    $arguments[] = $this->getExpression($v, $fields_definition);
+                }
+            }
+            if (method_exists($this->db->function, $element['data']['name'])) {
+                $user_func = array(&$this->db->function, $element['data']['name']);
+
+                $str .= call_user_func_array($user_func, $arguments);
+            } else {
+                $str .= $element['data']['name'].'(';
+                $str .= implode(', ', $arguments);
+                $str .= ')';
+            }
+            break;
+        case 'expression':
+            $type0 = $type1 = null;
+            if ($element['data']['operants'][0]['type'] == 'column'
+                && array_key_exists($element['data']['operants'][0]['data'], $fields_definition)
+            ) {
+                $type0 = $fields_definition[$element['data']['operants'][0]['data']]['type'];
+            }
+
+            if ($element['data']['operants'][1]['type'] == 'column'
+                && array_key_exists($element['data']['operants'][1]['data'], $fields_definition)
+            ) {
+                $type1 = $fields_definition[$element['data']['operants'][1]['data']]['type'];
+            }
+
+            $str .= '(';
+            $str .= $this->getExpression($element['data']['operants'][0], $fields_definition, $type1);
+            $str .= $this->getOperator($element['data']['operator']);
+            $str .= $this->getExpression($element['data']['operants'][1], $fields_definition, $type0);
+            $str .= ')';
+            break;
+        }
+
+        return $str;
+    }
+
+    // }}}
+    // {{{ getOperator()
+
+    /**
+     * Returns the matching SQL operator
+     *
+     * @param string $op parsed descriptive operator
+     *
+     * @return string matching SQL operator
+     *
+     * @access public
+     * @static
+     * @see MDB2_Schema::getExpression()
+     */
+    function getOperator($op)
+    {
+        switch ($op) {
+        case 'PLUS':
+            return ' + ';
+        case 'MINUS':
+            return ' - ';
+        case 'TIMES':
+            return ' * ';
+        case 'DIVIDED':
+            return ' / ';
+        case 'EQUAL':
+            return ' = ';
+        case 'NOT EQUAL':
+            return ' != ';
+        case 'LESS THAN':
+            return ' < ';
+        case 'GREATER THAN':
+            return ' > ';
+        case 'LESS THAN OR EQUAL':
+            return ' <= ';
+        case 'GREATER THAN OR EQUAL':
+            return ' >= ';
+        default:
+            return ' '.$op.' ';
+        }
+    }
+
+    // }}}
+    // {{{ getInstructionFields()
+
+    /**
+     * Walks the parsed DML instruction array, field by field,
+     * storing them and their processed values inside a new array.
+     *
+     * @param array $instruction       multi dimensional array that contains the
+     *                                 structure of the current DML instruction.
+     * @param array $fields_definition multi dimensional array that contains the
+     *                                 definition for current table's fields
+     *
+     * @return array  array of strings in the form 'field_name' => 'value'
+     *
+     * @access public
+     * @static
+     * @see MDB2_Schema::initializeTable()
+     */
+    function getInstructionFields($instruction, $fields_definition = array())
+    {
+        $fields = array();
+        if (!empty($instruction['field']) && is_array($instruction['field'])) {
+            foreach ($instruction['field'] as $field) {
+                $field_name = $this->db->quoteIdentifier($field['name'], true);
+
+                $fields[$field_name] = $this->getExpression($field['group'], $fields_definition);
+            }
+        }
+        return $fields;
+    }
+
+    // }}}
+    // {{{ getInstructionWhere()
+
+    /**
+     * Translates the parsed WHERE expression of a DML instruction
+     * (array structure) to a SQL WHERE clause (string).
+     *
+     * @param array $instruction       multi dimensional array that contains the
+     *                                 structure of the current DML instruction.
+     * @param array $fields_definition multi dimensional array that contains the
+     *                                 definition for current table's fields.
+     *
+     * @return string SQL WHERE clause
+     *
+     * @access public
+     * @static
+     * @see MDB2_Schema::initializeTable()
+     */
+    function getInstructionWhere($instruction, $fields_definition = array())
+    {
+        $where = '';
+        if (!empty($instruction['where'])) {
+            $where = 'WHERE '.$this->getExpression($instruction['where'], $fields_definition);
+        }
+        return $where;
+    }
+
+    // }}}
+    // {{{ createSequence()
+
+    /**
+     * Create a sequence
+     *
+     * @param string $sequence_name name of the sequence to be created
+     * @param array  $sequence      multi dimensional array that contains the
+     *                              structure and optional data of the table
+     * @param bool   $overwrite     if the sequence should be overwritten if it already exists
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function createSequence($sequence_name, $sequence, $overwrite = false)
+    {
+        if (!$this->db->supports('sequences')) {
+            $this->db->debug('Sequences are not supported', __FUNCTION__);
+            return MDB2_OK;
+        }
+
+        $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
+        $this->db->expectError($errorcodes);
+        $sequences = $this->db->manager->listSequences();
+        $this->db->popExpect();
+        if (PEAR::isError($sequences)) {
+            if (!MDB2::isError($sequences, $errorcodes)) {
+                return $sequences;
+            }
+        } elseif (is_array($sequence) && in_array($sequence_name, $sequences)) {
+            if (!$overwrite) {
+                $this->db->debug('Sequence already exists: '.$sequence_name, __FUNCTION__);
+                return MDB2_OK;
+            }
+
+            $result = $this->db->manager->dropSequence($sequence_name);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+            $this->db->debug('Overwritting sequence: '.$sequence_name, __FUNCTION__);
+        }
+
+        $start = 1;
+        $field = '';
+        if (!empty($sequence['on'])) {
+            $table = $sequence['on']['table'];
+            $field = $sequence['on']['field'];
+
+            $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NOT_CAPABLE);
+            $this->db->expectError($errorcodes);
+            $tables = $this->db->manager->listTables();
+            $this->db->popExpect();
+            if (PEAR::isError($tables) && !MDB2::isError($tables, $errorcodes)) {
+                 return $tables;
+            }
+
+            if (!PEAR::isError($tables) && is_array($tables) && in_array($table, $tables)) {
+                if ($this->db->supports('summary_functions')) {
+                    $query = "SELECT MAX($field) FROM ".$this->db->quoteIdentifier($table, true);
+                } else {
+                    $query = "SELECT $field FROM ".$this->db->quoteIdentifier($table, true)." ORDER BY $field DESC";
+                }
+                $start = $this->db->queryOne($query, 'integer');
+                if (PEAR::isError($start)) {
+                    return $start;
+                }
+                ++$start;
+            } else {
+                $this->warnings[] = 'Could not sync sequence: '.$sequence_name;
+            }
+        } elseif (!empty($sequence['start']) && is_numeric($sequence['start'])) {
+            $start = $sequence['start'];
+            $table = '';
+        }
+
+        $result = $this->db->manager->createSequence($sequence_name, $start);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ createDatabase()
+
+    /**
+     * Create a database space within which may be created database objects
+     * like tables, indexes and sequences. The implementation of this function
+     * is highly DBMS specific and may require special permissions to run
+     * successfully. Consult the documentation or the DBMS drivers that you
+     * use to be aware of eventual configuration requirements.
+     *
+     * @param array $database_definition multi dimensional array that contains the current definition
+     * @param array $options             an array of options to be passed to the 
+     *                                   database specific driver version of
+     *                                   MDB2_Driver_Manager_Common::createTable().
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function createDatabase($database_definition, $options = array())
+    {
+        if (!isset($database_definition['name']) || !$database_definition['name']) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                'no valid database name specified');
+        }
+
+        $create    = (isset($database_definition['create']) && $database_definition['create']);
+        $overwrite = (isset($database_definition['overwrite']) && $database_definition['overwrite']);
+
+        /**
+         *
+         * We need to clean up database name before any query to prevent
+         * database driver from using a inexistent database
+         *
+         */
+        $previous_database_name = $this->db->setDatabase('');
+
+        // Lower / Upper case the db name if the portability deems so.
+        if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
+            $func = $this->db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper';
+
+            $db_name = $func($database_definition['name']);
+        } else {
+            $db_name = $database_definition['name'];
+        }
+
+        if ($create) {
+
+            $dbExists = $this->db->databaseExists($db_name);
+            if (PEAR::isError($dbExists)) {
+                return $dbExists;
+            }
+
+            if ($dbExists && $overwrite) {
+                $this->db->expectError(MDB2_ERROR_CANNOT_DROP);
+                $result = $this->db->manager->dropDatabase($db_name);
+                $this->db->popExpect();
+                if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_CANNOT_DROP)) {
+                    return $result;
+                }
+                $dbExists = false;
+                $this->db->debug('Overwritting database: ' . $db_name, __FUNCTION__);
+            }
+
+            $dbOptions = array();
+            if (array_key_exists('charset', $database_definition)
+                && !empty($database_definition['charset'])) {
+                $dbOptions['charset'] = $database_definition['charset'];
+            }
+
+            if ($dbExists) {
+                $this->db->debug('Database already exists: ' . $db_name, __FUNCTION__);
+//                 if (!empty($dbOptions)) {
+//                     $errorcodes = array(MDB2_ERROR_UNSUPPORTED, MDB2_ERROR_NO_PERMISSION);
+//                     $this->db->expectError($errorcodes);
+//                     $result = $this->db->manager->alterDatabase($db_name, $dbOptions);
+//                     $this->db->popExpect();
+//                     if (PEAR::isError($result) && !MDB2::isError($result, $errorcodes)) {
+//                         return $result;
+//                     }
+//                 }
+                $create = false;
+            } else {
+                $this->db->expectError(MDB2_ERROR_UNSUPPORTED);
+                $result = $this->db->manager->createDatabase($db_name, $dbOptions);
+                $this->db->popExpect();
+                if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_UNSUPPORTED)) {
+                    return $result;
+                }
+                $this->db->debug('Creating database: ' . $db_name, __FUNCTION__);
+            }
+        }
+
+        $this->db->setDatabase($db_name);
+        if (($support_transactions = $this->db->supports('transactions'))
+            && PEAR::isError($result = $this->db->beginNestedTransaction())
+        ) {
+            return $result;
+        }
+
+        $created_objects = 0;
+        if (isset($database_definition['tables'])
+            && is_array($database_definition['tables'])
+        ) {
+            foreach ($database_definition['tables'] as $table_name => $table) {
+                $result = $this->createTable($table_name, $table, $overwrite, $options);
+                if (PEAR::isError($result)) {
+                    break;
+                }
+                $created_objects++;
+            }
+        }
+        if (!PEAR::isError($result)
+            && isset($database_definition['sequences'])
+            && is_array($database_definition['sequences'])
+        ) {
+            foreach ($database_definition['sequences'] as $sequence_name => $sequence) {
+                $result = $this->createSequence($sequence_name, $sequence, false, $overwrite);
+
+                if (PEAR::isError($result)) {
+                    break;
+                }
+                $created_objects++;
+            }
+        }
+
+        if ($support_transactions) {
+            $res = $this->db->completeNestedTransaction();
+            if (PEAR::isError($res)) {
+                $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                    'Could not end transaction ('.
+                    $res->getMessage().' ('.$res->getUserinfo().'))');
+            }
+        } elseif (PEAR::isError($result) && $created_objects) {
+            $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                'the database was only partially created ('.
+                $result->getMessage().' ('.$result->getUserinfo().'))');
+        }
+
+        $this->db->setDatabase($previous_database_name);
+
+        if (PEAR::isError($result) && $create
+            && PEAR::isError($result2 = $this->db->manager->dropDatabase($db_name))
+        ) {
+            if (!MDB2::isError($result2, MDB2_ERROR_UNSUPPORTED)) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                       'Could not drop the created database after unsuccessful creation attempt ('.
+                       $result2->getMessage().' ('.$result2->getUserinfo().'))');
+            }
+        }
+
+        return $result;
+    }
+
+    // }}}
+    // {{{ compareDefinitions()
+
+    /**
+     * Compare a previous definition with the currently parsed definition
+     *
+     * @param array $current_definition  multi dimensional array that contains the current definition
+     * @param array $previous_definition multi dimensional array that contains the previous definition
+     *
+     * @return array|MDB2_Error array of changes on success, or a error object
+     * @access public
+     */
+    function compareDefinitions($current_definition, $previous_definition)
+    {
+        $changes = array();
+
+        if (!empty($current_definition['tables']) && is_array($current_definition['tables'])) {
+            $changes['tables'] = $defined_tables = array();
+            foreach ($current_definition['tables'] as $table_name => $table) {
+                $previous_tables = array();
+                if (!empty($previous_definition) && is_array($previous_definition)) {
+                    $previous_tables = $previous_definition['tables'];
+                }
+                $change = $this->compareTableDefinitions($table_name, $table, $previous_tables, $defined_tables);
+                if (PEAR::isError($change)) {
+                    return $change;
+                }
+                if (!empty($change)) {
+                    $changes['tables'] = MDB2_Schema::arrayMergeClobber($changes['tables'], $change);
+                }
+            }
+
+            if (!empty($previous_definition['tables'])
+                && is_array($previous_definition['tables'])) {
+                foreach ($previous_definition['tables'] as $table_name => $table) {
+                    if (empty($defined_tables[$table_name])) {
+                        $changes['tables']['remove'][$table_name] = true;
+                    }
+                }
+            }
+        }
+        if (!empty($current_definition['sequences']) && is_array($current_definition['sequences'])) {
+            $changes['sequences'] = $defined_sequences = array();
+            foreach ($current_definition['sequences'] as $sequence_name => $sequence) {
+                $previous_sequences = array();
+                if (!empty($previous_definition) && is_array($previous_definition)) {
+                    $previous_sequences = $previous_definition['sequences'];
+                }
+
+                $change = $this->compareSequenceDefinitions($sequence_name,
+                                                            $sequence,
+                                                            $previous_sequences,
+                                                            $defined_sequences);
+                if (PEAR::isError($change)) {
+                    return $change;
+                }
+                if (!empty($change)) {
+                    $changes['sequences'] = MDB2_Schema::arrayMergeClobber($changes['sequences'], $change);
+                }
+            }
+            if (!empty($previous_definition['sequences']) && is_array($previous_definition['sequences'])) {
+                foreach ($previous_definition['sequences'] as $sequence_name => $sequence) {
+                    if (empty($defined_sequences[$sequence_name])) {
+                        $changes['sequences']['remove'][$sequence_name] = true;
+                    }
+                }
+            }
+        }
+        return $changes;
+    }
+
+    // }}}
+    // {{{ compareTableFieldsDefinitions()
+
+    /**
+     * Compare a previous definition with the currently parsed definition
+     *
+     * @param string $table_name          name of the table
+     * @param array  $current_definition  multi dimensional array that contains the current definition
+     * @param array  $previous_definition multi dimensional array that contains the previous definition
+     *
+     * @return array|MDB2_Error array of changes on success, or a error object
+     * @access public
+     */
+    function compareTableFieldsDefinitions($table_name, $current_definition,
+        $previous_definition)
+    {
+        $changes = $defined_fields = array();
+
+        if (is_array($current_definition)) {
+            foreach ($current_definition as $field_name => $field) {
+                $was_field_name = $field['was'];
+                if (!empty($previous_definition[$field_name])
+                    && (
+                        (isset($previous_definition[$field_name]['was'])
+                         && $previous_definition[$field_name]['was'] == $was_field_name)
+                        || !isset($previous_definition[$was_field_name])
+                       )) {
+                    $was_field_name = $field_name;
+                }
+
+                if (!empty($previous_definition[$was_field_name])) {
+                    if ($was_field_name != $field_name) {
+                        $changes['rename'][$was_field_name] = array('name' => $field_name, 'definition' => $field);
+                    }
+
+                    if (!empty($defined_fields[$was_field_name])) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                            'the field "'.$was_field_name.
+                            '" was specified for more than one field of table');
+                    }
+
+                    $defined_fields[$was_field_name] = true;
+
+                    $change = $this->db->compareDefinition($field, $previous_definition[$was_field_name]);
+                    if (PEAR::isError($change)) {
+                        return $change;
+                    }
+
+                    if (!empty($change)) {
+                        if (array_key_exists('default', $change)
+                            && $change['default']
+                            && !array_key_exists('default', $field)) {
+                                $field['default'] = null;
+                        }
+
+                        $change['definition'] = $field;
+
+                        $changes['change'][$field_name] = $change;
+                    }
+                } else {
+                    if ($field_name != $was_field_name) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                            'it was specified a previous field name ("'.
+                            $was_field_name.'") for field "'.$field_name.'" of table "'.
+                            $table_name.'" that does not exist');
+                    }
+
+                    $changes['add'][$field_name] = $field;
+                }
+            }
+        }
+
+        if (isset($previous_definition) && is_array($previous_definition)) {
+            foreach ($previous_definition as $field_previous_name => $field_previous) {
+                if (empty($defined_fields[$field_previous_name])) {
+                    $changes['remove'][$field_previous_name] = true;
+                }
+            }
+        }
+
+        return $changes;
+    }
+
+    // }}}
+    // {{{ compareTableIndexesDefinitions()
+
+    /**
+     * Compare a previous definition with the currently parsed definition
+     *
+     * @param string $table_name          name of the table
+     * @param array  $current_definition  multi dimensional array that contains the current definition
+     * @param array  $previous_definition multi dimensional array that contains the previous definition
+     *
+     * @return array|MDB2_Error array of changes on success, or a error object
+     * @access public
+     */
+    function compareTableIndexesDefinitions($table_name, $current_definition,
+        $previous_definition)
+    {
+        $changes = $defined_indexes = array();
+
+        if (is_array($current_definition)) {
+            foreach ($current_definition as $index_name => $index) {
+                $was_index_name = $index['was'];
+                if (!empty($previous_definition[$index_name])
+                    && isset($previous_definition[$index_name]['was'])
+                    && $previous_definition[$index_name]['was'] == $was_index_name
+                ) {
+                    $was_index_name = $index_name;
+                }
+                if (!empty($previous_definition[$was_index_name])) {
+                    $change = array();
+                    if ($was_index_name != $index_name) {
+                        $change['name'] = $was_index_name;
+                    }
+
+                    if (!empty($defined_indexes[$was_index_name])) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                            'the index "'.$was_index_name.'" was specified for'.
+                            ' more than one index of table "'.$table_name.'"');
+                    }
+                    $defined_indexes[$was_index_name] = true;
+
+                    $previous_unique = array_key_exists('unique', $previous_definition[$was_index_name])
+                        ? $previous_definition[$was_index_name]['unique'] : false;
+
+                    $unique = array_key_exists('unique', $index) ? $index['unique'] : false;
+                    if ($previous_unique != $unique) {
+                        $change['unique'] = $unique;
+                    }
+
+                    $previous_primary = array_key_exists('primary', $previous_definition[$was_index_name])
+                        ? $previous_definition[$was_index_name]['primary'] : false;
+
+                    $primary = array_key_exists('primary', $index) ? $index['primary'] : false;
+                    if ($previous_primary != $primary) {
+                        $change['primary'] = $primary;
+                    }
+
+                    $defined_fields  = array();
+                    $previous_fields = $previous_definition[$was_index_name]['fields'];
+                    if (!empty($index['fields']) && is_array($index['fields'])) {
+                        foreach ($index['fields'] as $field_name => $field) {
+                            if (!empty($previous_fields[$field_name])) {
+                                $defined_fields[$field_name] = true;
+
+                                $previous_sorting = array_key_exists('sorting', $previous_fields[$field_name])
+                                    ? $previous_fields[$field_name]['sorting'] : '';
+
+                                $sorting = array_key_exists('sorting', $field) ? $field['sorting'] : '';
+                                if ($previous_sorting != $sorting) {
+                                    $change['change'] = true;
+                                }
+                            } else {
+                                $change['change'] = true;
+                            }
+                        }
+                    }
+                    if (isset($previous_fields) && is_array($previous_fields)) {
+                        foreach ($previous_fields as $field_name => $field) {
+                            if (empty($defined_fields[$field_name])) {
+                                $change['change'] = true;
+                            }
+                        }
+                    }
+                    if (!empty($change)) {
+                        $changes['change'][$index_name] = $current_definition[$index_name];
+                    }
+                } else {
+                    if ($index_name != $was_index_name) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                            'it was specified a previous index name ("'.$was_index_name.
+                            ') for index "'.$index_name.'" of table "'.$table_name.'" that does not exist');
+                    }
+                    $changes['add'][$index_name] = $current_definition[$index_name];
+                }
+            }
+        }
+        foreach ($previous_definition as $index_previous_name => $index_previous) {
+            if (empty($defined_indexes[$index_previous_name])) {
+                $changes['remove'][$index_previous_name] = $index_previous;
+            }
+        }
+        return $changes;
+    }
+
+    // }}}
+    // {{{ compareTableDefinitions()
+
+    /**
+     * Compare a previous definition with the currently parsed definition
+     *
+     * @param string $table_name          name of the table
+     * @param array  $current_definition  multi dimensional array that contains the current definition
+     * @param array  $previous_definition multi dimensional array that contains the previous definition
+     * @param array  &$defined_tables     table names in the schema
+     *
+     * @return array|MDB2_Error array of changes on success, or a error object
+     * @access public
+     */
+    function compareTableDefinitions($table_name, $current_definition,
+        $previous_definition, &$defined_tables)
+    {
+        $changes = array();
+
+        if (is_array($current_definition)) {
+            $was_table_name = $table_name;
+            if (!empty($current_definition['was'])) {
+                $was_table_name = $current_definition['was'];
+            }
+            if (!empty($previous_definition[$was_table_name])) {
+                $changes['change'][$was_table_name] = array();
+                if ($was_table_name != $table_name) {
+                    $changes['change'][$was_table_name] = array('name' => $table_name);
+                }
+                if (!empty($defined_tables[$was_table_name])) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                        'the table "'.$was_table_name.
+                        '" was specified for more than one table of the database');
+                }
+                $defined_tables[$was_table_name] = true;
+                if (!empty($current_definition['fields']) && is_array($current_definition['fields'])) {
+                    $previous_fields = array();
+                    if (isset($previous_definition[$was_table_name]['fields'])
+                        && is_array($previous_definition[$was_table_name]['fields'])) {
+                        $previous_fields = $previous_definition[$was_table_name]['fields'];
+                    }
+
+                    $change = $this->compareTableFieldsDefinitions($table_name,
+                                                                   $current_definition['fields'],
+                                                                   $previous_fields);
+
+                    if (PEAR::isError($change)) {
+                        return $change;
+                    }
+                    if (!empty($change)) {
+                        $changes['change'][$was_table_name] =
+                            MDB2_Schema::arrayMergeClobber($changes['change'][$was_table_name], $change);
+                    }
+                }
+                if (!empty($current_definition['indexes']) && is_array($current_definition['indexes'])) {
+                    $previous_indexes = array();
+                    if (isset($previous_definition[$was_table_name]['indexes'])
+                        && is_array($previous_definition[$was_table_name]['indexes'])) {
+                        $previous_indexes = $previous_definition[$was_table_name]['indexes'];
+                    }
+                    $change = $this->compareTableIndexesDefinitions($table_name,
+                                                                    $current_definition['indexes'],
+                                                                    $previous_indexes);
+
+                    if (PEAR::isError($change)) {
+                        return $change;
+                    }
+                    if (!empty($change)) {
+                        $changes['change'][$was_table_name]['indexes'] = $change;
+                    }
+                }
+                if (empty($changes['change'][$was_table_name])) {
+                    unset($changes['change'][$was_table_name]);
+                }
+                if (empty($changes['change'])) {
+                    unset($changes['change']);
+                }
+            } else {
+                if ($table_name != $was_table_name) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                        'it was specified a previous table name ("'.$was_table_name.
+                        '") for table "'.$table_name.'" that does not exist');
+                }
+                $changes['add'][$table_name] = true;
+            }
+        }
+
+        return $changes;
+    }
+
+    // }}}
+    // {{{ compareSequenceDefinitions()
+
+    /**
+     * Compare a previous definition with the currently parsed definition
+     *
+     * @param string $sequence_name       name of the sequence
+     * @param array  $current_definition  multi dimensional array that contains the current definition
+     * @param array  $previous_definition multi dimensional array that contains the previous definition
+     * @param array  &$defined_sequences  names in the schema
+     *
+     * @return array|MDB2_Error array of changes on success, or a error object
+     * @access public
+     */
+    function compareSequenceDefinitions($sequence_name, $current_definition,
+        $previous_definition, &$defined_sequences)
+    {
+        $changes = array();
+
+        if (is_array($current_definition)) {
+            $was_sequence_name = $sequence_name;
+            if (!empty($previous_definition[$sequence_name])
+                && isset($previous_definition[$sequence_name]['was'])
+                && $previous_definition[$sequence_name]['was'] == $was_sequence_name
+            ) {
+                $was_sequence_name = $sequence_name;
+            } elseif (!empty($current_definition['was'])) {
+                $was_sequence_name = $current_definition['was'];
+            }
+            if (!empty($previous_definition[$was_sequence_name])) {
+                if ($was_sequence_name != $sequence_name) {
+                    $changes['change'][$was_sequence_name]['name'] = $sequence_name;
+                }
+
+                if (!empty($defined_sequences[$was_sequence_name])) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                        'the sequence "'.$was_sequence_name.'" was specified as base'.
+                        ' of more than of sequence of the database');
+                }
+
+                $defined_sequences[$was_sequence_name] = true;
+
+                $change = array();
+                if (!empty($current_definition['start'])
+                    && isset($previous_definition[$was_sequence_name]['start'])
+                    && $current_definition['start'] != $previous_definition[$was_sequence_name]['start']
+                ) {
+                    $change['start'] = $previous_definition[$sequence_name]['start'];
+                }
+                if (isset($current_definition['on']['table'])
+                    && isset($previous_definition[$was_sequence_name]['on']['table'])
+                    && $current_definition['on']['table'] != $previous_definition[$was_sequence_name]['on']['table']
+                    && isset($current_definition['on']['field'])
+                    && isset($previous_definition[$was_sequence_name]['on']['field'])
+                    && $current_definition['on']['field'] != $previous_definition[$was_sequence_name]['on']['field']
+                ) {
+                    $change['on'] = $current_definition['on'];
+                }
+                if (!empty($change)) {
+                    $changes['change'][$was_sequence_name][$sequence_name] = $change;
+                }
+            } else {
+                if ($sequence_name != $was_sequence_name) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_INVALID, null, null,
+                        'it was specified a previous sequence name ("'.$was_sequence_name.
+                        '") for sequence "'.$sequence_name.'" that does not exist');
+                }
+                $changes['add'][$sequence_name] = true;
+            }
+        }
+        return $changes;
+    }
+    // }}}
+    // {{{ verifyAlterDatabase()
+
+    /**
+     * Verify that the changes requested are supported
+     *
+     * @param array $changes associative array that contains the definition of the changes
+     *              that are meant to be applied to the database structure.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function verifyAlterDatabase($changes)
+    {
+        if (!empty($changes['tables']['change']) && is_array($changes['tables']['change'])) {
+            foreach ($changes['tables']['change'] as $table_name => $table) {
+                if (!empty($table['indexes']) && is_array($table['indexes'])) {
+                    if (!$this->db->supports('indexes')) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
+                            'indexes are not supported');
+                    }
+                    $table_changes = count($table['indexes']);
+                    if (!empty($table['indexes']['add'])) {
+                        $table_changes--;
+                    }
+                    if (!empty($table['indexes']['remove'])) {
+                        $table_changes--;
+                    }
+                    if (!empty($table['indexes']['change'])) {
+                        $table_changes--;
+                    }
+                    if ($table_changes) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
+                            'index alteration not yet supported: '.implode(', ', array_keys($table['indexes'])));
+                    }
+                }
+                unset($table['indexes']);
+                $result = $this->db->manager->alterTable($table_name, $table, true);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+        if (!empty($changes['sequences']) && is_array($changes['sequences'])) {
+            if (!$this->db->supports('sequences')) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
+                    'sequences are not supported');
+            }
+            $sequence_changes = count($changes['sequences']);
+            if (!empty($changes['sequences']['add'])) {
+                $sequence_changes--;
+            }
+            if (!empty($changes['sequences']['remove'])) {
+                $sequence_changes--;
+            }
+            if (!empty($changes['sequences']['change'])) {
+                $sequence_changes--;
+            }
+            if ($sequence_changes) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
+                    'sequence alteration not yet supported: '.implode(', ', array_keys($changes['sequences'])));
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ alterDatabaseIndexes()
+
+    /**
+     * Execute the necessary actions to implement the requested changes
+     * in the indexes inside a database structure.
+     *
+     * @param string $table_name name of the table
+     * @param array  $changes    associative array that contains the definition of the changes
+     *                       that are meant to be applied to the database structure.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function alterDatabaseIndexes($table_name, $changes)
+    {
+        $alterations = 0;
+        if (empty($changes)) {
+            return $alterations;
+        }
+
+        if (!empty($changes['remove']) && is_array($changes['remove'])) {
+            foreach ($changes['remove'] as $index_name => $index) {
+                $this->db->expectError(MDB2_ERROR_NOT_FOUND);
+                if (!empty($index['primary']) || !empty($index['unique'])) {
+                    $result = $this->db->manager->dropConstraint($table_name, $index_name, !empty($index['primary']));
+                } else {
+                    $result = $this->db->manager->dropIndex($table_name, $index_name);
+                }
+                $this->db->popExpect();
+                if (PEAR::isError($result) && !MDB2::isError($result, MDB2_ERROR_NOT_FOUND)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+        if (!empty($changes['change']) && is_array($changes['change'])) {
+            foreach ($changes['change'] as $index_name => $index) {
+                /**
+                 * Drop existing index/constraint first.
+                 * Since $changes doesn't tell us whether it's an index or a constraint before the change,
+                 * we have to find out and call the appropriate method.
+                 */
+                if (in_array($index_name, $this->db->manager->listTableIndexes($table_name))) {
+                    $result = $this->db->manager->dropIndex($table_name, $index_name);
+                } elseif (in_array($index_name, $this->db->manager->listTableConstraints($table_name))) {
+                    $result = $this->db->manager->dropConstraint($table_name, $index_name);
+                }
+                if (!empty($result) && PEAR::isError($result)) {
+                    return $result;
+                }
+
+                if (!empty($index['primary']) || !empty($index['unique'])) {
+                    $result = $this->db->manager->createConstraint($table_name, $index_name, $index);
+                } else {
+                    $result = $this->db->manager->createIndex($table_name, $index_name, $index);
+                }
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+        if (!empty($changes['add']) && is_array($changes['add'])) {
+            foreach ($changes['add'] as $index_name => $index) {
+                if (!empty($index['primary']) || !empty($index['unique'])) {
+                    $result = $this->db->manager->createConstraint($table_name, $index_name, $index);
+                } else {
+                    $result = $this->db->manager->createIndex($table_name, $index_name, $index);
+                }
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+
+        return $alterations;
+    }
+
+    // }}}
+    // {{{ alterDatabaseTables()
+
+    /**
+     * Execute the necessary actions to implement the requested changes
+     * in the tables inside a database structure.
+     *
+     * @param array $current_definition  multi dimensional array that contains the current definition
+     * @param array $previous_definition multi dimensional array that contains the previous definition
+     * @param array $changes             associative array that contains the definition of the changes
+     *                                   that are meant to be applied to the database structure.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function alterDatabaseTables($current_definition, $previous_definition, $changes)
+    {
+        /* FIXME: tables marked to be added are initialized by createTable(), others don't */
+        $alterations = 0;
+        if (empty($changes)) {
+            return $alterations;
+        }
+
+        if (!empty($changes['add']) && is_array($changes['add'])) {
+            foreach ($changes['add'] as $table_name => $table) {
+                $result = $this->createTable($table_name, $current_definition[$table_name]);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+ 
+        if ($this->options['drop_missing_tables']
+            && !empty($changes['remove'])
+            && is_array($changes['remove'])) {
+            foreach ($changes['remove'] as $table_name => $table) {
+                $result = $this->db->manager->dropTable($table_name);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+
+        if (!empty($changes['change']) && is_array($changes['change'])) {
+            foreach ($changes['change'] as $table_name => $table) {
+                $indexes = array();
+                if (!empty($table['indexes'])) {
+                    $indexes = $table['indexes'];
+                    unset($table['indexes']);
+                }
+                if (!empty($indexes['remove'])) {
+                    $result = $this->alterDatabaseIndexes($table_name, array('remove' => $indexes['remove']));
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                    unset($indexes['remove']);
+                    $alterations += $result;
+                }
+                $result = $this->db->manager->alterTable($table_name, $table, false);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+
+                // table may be renamed at this point
+                if (!empty($table['name'])) {
+                    $table_name = $table['name'];
+                }
+
+                if (!empty($indexes)) {
+                    $result = $this->alterDatabaseIndexes($table_name, $indexes);
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                    $alterations += $result;
+                }
+            }
+        }
+
+        return $alterations;
+    }
+
+    // }}}
+    // {{{ alterDatabaseSequences()
+
+    /**
+     * Execute the necessary actions to implement the requested changes
+     * in the sequences inside a database structure.
+     *
+     * @param array $current_definition  multi dimensional array that contains the current definition
+     * @param array $previous_definition multi dimensional array that contains the previous definition
+     * @param array $changes             associative array that contains the definition of the changes
+     *                                   that are meant to be applied to the database structure.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function alterDatabaseSequences($current_definition, $previous_definition, $changes)
+    {
+        $alterations = 0;
+        if (empty($changes)) {
+            return $alterations;
+        }
+
+        if (!empty($changes['add']) && is_array($changes['add'])) {
+            foreach ($changes['add'] as $sequence_name => $sequence) {
+                $result = $this->createSequence($sequence_name, $current_definition[$sequence_name]);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+
+        if (!empty($changes['remove']) && is_array($changes['remove'])) {
+            foreach ($changes['remove'] as $sequence_name => $sequence) {
+                $result = $this->db->manager->dropSequence($sequence_name);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+
+        if (!empty($changes['change']) && is_array($changes['change'])) {
+            foreach ($changes['change'] as $sequence_name => $sequence) {
+                $result = $this->db->manager->dropSequence($previous_definition[$sequence_name]['was']);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $result = $this->createSequence($sequence_name, $sequence);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $alterations++;
+            }
+        }
+
+        return $alterations;
+    }
+
+    // }}}
+    // {{{ alterDatabase()
+
+    /**
+     * Execute the necessary actions to implement the requested changes
+     * in a database structure.
+     *
+     * @param array $current_definition  multi dimensional array that contains the current definition
+     * @param array $previous_definition multi dimensional array that contains the previous definition
+     * @param array $changes             associative array that contains the definition of the changes
+     *                                   that are meant to be applied to the database structure.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function alterDatabase($current_definition, $previous_definition, $changes)
+    {
+        $alterations = 0;
+        if (empty($changes)) {
+            return $alterations;
+        }
+
+        $result = $this->verifyAlterDatabase($changes);
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        if (!empty($current_definition['name'])) {
+            $previous_database_name = $this->db->setDatabase($current_definition['name']);
+        }
+
+        if (($support_transactions = $this->db->supports('transactions'))
+            && PEAR::isError($result = $this->db->beginNestedTransaction())
+        ) {
+            return $result;
+        }
+
+        if (!empty($changes['tables']) && !empty($current_definition['tables'])) {
+            $current_tables  = isset($current_definition['tables']) ? $current_definition['tables'] : array();
+            $previous_tables = isset($previous_definition['tables']) ? $previous_definition['tables'] : array();
+
+            $result = $this->alterDatabaseTables($current_tables, $previous_tables, $changes['tables']);
+            if (is_numeric($result)) {
+                $alterations += $result;
+            }
+        }
+
+        if (!PEAR::isError($result) && !empty($changes['sequences'])) {
+            $current_sequences  = isset($current_definition['sequences']) ? $current_definition['sequences'] : array();
+            $previous_sequences = isset($previous_definition['sequences']) ? $previous_definition['sequences'] : array();
+
+            $result = $this->alterDatabaseSequences($current_sequences, $previous_sequences, $changes['sequences']);
+            if (is_numeric($result)) {
+                $alterations += $result;
+            }
+        }
+
+        if ($support_transactions) {
+            $res = $this->db->completeNestedTransaction();
+            if (PEAR::isError($res)) {
+                $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                    'Could not end transaction ('.
+                    $res->getMessage().' ('.$res->getUserinfo().'))');
+            }
+        } elseif (PEAR::isError($result) && $alterations) {
+            $result = $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                'the requested database alterations were only partially implemented ('.
+                $result->getMessage().' ('.$result->getUserinfo().'))');
+        }
+
+        if (isset($previous_database_name)) {
+            $this->db->setDatabase($previous_database_name);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ dumpDatabaseChanges()
+
+    /**
+     * Dump the changes between two database definitions.
+     *
+     * @param array $changes associative array that specifies the list of database
+     *              definitions changes as returned by the _compareDefinitions
+     *              manager class function.
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function dumpDatabaseChanges($changes)
+    {
+        if (!empty($changes['tables'])) {
+            if (!empty($changes['tables']['add']) && is_array($changes['tables']['add'])) {
+                foreach ($changes['tables']['add'] as $table_name => $table) {
+                    $this->db->debug("$table_name:", __FUNCTION__);
+                    $this->db->debug("\tAdded table '$table_name'", __FUNCTION__);
+                }
+            }
+
+            if (!empty($changes['tables']['remove']) && is_array($changes['tables']['remove'])) {
+                if ($this->options['drop_missing_tables']) {
+                    foreach ($changes['tables']['remove'] as $table_name => $table) {
+                        $this->db->debug("$table_name:", __FUNCTION__);
+                        $this->db->debug("\tRemoved table '$table_name'", __FUNCTION__);
+                    }
+                } else {
+                    foreach ($changes['tables']['remove'] as $table_name => $table) {
+                        $this->db->debug("\tObsolete table '$table_name' left as is", __FUNCTION__);
+                    }
+                }
+            }
+
+            if (!empty($changes['tables']['change']) && is_array($changes['tables']['change'])) {
+                foreach ($changes['tables']['change'] as $table_name => $table) {
+                    if (array_key_exists('name', $table)) {
+                        $this->db->debug("\tRenamed table '$table_name' to '".$table['name']."'", __FUNCTION__);
+                    }
+                    if (!empty($table['add']) && is_array($table['add'])) {
+                        foreach ($table['add'] as $field_name => $field) {
+                            $this->db->debug("\tAdded field '".$field_name."'", __FUNCTION__);
+                        }
+                    }
+                    if (!empty($table['remove']) && is_array($table['remove'])) {
+                        foreach ($table['remove'] as $field_name => $field) {
+                            $this->db->debug("\tRemoved field '".$field_name."'", __FUNCTION__);
+                        }
+                    }
+                    if (!empty($table['rename']) && is_array($table['rename'])) {
+                        foreach ($table['rename'] as $field_name => $field) {
+                            $this->db->debug("\tRenamed field '".$field_name."' to '".$field['name']."'", __FUNCTION__);
+                        }
+                    }
+                    if (!empty($table['change']) && is_array($table['change'])) {
+                        foreach ($table['change'] as $field_name => $field) {
+                            $field = $field['definition'];
+                            if (array_key_exists('type', $field)) {
+                                $this->db->debug("\tChanged field '$field_name' type to '".$field['type']."'", __FUNCTION__);
+                            }
+
+                            if (array_key_exists('unsigned', $field)) {
+                                $this->db->debug("\tChanged field '$field_name' type to '".
+                                                 (!empty($field['unsigned']) && $field['unsigned'] ? '' : 'not ')."unsigned'",
+                                                 __FUNCTION__);
+                            }
+
+                            if (array_key_exists('length', $field)) {
+                                $this->db->debug("\tChanged field '$field_name' length to '".
+                                                (!empty($field['length']) ? $field['length']: 'no length')."'", __FUNCTION__);
+                            }
+                            if (array_key_exists('default', $field)) {
+                                $this->db->debug("\tChanged field '$field_name' default to ".
+                                                 (isset($field['default']) ? "'".$field['default']."'" : 'NULL'), __FUNCTION__);
+                            }
+
+                            if (array_key_exists('notnull', $field)) {
+                                $this->db->debug("\tChanged field '$field_name' notnull to ".
+                                                 (!empty($field['notnull']) && $field['notnull'] ? 'true' : 'false'),
+                                                 __FUNCTION__);
+                            }
+                        }
+                    }
+                    if (!empty($table['indexes']) && is_array($table['indexes'])) {
+                        if (!empty($table['indexes']['add']) && is_array($table['indexes']['add'])) {
+                            foreach ($table['indexes']['add'] as $index_name => $index) {
+                                $this->db->debug("\tAdded index '".$index_name.
+                                    "' of table '$table_name'", __FUNCTION__);
+                            }
+                        }
+                        if (!empty($table['indexes']['remove']) && is_array($table['indexes']['remove'])) {
+                            foreach ($table['indexes']['remove'] as $index_name => $index) {
+                                $this->db->debug("\tRemoved index '".$index_name.
+                                    "' of table '$table_name'", __FUNCTION__);
+                            }
+                        }
+                        if (!empty($table['indexes']['change']) && is_array($table['indexes']['change'])) {
+                            foreach ($table['indexes']['change'] as $index_name => $index) {
+                                if (array_key_exists('name', $index)) {
+                                    $this->db->debug("\tRenamed index '".$index_name."' to '".$index['name'].
+                                                     "' on table '$table_name'", __FUNCTION__);
+                                }
+                                if (array_key_exists('unique', $index)) {
+                                    $this->db->debug("\tChanged index '".$index_name."' unique to '".
+                                                     !empty($index['unique'])."' on table '$table_name'", __FUNCTION__);
+                                }
+                                if (array_key_exists('primary', $index)) {
+                                    $this->db->debug("\tChanged index '".$index_name."' primary to '".
+                                                     !empty($index['primary'])."' on table '$table_name'", __FUNCTION__);
+                                }
+                                if (array_key_exists('change', $index)) {
+                                    $this->db->debug("\tChanged index '".$index_name.
+                                        "' on table '$table_name'", __FUNCTION__);
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (!empty($changes['sequences'])) {
+            if (!empty($changes['sequences']['add']) && is_array($changes['sequences']['add'])) {
+                foreach ($changes['sequences']['add'] as $sequence_name => $sequence) {
+                    $this->db->debug("$sequence_name:", __FUNCTION__);
+                    $this->db->debug("\tAdded sequence '$sequence_name'", __FUNCTION__);
+                }
+            }
+            if (!empty($changes['sequences']['remove']) && is_array($changes['sequences']['remove'])) {
+                foreach ($changes['sequences']['remove'] as $sequence_name => $sequence) {
+                    $this->db->debug("$sequence_name:", __FUNCTION__);
+                    $this->db->debug("\tAdded sequence '$sequence_name'", __FUNCTION__);
+                }
+            }
+            if (!empty($changes['sequences']['change']) && is_array($changes['sequences']['change'])) {
+                foreach ($changes['sequences']['change'] as $sequence_name => $sequence) {
+                    if (array_key_exists('name', $sequence)) {
+                        $this->db->debug("\tRenamed sequence '$sequence_name' to '".
+                                         $sequence['name']."'", __FUNCTION__);
+                    }
+                    if (!empty($sequence['change']) && is_array($sequence['change'])) {
+                        foreach ($sequence['change'] as $sequence_name => $sequence) {
+                            if (array_key_exists('start', $sequence)) {
+                                $this->db->debug("\tChanged sequence '$sequence_name' start to '".
+                                                 $sequence['start']."'", __FUNCTION__);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ dumpDatabase()
+
+    /**
+     * Dump a previously parsed database structure in the Metabase schema
+     * XML based format suitable for the Metabase parser. This function
+     * may optionally dump the database definition with initialization
+     * commands that specify the data that is currently present in the tables.
+     *
+     * @param array $database_definition multi dimensional array that contains the current definition
+     * @param array $arguments           associative array that takes pairs of tag
+     * names and values that define dump options.
+     *                 <pre>array (
+     *                     'output_mode'    =>    String
+     *                         'file' :   dump into a file
+     *                         default:   dump using a function
+     *                     'output'        =>    String
+     *                         depending on the 'Output_Mode'
+     *                                  name of the file
+     *                                  name of the function
+     *                     'end_of_line'        =>    String
+     *                         end of line delimiter that should be used
+     *                         default: "\n"
+     *                 );</pre>
+     * @param int   $dump                Int that determines what data to dump
+     *              + MDB2_SCHEMA_DUMP_ALL       : the entire db
+     *              + MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db
+     *              + MDB2_SCHEMA_DUMP_CONTENT   : only the content of the db
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function dumpDatabase($database_definition, $arguments, $dump = MDB2_SCHEMA_DUMP_ALL)
+    {
+        $class_name = $this->options['writer'];
+
+        $result = MDB2::loadClass($class_name, $this->db->getOption('debug'));
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        // get initialization data
+        if (isset($database_definition['tables']) && is_array($database_definition['tables'])
+            && $dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT
+        ) {
+            foreach ($database_definition['tables'] as $table_name => $table) {
+                $fields  = array();
+                $fieldsq = array();
+                foreach ($table['fields'] as $field_name => $field) {
+                    $fields[$field_name] = $field['type'];
+
+                    $fieldsq[] = $this->db->quoteIdentifier($field_name, true);
+                }
+
+                $query  = 'SELECT '.implode(', ', $fieldsq).' FROM ';
+                $query .= $this->db->quoteIdentifier($table_name, true);
+
+                $data = $this->db->queryAll($query, $fields, MDB2_FETCHMODE_ASSOC);
+
+                if (PEAR::isError($data)) {
+                    return $data;
+                }
+
+                if (!empty($data)) {
+                    $initialization    = array();
+                    $lob_buffer_length = $this->db->getOption('lob_buffer_length');
+                    foreach ($data as $row) {
+                        $rows = array();
+                        foreach ($row as $key => $lob) {
+                            if (is_resource($lob)) {
+                                $value = '';
+                                while (!feof($lob)) {
+                                    $value .= fread($lob, $lob_buffer_length);
+                                }
+                                $row[$key] = $value;
+                            }
+                            $rows[] = array('name' => $key, 'group' => array('type' => 'value', 'data' => $row[$key]));
+                        }
+                        $initialization[] = array('type' => 'insert', 'data' => array('field' => $rows));
+                    }
+                    $database_definition['tables'][$table_name]['initialization'] = $initialization;
+                }
+            }
+        }
+
+        $writer = new $class_name($this->options['valid_types']);
+        return $writer->dumpDatabase($database_definition, $arguments, $dump);
+    }
+
+    // }}}
+    // {{{ writeInitialization()
+
+    /**
+     * Write initialization and sequences
+     *
+     * @param string|array $data      data file or data array
+     * @param string|array $structure structure file or array
+     * @param array        $variables associative array that is passed to the argument
+     * of the same name to the parseDatabaseDefinitionFile function. (there third
+     * param)
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function writeInitialization($data, $structure = false, $variables = array())
+    {
+        if ($structure) {
+            $structure = $this->parseDatabaseDefinition($structure, false, $variables);
+            if (PEAR::isError($structure)) {
+                return $structure;
+            }
+        }
+
+        $data = $this->parseDatabaseDefinition($data, false, $variables, false, $structure);
+        if (PEAR::isError($data)) {
+            return $data;
+        }
+
+        $previous_database_name = null;
+        if (!empty($data['name'])) {
+            $previous_database_name = $this->db->setDatabase($data['name']);
+        } elseif (!empty($structure['name'])) {
+            $previous_database_name = $this->db->setDatabase($structure['name']);
+        }
+
+        if (!empty($data['tables']) && is_array($data['tables'])) {
+            foreach ($data['tables'] as $table_name => $table) {
+                if (empty($table['initialization'])) {
+                    continue;
+                }
+                $result = $this->initializeTable($table_name, $table);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+
+        if (!empty($structure['sequences']) && is_array($structure['sequences'])) {
+            foreach ($structure['sequences'] as $sequence_name => $sequence) {
+                if (isset($data['sequences'][$sequence_name])
+                    || !isset($sequence['on']['table'])
+                    || !isset($data['tables'][$sequence['on']['table']])
+                ) {
+                    continue;
+                }
+                $result = $this->createSequence($sequence_name, $sequence, true);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+        if (!empty($data['sequences']) && is_array($data['sequences'])) {
+            foreach ($data['sequences'] as $sequence_name => $sequence) {
+                $result = $this->createSequence($sequence_name, $sequence, true);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+            }
+        }
+
+        if (isset($previous_database_name)) {
+            $this->db->setDatabase($previous_database_name);
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ updateDatabase()
+
+    /**
+     * Compare the correspondent files of two versions of a database schema
+     * definition: the previously installed and the one that defines the schema
+     * that is meant to update the database.
+     * If the specified previous definition file does not exist, this function
+     * will create the database from the definition specified in the current
+     * schema file.
+     * If both files exist, the function assumes that the database was previously
+     * installed based on the previous schema file and will update it by just
+     * applying the changes.
+     * If this function succeeds, the contents of the current schema file are
+     * copied to replace the previous schema file contents. Any subsequent schema
+     * changes should only be done on the file specified by the $current_schema_file
+     * to let this function make a consistent evaluation of the exact changes that
+     * need to be applied.
+     *
+     * @param string|array $current_schema            filename or array of the updated database schema definition.
+     * @param string|array $previous_schema           filename or array of the previously installed database schema definition.
+     * @param array        $variables                 associative array that is passed to the argument of the same
+     *                                                name to the parseDatabaseDefinitionFile function. (there third param)
+     * @param bool         $disable_query             determines if the disable_query option should be set to true
+     *                                                for the alterDatabase() or createDatabase() call
+     * @param bool         $overwrite_old_schema_file Overwrite?
+     *
+     * @return bool|MDB2_Error MDB2_OK or error object
+     * @access public
+     */
+    function updateDatabase($current_schema, $previous_schema = false,
+                            $variables = array(), $disable_query = false,
+                            $overwrite_old_schema_file = false)
+    {
+        $current_definition = $this->parseDatabaseDefinition($current_schema, false, $variables,
+                                                             $this->options['fail_on_invalid_names']);
+
+        if (PEAR::isError($current_definition)) {
+            return $current_definition;
+        }
+
+        $previous_definition = false;
+        if ($previous_schema) {
+            $previous_definition = $this->parseDatabaseDefinition($previous_schema, true, $variables,
+                                                                  $this->options['fail_on_invalid_names']);
+            if (PEAR::isError($previous_definition)) {
+                return $previous_definition;
+            }
+        }
+
+        if ($previous_definition) {
+            $dbExists = $this->db->databaseExists($current_definition['name']);
+            if (PEAR::isError($dbExists)) {
+                return $dbExists;
+            }
+
+            if (!$dbExists) {
+                 return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                    'database to update does not exist: '.$current_definition['name']);
+            }
+
+            $changes = $this->compareDefinitions($current_definition, $previous_definition);
+            if (PEAR::isError($changes)) {
+                return $changes;
+            }
+
+            if (is_array($changes)) {
+                $this->db->setOption('disable_query', $disable_query);
+                $result = $this->alterDatabase($current_definition, $previous_definition, $changes);
+                $this->db->setOption('disable_query', false);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+                $copy = true;
+                if ($this->db->options['debug']) {
+                    $result = $this->dumpDatabaseChanges($changes);
+                    if (PEAR::isError($result)) {
+                        return $result;
+                    }
+                }
+            }
+        } else {
+            $this->db->setOption('disable_query', $disable_query);
+            $result = $this->createDatabase($current_definition);
+            $this->db->setOption('disable_query', false);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        if ($overwrite_old_schema_file
+            && !$disable_query
+            && is_string($previous_schema) && is_string($current_schema)
+            && !copy($current_schema, $previous_schema)) {
+
+            return $this->raiseError(MDB2_SCHEMA_ERROR, null, null,
+                'Could not copy the new database definition file to the current file');
+        }
+
+        return MDB2_OK;
+    }
+    // }}}
+    // {{{ errorMessage()
+
+    /**
+     * Return a textual error message for a MDB2 error code
+     *
+     * @param int|array $value integer error code, <code>null</code> to get the
+     *                          current error code-message map,
+     *                          or an array with a new error code-message map
+     *
+     * @return  string  error message, or false if the error code was not recognized
+     * @access public
+     */
+    function errorMessage($value = null)
+    {
+        static $errorMessages;
+        if (is_array($value)) {
+            $errorMessages = $value;
+            return MDB2_OK;
+        } elseif (!isset($errorMessages)) {
+            $errorMessages = array(
+                MDB2_SCHEMA_ERROR              => 'unknown error',
+                MDB2_SCHEMA_ERROR_PARSE        => 'schema parse error',
+                MDB2_SCHEMA_ERROR_VALIDATE     => 'schema validation error',
+                MDB2_SCHEMA_ERROR_INVALID      => 'invalid',
+                MDB2_SCHEMA_ERROR_UNSUPPORTED  => 'not supported',
+                MDB2_SCHEMA_ERROR_WRITER       => 'schema writer error',
+            );
+        }
+
+        if (is_null($value)) {
+            return $errorMessages;
+        }
+
+        if (PEAR::isError($value)) {
+            $value = $value->getCode();
+        }
+
+        return !empty($errorMessages[$value]) ?
+           $errorMessages[$value] : $errorMessages[MDB2_SCHEMA_ERROR];
+    }
+
+    // }}}
+    // {{{ raiseError()
+
+    /**
+     * This method is used to communicate an error and invoke error
+     * callbacks etc.  Basically a wrapper for PEAR::raiseError
+     * without the message string.
+     *
+     * @param int|PEAR_Error $code     integer error code or and PEAR_Error instance
+     * @param int            $mode     error mode, see PEAR_Error docs
+     *                                 error level (E_USER_NOTICE etc).  If error mode is
+     *                                 PEAR_ERROR_CALLBACK, this is the callback function,
+     *                                 either as a function name, or as an array of an
+     *                                 object and method name.  For other error modes this
+     *                                 parameter is ignored.
+     * @param array          $options  Options, depending on the mode, @see PEAR::setErrorHandling
+     * @param string         $userinfo Extra debug information.  Defaults to the last
+     *                                 query and native error code.
+     *
+     * @return object  a PEAR error object
+     * @access  public
+     * @see PEAR_Error
+     */
+    function raiseError($code = null, $mode = null, $options = null, $userinfo = null,$a=null,$b=null,$c=null)
+    {
+        $err =PEAR::raiseError(null, $code, $mode, $options,
+                                $userinfo, 'MDB2_Schema_Error', true);
+        return $err;
+    }
+
+    // }}}
+    // {{{ isError()
+
+    /**
+     * Tell whether a value is an MDB2_Schema error.
+     *
+     * @param mixed $data the value to test
+     * @param int   $code if $data is an error object, return true only if $code is
+     *                    a string and $db->getMessage() == $code or
+     *                    $code is an integer and $db->getCode() == $code
+     *
+     * @return  bool  true if parameter is an error
+     * @access  public
+     */
+    static function isError($data, $code = null)
+    {
+        if (is_a($data, 'MDB2_Schema_Error')) {
+            if (is_null($code)) {
+                return true;
+            } elseif (is_string($code)) {
+                return $data->getMessage() === $code;
+            } else {
+                $code = (array)$code;
+                return in_array($data->getCode(), $code);
+            }
+        }
+        return false;
+    }
+
+    // }}}
+}
+
+/**
+ * MDB2_Schema_Error implements a class for reporting portable database error
+ * messages.
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Stig Bakken <ssb@fast.no>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema_Error extends PEAR_Error
+{
+    /**
+     * MDB2_Schema_Error constructor.
+     *
+     * @param mixed $code      error code, or string with error message.
+     * @param int   $mode      what 'error mode' to operate in
+     * @param int   $level     what error level to use for $mode & PEAR_ERROR_TRIGGER
+     * @param mixed $debuginfo additional debug info, such as the last query
+     *
+     * @access  public
+     */
+    function MDB2_Schema_Error($code = MDB2_SCHEMA_ERROR, $mode = PEAR_ERROR_RETURN,
+              $level = E_USER_NOTICE, $debuginfo = null)
+    {
+        $this->PEAR_Error('MDB2_Schema Error: ' . MDB2_Schema::errorMessage($code), $code,
+            $mode, $level, $debuginfo);
+    }
+}
+?>
diff --git a/3rdparty/MDB2/Schema/Parser.php b/3rdparty/MDB2/Schema/Parser.php
new file mode 100644
index 0000000000000000000000000000000000000000..b740ef55fa85d17fc82dfb1fb08ecccdc493e02b
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Parser.php
@@ -0,0 +1,812 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
+ * Stig. S. Bakken, Lukas Smith, Igor Feghali
+ * All rights reserved.
+ *
+ * MDB2_Schema enables users to maintain RDBMS independant schema files
+ * in XML that can be used to manipulate both data and database schemas
+ * 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, Igor Feghali 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: Christian Dickmann <dickmann@php.net>
+ * Author: Igor Feghali <ifeghali@php.net>
+ *
+ * $Id: Parser.php,v 1.68 2008/11/30 03:34:00 clockwerx Exp $
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Christian Dickmann <dickmann@php.net>
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @version  CVS: $Id: Parser.php,v 1.68 2008/11/30 03:34:00 clockwerx Exp $
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+
+
+require_once('XML/Parser.php');
+require_once('MDB2/Schema/Validate.php');
+
+/**
+ * Parses an XML schema file
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Christian Dickmann <dickmann@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema_Parser extends XML_Parser
+{
+    var $database_definition = array();
+
+    var $elements = array();
+
+    var $element = '';
+
+    var $count = 0;
+
+    var $table = array();
+
+    var $table_name = '';
+
+    var $field = array();
+
+    var $field_name = '';
+
+    var $init = array();
+
+    var $init_function = array();
+
+    var $init_expression = array();
+
+    var $init_field = array();
+
+    var $index = array();
+
+    var $index_name = '';
+
+    var $constraint = array();
+
+    var $constraint_name = '';
+
+    var $var_mode = false;
+
+    var $variables = array();
+
+    var $sequence = array();
+
+    var $sequence_name = '';
+
+    var $error;
+
+    var $structure = false;
+
+    var $val;
+
+    function __construct($variables, $fail_on_invalid_names = true,
+                         $structure = false, $valid_types = array(),
+                         $force_defaults = true)
+    {
+        // force ISO-8859-1 due to different defaults for PHP4 and PHP5
+        // todo: this probably needs to be investigated some more andcleaned up
+        parent::__construct('ISO-8859-1');
+
+        $this->variables = $variables;
+        $this->structure = $structure;
+        $this->val       =new MDB2_Schema_Validate($fail_on_invalid_names, $valid_types, $force_defaults);
+    }
+
+    function startHandler($xp, $element, $attribs)
+    {
+        if (strtolower($element) == 'variable') {
+            $this->var_mode = true;
+            return;
+        }
+
+        $this->elements[$this->count++] = strtolower($element);
+
+        $this->element = implode('-', $this->elements);
+
+        switch ($this->element) {
+        /* Initialization */
+        case 'database-table-initialization':
+            $this->table['initialization'] = array();
+            break;
+
+        /* Insert */
+        /* insert: field+ */
+        case 'database-table-initialization-insert':
+            $this->init = array('type' => 'insert', 'data' => array('field' => array()));
+            break;
+        /* insert-select: field+, table, where? */
+        case 'database-table-initialization-insert-select':
+            $this->init['data']['table'] = '';
+            break;
+
+        /* Update */
+        /* update: field+, where? */
+        case 'database-table-initialization-update':
+            $this->init = array('type' => 'update', 'data' => array('field' => array()));
+            break;
+
+        /* Delete */
+        /* delete: where */
+        case 'database-table-initialization-delete':
+            $this->init = array('type' => 'delete', 'data' => array('where' => array()));
+            break;
+
+        /* Insert and Update */
+        case 'database-table-initialization-insert-field':
+        case 'database-table-initialization-insert-select-field':
+        case 'database-table-initialization-update-field':
+            $this->init_field = array('name' => '', 'group' => array());
+            break;
+        case 'database-table-initialization-insert-field-value':
+        case 'database-table-initialization-insert-select-field-value':
+        case 'database-table-initialization-update-field-value':
+            /* if value tag is empty cdataHandler is not called so we must force value element creation here */
+            $this->init_field['group'] = array('type' => 'value', 'data' => '');
+            break;
+        case 'database-table-initialization-insert-field-null':
+        case 'database-table-initialization-insert-select-field-null':
+        case 'database-table-initialization-update-field-null':
+            $this->init_field['group'] = array('type' => 'null');
+            break;
+        case 'database-table-initialization-insert-field-function':
+        case 'database-table-initialization-insert-select-field-function':
+        case 'database-table-initialization-update-field-function':
+            $this->init_function = array('name' => '');
+            break;
+        case 'database-table-initialization-insert-field-expression':
+        case 'database-table-initialization-insert-select-field-expression':
+        case 'database-table-initialization-update-field-expression':
+            $this->init_expression = array();
+            break;
+
+        /* All */
+        case 'database-table-initialization-insert-select-where':
+        case 'database-table-initialization-update-where':
+        case 'database-table-initialization-delete-where':
+            $this->init['data']['where'] = array('type' => '', 'data' => array());
+            break;
+        case 'database-table-initialization-insert-select-where-expression':
+        case 'database-table-initialization-update-where-expression':
+        case 'database-table-initialization-delete-where-expression':
+            $this->init_expression = array();
+            break;
+
+        /* One level simulation of expression-function recursion */
+        case 'database-table-initialization-insert-field-expression-function':
+        case 'database-table-initialization-insert-select-field-expression-function':
+        case 'database-table-initialization-insert-select-where-expression-function':
+        case 'database-table-initialization-update-field-expression-function':
+        case 'database-table-initialization-update-where-expression-function':
+        case 'database-table-initialization-delete-where-expression-function':
+            $this->init_function = array('name' => '');
+            break;
+
+        /* One level simulation of function-expression recursion */
+        case 'database-table-initialization-insert-field-function-expression':
+        case 'database-table-initialization-insert-select-field-function-expression':
+        case 'database-table-initialization-insert-select-where-function-expression':
+        case 'database-table-initialization-update-field-function-expression':
+        case 'database-table-initialization-update-where-function-expression':
+        case 'database-table-initialization-delete-where-function-expression':
+            $this->init_expression = array();
+            break;
+
+        /* Definition */
+        case 'database':
+            $this->database_definition = array(
+                'name' => '',
+                'create' => '',
+                'overwrite' => '',
+                'charset' => '',
+                'description' => '',
+                'comments' => '',
+                'tables' => array(),
+                'sequences' => array()
+            );
+            break;
+        case 'database-table':
+            $this->table_name = '';
+
+            $this->table = array(
+                'was' => '',
+                'description' => '',
+                'comments' => '',
+                'fields' => array(),
+                'indexes' => array(),
+                'constraints' => array(),
+                'initialization' => array()
+            );
+            break;
+        case 'database-table-declaration-field':
+        case 'database-table-declaration-foreign-field':
+        case 'database-table-declaration-foreign-references-field':
+            $this->field_name = '';
+
+            $this->field = array();
+            break;
+        case 'database-table-declaration-index-field':
+            $this->field_name = '';
+
+            $this->field = array('sorting' => '', 'length' => '');
+            break;
+        /* force field attributes to be initialized when the tag is empty in the XML */
+        case 'database-table-declaration-field-was':
+            $this->field['was'] = '';
+            break;
+        case 'database-table-declaration-field-type':
+            $this->field['type'] = '';
+            break;
+        case 'database-table-declaration-field-fixed':
+            $this->field['fixed'] = '';
+            break;
+        case 'database-table-declaration-field-default':
+            $this->field['default'] = '';
+            break;
+        case 'database-table-declaration-field-notnull':
+            $this->field['notnull'] = '';
+            break;
+        case 'database-table-declaration-field-autoincrement':
+            $this->field['autoincrement'] = '';
+            break;
+        case 'database-table-declaration-field-unsigned':
+            $this->field['unsigned'] = '';
+            break;
+        case 'database-table-declaration-field-length':
+            $this->field['length'] = '';
+            break;
+        case 'database-table-declaration-field-description':
+            $this->field['description'] = '';
+            break;
+        case 'database-table-declaration-field-comments':
+            $this->field['comments'] = '';
+            break;
+        case 'database-table-declaration-index':
+            $this->index_name = '';
+
+            $this->index = array(
+                'was' => '',
+                'unique' =>'',
+                'primary' => '',
+                'fields' => array()
+            );
+            break;
+        case 'database-table-declaration-foreign':
+            $this->constraint_name = '';
+
+            $this->constraint = array(
+                'was' => '',
+                'match' => '',
+                'ondelete' => '',
+                'onupdate' => '',
+                'deferrable' => '',
+                'initiallydeferred' => '',
+                'foreign' => true,
+                'fields' => array(),
+                'references' => array('table' => '', 'fields' => array())
+            );
+            break;
+        case 'database-sequence':
+            $this->sequence_name = '';
+
+            $this->sequence = array(
+                'was' => '',
+                'start' => '',
+                'description' => '',
+                'comments' => '',
+                'on' => array('table' => '', 'field' => '')
+            );
+            break;
+        }
+    }
+
+    function endHandler($xp, $element)
+    {
+        if (strtolower($element) == 'variable') {
+            $this->var_mode = false;
+            return;
+        }
+
+        switch ($this->element) {
+        /* Initialization */
+
+        /* Insert */
+        case 'database-table-initialization-insert-select':
+            $this->init['data'] = array('select' => $this->init['data']);
+            break;
+
+        /* Insert and Delete */
+        case 'database-table-initialization-insert-field':
+        case 'database-table-initialization-insert-select-field':
+        case 'database-table-initialization-update-field':
+            $result = $this->val->validateDataField($this->table['fields'], $this->init['data']['field'], $this->init_field);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->init['data']['field'][] = $this->init_field;
+            }
+            break;
+        case 'database-table-initialization-insert-field-function':
+        case 'database-table-initialization-insert-select-field-function':
+        case 'database-table-initialization-update-field-function':
+            $this->init_field['group'] = array('type' => 'function', 'data' => $this->init_function);
+            break;
+        case 'database-table-initialization-insert-field-expression':
+        case 'database-table-initialization-insert-select-field-expression':
+        case 'database-table-initialization-update-field-expression':
+            $this->init_field['group'] = array('type' => 'expression', 'data' => $this->init_expression);
+            break;
+
+        /* All */
+        case 'database-table-initialization-insert-select-where-expression':
+        case 'database-table-initialization-update-where-expression':
+        case 'database-table-initialization-delete-where-expression':
+            $this->init['data']['where']['type'] = 'expression';
+            $this->init['data']['where']['data'] = $this->init_expression;
+            break;
+        case 'database-table-initialization-insert':
+        case 'database-table-initialization-delete':
+        case 'database-table-initialization-update':
+            $this->table['initialization'][] = $this->init;
+            break;
+
+        /* One level simulation of expression-function recursion */
+        case 'database-table-initialization-insert-field-expression-function':
+        case 'database-table-initialization-insert-select-field-expression-function':
+        case 'database-table-initialization-insert-select-where-expression-function':
+        case 'database-table-initialization-update-field-expression-function':
+        case 'database-table-initialization-update-where-expression-function':
+        case 'database-table-initialization-delete-where-expression-function':
+            $this->init_expression['operants'][] = array('type' => 'function', 'data' => $this->init_function);
+            break;
+
+        /* One level simulation of function-expression recursion */
+        case 'database-table-initialization-insert-field-function-expression':
+        case 'database-table-initialization-insert-select-field-function-expression':
+        case 'database-table-initialization-insert-select-where-function-expression':
+        case 'database-table-initialization-update-field-function-expression':
+        case 'database-table-initialization-update-where-function-expression':
+        case 'database-table-initialization-delete-where-function-expression':
+            $this->init_function['arguments'][] = array('type' => 'expression', 'data' => $this->init_expression);
+            break;
+
+        /* Table definition */
+        case 'database-table':
+            $result = $this->val->validateTable($this->database_definition['tables'], $this->table, $this->table_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->database_definition['tables'][$this->table_name] = $this->table;
+            }
+            break;
+        case 'database-table-name':
+            if (isset($this->structure['tables'][$this->table_name])) {
+                $this->table = $this->structure['tables'][$this->table_name];
+            }
+            break;
+
+        /* Field declaration */
+        case 'database-table-declaration-field':
+            $result = $this->val->validateField($this->table['fields'], $this->field, $this->field_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->table['fields'][$this->field_name] = $this->field;
+            }
+            break;
+
+        /* Index declaration */
+        case 'database-table-declaration-index':
+            $result = $this->val->validateIndex($this->table['indexes'], $this->index, $this->index_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->table['indexes'][$this->index_name] = $this->index;
+            }
+            break;
+        case 'database-table-declaration-index-field':
+            $result = $this->val->validateIndexField($this->index['fields'], $this->field, $this->field_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->index['fields'][$this->field_name] = $this->field;
+            }
+            break;
+
+        /* Foreign Key declaration */
+        case 'database-table-declaration-foreign':
+            $result = $this->val->validateConstraint($this->table['constraints'], $this->constraint, $this->constraint_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->table['constraints'][$this->constraint_name] = $this->constraint;
+            }
+            break;
+        case 'database-table-declaration-foreign-field':
+            $result = $this->val->validateConstraintField($this->constraint['fields'], $this->field_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->constraint['fields'][$this->field_name] = '';
+            }
+            break;
+        case 'database-table-declaration-foreign-references-field':
+            $result = $this->val->validateConstraintReferencedField($this->constraint['references']['fields'], $this->field_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->constraint['references']['fields'][$this->field_name] = '';
+            }
+            break;
+
+        /* Sequence declaration */
+        case 'database-sequence':
+            $result = $this->val->validateSequence($this->database_definition['sequences'], $this->sequence, $this->sequence_name);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            } else {
+                $this->database_definition['sequences'][$this->sequence_name] = $this->sequence;
+            }
+            break;
+
+        /* End of File */
+        case 'database':
+            $result = $this->val->validateDatabase($this->database_definition);
+            if (PEAR::isError($result)) {
+                $this->raiseError($result->getUserinfo(), 0, $xp, $result->getCode());
+            }
+            break;
+        }
+
+        unset($this->elements[--$this->count]);
+        $this->element = implode('-', $this->elements);
+    }
+
+    function raiseError($msg = null, $xmlecode = 0, $xp = null, $ecode = MDB2_SCHEMA_ERROR_PARSE,$a=null,$b=null,$c=null)
+    {
+        if (is_null($this->error)) {
+            $error = '';
+            if (is_resource($msg)) {
+                $error .= 'Parser error: '.xml_error_string(xml_get_error_code($msg));
+                $xp     = $msg;
+            } else {
+                $error .= 'Parser error: '.$msg;
+                if (!is_resource($xp)) {
+                    $xp = $this->parser;
+                }
+            }
+
+            if ($error_string = xml_error_string($xmlecode)) {
+                $error .= ' - '.$error_string;
+            }
+
+            if (is_resource($xp)) {
+                $byte   = @xml_get_current_byte_index($xp);
+                $line   = @xml_get_current_line_number($xp);
+                $column = @xml_get_current_column_number($xp);
+                $error .= " - Byte: $byte; Line: $line; Col: $column";
+            }
+
+            $error .= "\n";
+
+            $this->error =& MDB2_Schema::raiseError($ecode, null, null, $error);
+        }
+        return $this->error;
+    }
+
+    function cdataHandler($xp, $data)
+    {
+        if ($this->var_mode == true) {
+            if (!isset($this->variables[$data])) {
+                $this->raiseError('variable "'.$data.'" not found', null, $xp);
+                return;
+            }
+            $data = $this->variables[$data];
+        }
+
+        switch ($this->element) {
+        /* Initialization */
+
+        /* Insert */
+        case 'database-table-initialization-insert-select-table':
+            $this->init['data']['table'] = $data;
+            break;
+
+        /* Insert and Update */
+        case 'database-table-initialization-insert-field-name':
+        case 'database-table-initialization-insert-select-field-name':
+        case 'database-table-initialization-update-field-name':
+            $this->init_field['name'] .= $data;
+            break;
+        case 'database-table-initialization-insert-field-value':
+        case 'database-table-initialization-insert-select-field-value':
+        case 'database-table-initialization-update-field-value':
+            $this->init_field['group']['data'] .= $data;
+            break;
+        case 'database-table-initialization-insert-field-function-name':
+        case 'database-table-initialization-insert-select-field-function-name':
+        case 'database-table-initialization-update-field-function-name':
+            $this->init_function['name'] .= $data;
+            break;
+        case 'database-table-initialization-insert-field-function-value':
+        case 'database-table-initialization-insert-select-field-function-value':
+        case 'database-table-initialization-update-field-function-value':
+            $this->init_function['arguments'][] = array('type' => 'value', 'data' => $data);
+            break;
+        case 'database-table-initialization-insert-field-function-column':
+        case 'database-table-initialization-insert-select-field-function-column':
+        case 'database-table-initialization-update-field-function-column':
+            $this->init_function['arguments'][] = array('type' => 'column', 'data' => $data);
+            break;
+        case 'database-table-initialization-insert-field-column':
+        case 'database-table-initialization-insert-select-field-column':
+        case 'database-table-initialization-update-field-column':
+            $this->init_field['group'] = array('type' => 'column', 'data' => $data);
+            break;
+
+        /* All */
+        case 'database-table-initialization-insert-field-expression-operator':
+        case 'database-table-initialization-insert-select-field-expression-operator':
+        case 'database-table-initialization-insert-select-where-expression-operator':
+        case 'database-table-initialization-update-field-expression-operator':
+        case 'database-table-initialization-update-where-expression-operator':
+        case 'database-table-initialization-delete-where-expression-operator':
+            $this->init_expression['operator'] = $data;
+            break;
+        case 'database-table-initialization-insert-field-expression-value':
+        case 'database-table-initialization-insert-select-field-expression-value':
+        case 'database-table-initialization-insert-select-where-expression-value':
+        case 'database-table-initialization-update-field-expression-value':
+        case 'database-table-initialization-update-where-expression-value':
+        case 'database-table-initialization-delete-where-expression-value':
+            $this->init_expression['operants'][] = array('type' => 'value', 'data' => $data);
+            break;
+        case 'database-table-initialization-insert-field-expression-column':
+        case 'database-table-initialization-insert-select-field-expression-column':
+        case 'database-table-initialization-insert-select-where-expression-column':
+        case 'database-table-initialization-update-field-expression-column':
+        case 'database-table-initialization-update-where-expression-column':
+        case 'database-table-initialization-delete-where-expression-column':
+            $this->init_expression['operants'][] = array('type' => 'column', 'data' => $data);
+            break;
+
+        case 'database-table-initialization-insert-field-function-function':
+        case 'database-table-initialization-insert-field-function-expression':
+        case 'database-table-initialization-insert-field-expression-expression':
+        case 'database-table-initialization-update-field-function-function':
+        case 'database-table-initialization-update-field-function-expression':
+        case 'database-table-initialization-update-field-expression-expression':
+        case 'database-table-initialization-update-where-expression-expression':
+        case 'database-table-initialization-delete-where-expression-expression':
+            /* Recursion to be implemented yet */
+            break;
+
+        /* One level simulation of expression-function recursion */
+        case 'database-table-initialization-insert-field-expression-function-name':
+        case 'database-table-initialization-insert-select-field-expression-function-name':
+        case 'database-table-initialization-insert-select-where-expression-function-name':
+        case 'database-table-initialization-update-field-expression-function-name':
+        case 'database-table-initialization-update-where-expression-function-name':
+        case 'database-table-initialization-delete-where-expression-function-name':
+            $this->init_function['name'] .= $data;
+            break;
+        case 'database-table-initialization-insert-field-expression-function-value':
+        case 'database-table-initialization-insert-select-field-expression-function-value':
+        case 'database-table-initialization-insert-select-where-expression-function-value':
+        case 'database-table-initialization-update-field-expression-function-value':
+        case 'database-table-initialization-update-where-expression-function-value':
+        case 'database-table-initialization-delete-where-expression-function-value':
+            $this->init_function['arguments'][] = array('type' => 'value', 'data' => $data);
+            break;
+        case 'database-table-initialization-insert-field-expression-function-column':
+        case 'database-table-initialization-insert-select-field-expression-function-column':
+        case 'database-table-initialization-insert-select-where-expression-function-column':
+        case 'database-table-initialization-update-field-expression-function-column':
+        case 'database-table-initialization-update-where-expression-function-column':
+        case 'database-table-initialization-delete-where-expression-function-column':
+            $this->init_function['arguments'][] = array('type' => 'column', 'data' => $data);
+            break;
+
+        /* One level simulation of function-expression recursion */
+        case 'database-table-initialization-insert-field-function-expression-operator':
+        case 'database-table-initialization-insert-select-field-function-expression-operator':
+        case 'database-table-initialization-update-field-function-expression-operator':
+            $this->init_expression['operator'] = $data;
+            break;
+        case 'database-table-initialization-insert-field-function-expression-value':
+        case 'database-table-initialization-insert-select-field-function-expression-value':
+        case 'database-table-initialization-update-field-function-expression-value':
+            $this->init_expression['operants'][] = array('type' => 'value', 'data' => $data);
+            break;
+        case 'database-table-initialization-insert-field-function-expression-column':
+        case 'database-table-initialization-insert-select-field-function-expression-column':
+        case 'database-table-initialization-update-field-function-expression-column':
+            $this->init_expression['operants'][] = array('type' => 'column', 'data' => $data);
+            break;
+
+        /* Database */
+        case 'database-name':
+            $this->database_definition['name'] .= $data;
+            break;
+        case 'database-create':
+            $this->database_definition['create'] .= $data;
+            break;
+        case 'database-overwrite':
+            $this->database_definition['overwrite'] .= $data;
+            break;
+        case 'database-charset':
+            $this->database_definition['charset'] .= $data;
+            break;
+        case 'database-description':
+            $this->database_definition['description'] .= $data;
+            break;
+        case 'database-comments':
+            $this->database_definition['comments'] .= $data;
+            break;
+
+        /* Table declaration */
+        case 'database-table-name':
+            $this->table_name .= $data;
+            break;
+        case 'database-table-was':
+            $this->table['was'] .= $data;
+            break;
+        case 'database-table-description':
+            $this->table['description'] .= $data;
+            break;
+        case 'database-table-comments':
+            $this->table['comments'] .= $data;
+            break;
+
+        /* Field declaration */
+        case 'database-table-declaration-field-name':
+            $this->field_name .= $data;
+            break;
+        case 'database-table-declaration-field-was':
+            $this->field['was'] .= $data;
+            break;
+        case 'database-table-declaration-field-type':
+            $this->field['type'] .= $data;
+            break;
+        case 'database-table-declaration-field-fixed':
+            $this->field['fixed'] .= $data;
+            break;
+        case 'database-table-declaration-field-default':
+            $this->field['default'] .= $data;
+            break;
+        case 'database-table-declaration-field-notnull':
+            $this->field['notnull'] .= $data;
+            break;
+        case 'database-table-declaration-field-autoincrement':
+            $this->field['autoincrement'] .= $data;
+            break;
+        case 'database-table-declaration-field-unsigned':
+            $this->field['unsigned'] .= $data;
+            break;
+        case 'database-table-declaration-field-length':
+            $this->field['length'] .= $data;
+            break;
+        case 'database-table-declaration-field-description':
+            $this->field['description'] .= $data;
+            break;
+        case 'database-table-declaration-field-comments':
+            $this->field['comments'] .= $data;
+            break;
+
+        /* Index declaration */
+        case 'database-table-declaration-index-name':
+            $this->index_name .= $data;
+            break;
+        case 'database-table-declaration-index-was':
+            $this->index['was'] .= $data;
+            break;
+        case 'database-table-declaration-index-unique':
+            $this->index['unique'] .= $data;
+            break;
+        case 'database-table-declaration-index-primary':
+            $this->index['primary'] .= $data;
+            break;
+        case 'database-table-declaration-index-field-name':
+            $this->field_name .= $data;
+            break;
+        case 'database-table-declaration-index-field-sorting':
+            $this->field['sorting'] .= $data;
+            break;
+        /* Add by Leoncx */
+        case 'database-table-declaration-index-field-length':
+            $this->field['length'] .= $data;
+            break;
+
+        /* Foreign Key declaration */
+        case 'database-table-declaration-foreign-name':
+            $this->constraint_name .= $data;
+            break;
+        case 'database-table-declaration-foreign-was':
+            $this->constraint['was'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-match':
+            $this->constraint['match'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-ondelete':
+            $this->constraint['ondelete'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-onupdate':
+            $this->constraint['onupdate'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-deferrable':
+            $this->constraint['deferrable'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-initiallydeferred':
+            $this->constraint['initiallydeferred'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-field':
+            $this->field_name .= $data;
+            break;
+        case 'database-table-declaration-foreign-references-table':
+            $this->constraint['references']['table'] .= $data;
+            break;
+        case 'database-table-declaration-foreign-references-field':
+            $this->field_name .= $data;
+            break;
+
+        /* Sequence declaration */
+        case 'database-sequence-name':
+            $this->sequence_name .= $data;
+            break;
+        case 'database-sequence-was':
+            $this->sequence['was'] .= $data;
+            break;
+        case 'database-sequence-start':
+            $this->sequence['start'] .= $data;
+            break;
+        case 'database-sequence-description':
+            $this->sequence['description'] .= $data;
+            break;
+        case 'database-sequence-comments':
+            $this->sequence['comments'] .= $data;
+            break;
+        case 'database-sequence-on-table':
+            $this->sequence['on']['table'] .= $data;
+            break;
+        case 'database-sequence-on-field':
+            $this->sequence['on']['field'] .= $data;
+            break;
+        }
+    }
+}
+
+?>
diff --git a/3rdparty/MDB2/Schema/Parser2.php b/3rdparty/MDB2/Schema/Parser2.php
new file mode 100644
index 0000000000000000000000000000000000000000..01318473fddf0163f89626aa14d0002ce5ed53f3
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Parser2.php
@@ -0,0 +1,624 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
+ * Stig. S. Bakken, Lukas Smith, Igor Feghali
+ * All rights reserved.
+ *
+ * MDB2_Schema enables users to maintain RDBMS independant schema files
+ * in XML that can be used to manipulate both data and database schemas
+ * 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, Igor Feghali 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: Igor Feghali <ifeghali@php.net>
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @version  CVS: $Id: Parser2.php,v 1.12 2008/11/30 03:34:00 clockwerx Exp $
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+
+require_once 'XML/Unserializer.php';
+require_once 'MDB2/Schema/Validate.php';
+
+/**
+ * Parses an XML schema file
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema_Parser2 extends XML_Unserializer
+{
+    var $database_definition = array();
+
+    var $database_loaded = array();
+
+    var $variables = array();
+
+    var $error;
+
+    var $structure = false;
+
+    var $val;
+
+    var $options = array();
+
+    var $table = array();
+
+    var $table_name = '';
+
+    var $field = array();
+
+    var $field_name = '';
+
+    var $index = array();
+
+    var $index_name = '';
+
+    var $constraint = array();
+
+    var $constraint_name = '';
+
+    var $sequence = array();
+
+    var $sequence_name = '';
+
+    var $init = array();
+
+    function __construct($variables, $fail_on_invalid_names = true, $structure = false, $valid_types = array(), $force_defaults = true)
+    {
+        // force ISO-8859-1 due to different defaults for PHP4 and PHP5
+        // todo: this probably needs to be investigated some more and cleaned up
+        $this->options['encoding'] = 'ISO-8859-1';
+
+        $this->options['XML_UNSERIALIZER_OPTION_ATTRIBUTES_PARSE']    = true;
+        $this->options['XML_UNSERIALIZER_OPTION_ATTRIBUTES_ARRAYKEY'] = false;
+
+        $this->options['forceEnum'] = array('table', 'field', 'index', 'foreign', 'insert', 'update', 'delete', 'sequence');
+
+        /*
+         * todo: find a way to force the following items not to be parsed as arrays
+         * as it cause problems in functions with multiple arguments
+         */
+        //$this->options['forceNEnum'] = array('value', 'column');
+        $this->variables = $variables;
+        $this->structure = $structure;
+
+        $this->val =& new MDB2_Schema_Validate($fail_on_invalid_names, $valid_types, $force_defaults);
+        parent::XML_Unserializer($this->options);
+    }
+
+    function MDB2_Schema_Parser2($variables, $fail_on_invalid_names = true, $structure = false, $valid_types = array(), $force_defaults = true)
+    {
+        $this->__construct($variables, $fail_on_invalid_names, $structure, $valid_types, $force_defaults);
+    }
+
+    function parse()
+    {
+        $result = $this->unserialize($this->filename, true);
+
+        if (PEAR::isError($result)) {
+            return $result;
+        } else {
+            $this->database_loaded = $this->getUnserializedData();
+            return $this->fixDatabaseKeys($this->database_loaded);
+        }
+    }
+
+    function setInputFile($filename)
+    {
+        $this->filename = $filename;
+        return MDB2_OK;
+    }
+
+    function renameKey(&$arr, $oKey, $nKey)
+    {
+        $arr[$nKey] = &$arr[$oKey];
+        unset($arr[$oKey]);
+    }
+
+    function fixDatabaseKeys($database)
+    {
+        $this->database_definition = array(
+            'name' => '',
+            'create' => '',
+            'overwrite' => '',
+            'charset' => '',
+            'description' => '',
+            'comments' => '',
+            'tables' => array(),
+            'sequences' => array()
+        );
+
+        if (!empty($database['name'])) {
+            $this->database_definition['name'] = $database['name'];
+        }
+        if (!empty($database['create'])) {
+            $this->database_definition['create'] = $database['create'];
+        }
+        if (!empty($database['overwrite'])) {
+            $this->database_definition['overwrite'] = $database['overwrite'];
+        }
+        if (!empty($database['charset'])) {
+            $this->database_definition['charset'] = $database['charset'];
+        }
+        if (!empty($database['description'])) {
+            $this->database_definition['description'] = $database['description'];
+        }
+        if (!empty($database['comments'])) {
+            $this->database_definition['comments'] = $database['comments'];
+        }
+
+        if (!empty($database['table']) && is_array($database['table'])) {
+            foreach ($database['table'] as $table) {
+                $this->fixTableKeys($table);
+            }
+        }
+
+        if (!empty($database['sequence']) && is_array($database['sequence'])) {
+            foreach ($database['sequence'] as $sequence) {
+                $this->fixSequenceKeys($sequence);
+            }
+        }
+
+        $result = $this->val->validateDatabase($this->database_definition);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result->getUserinfo());
+        }
+
+        return MDB2_OK;
+    }
+
+    function fixTableKeys($table)
+    {
+        $this->table = array(
+            'was' => '',
+            'description' => '',
+            'comments' => '',
+            'fields' => array(),
+            'indexes' => array(),
+            'constraints' => array(),
+            'initialization' => array()
+        );
+
+        if (!empty($table['name'])) {
+            $this->table_name = $table['name'];
+        } else {
+            $this->table_name = '';
+        }
+        if (!empty($table['was'])) {
+            $this->table['was'] = $table['was'];
+        }
+        if (!empty($table['description'])) {
+            $this->table['description'] = $table['description'];
+        }
+        if (!empty($table['comments'])) {
+            $this->table['comments'] = $table['comments'];
+        }
+
+        if (!empty($table['declaration']) && is_array($table['declaration'])) {
+            if (!empty($table['declaration']['field']) && is_array($table['declaration']['field'])) {
+                foreach ($table['declaration']['field'] as $field) {
+                    $this->fixTableFieldKeys($field);
+                }
+            }
+
+            if (!empty($table['declaration']['index']) && is_array($table['declaration']['index'])) {
+                foreach ($table['declaration']['index'] as $index) {
+                    $this->fixTableIndexKeys($index);
+                }
+            }
+
+            if (!empty($table['declaration']['foreign']) && is_array($table['declaration']['foreign'])) {
+                foreach ($table['declaration']['foreign'] as $constraint) {
+                    $this->fixTableConstraintKeys($constraint);
+                }
+            }
+        }
+
+        if (!empty($table['initialization']) && is_array($table['initialization'])) {
+            if (!empty($table['initialization']['insert']) && is_array($table['initialization']['insert'])) {
+                foreach ($table['initialization']['insert'] as $init) {
+                    $this->fixTableInitializationKeys($init, 'insert');
+                }
+            }
+            if (!empty($table['initialization']['update']) && is_array($table['initialization']['update'])) {
+                foreach ($table['initialization']['update'] as $init) {
+                    $this->fixTableInitializationKeys($init, 'update');
+                }
+            }
+            if (!empty($table['initialization']['delete']) && is_array($table['initialization']['delete'])) {
+                foreach ($table['initialization']['delete'] as $init) {
+                    $this->fixTableInitializationKeys($init, 'delete');
+                }
+            }
+        }
+
+        $result = $this->val->validateTable($this->database_definition['tables'], $this->table, $this->table_name);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result->getUserinfo());
+        } else {
+            $this->database_definition['tables'][$this->table_name] = $this->table;
+        }
+
+        return MDB2_OK;
+    }
+
+    function fixTableFieldKeys($field)
+    {
+        $this->field = array();
+        if (!empty($field['name'])) {
+            $this->field_name = $field['name'];
+        } else {
+            $this->field_name = '';
+        }
+        if (!empty($field['was'])) {
+            $this->field['was'] = $field['was'];
+        }
+        if (!empty($field['type'])) {
+            $this->field['type'] = $field['type'];
+        }
+        if (!empty($field['fixed'])) {
+            $this->field['fixed'] = $field['fixed'];
+        }
+        if (isset($field['default'])) {
+            $this->field['default'] = $field['default'];
+        }
+        if (!empty($field['notnull'])) {
+            $this->field['notnull'] = $field['notnull'];
+        }
+        if (!empty($field['autoincrement'])) {
+            $this->field['autoincrement'] = $field['autoincrement'];
+        }
+        if (!empty($field['unsigned'])) {
+            $this->field['unsigned'] = $field['unsigned'];
+        }
+        if (!empty($field['length'])) {
+            $this->field['length'] = $field['length'];
+        }
+        if (!empty($field['description'])) {
+            $this->field['description'] = $field['description'];
+        }
+        if (!empty($field['comments'])) {
+            $this->field['comments'] = $field['comments'];
+        }
+
+        $result = $this->val->validateField($this->table['fields'], $this->field, $this->field_name);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result->getUserinfo());
+        } else {
+            $this->table['fields'][$this->field_name] = $this->field;
+        }
+
+        return MDB2_OK;
+    }
+
+    function fixTableIndexKeys($index)
+    {
+        $this->index = array(
+            'was' => '',
+            'unique' =>'',
+            'primary' => '',
+            'fields' => array()
+        );
+
+        if (!empty($index['name'])) {
+            $this->index_name = $index['name'];
+        } else {
+            $this->index_name = '';
+        }
+        if (!empty($index['was'])) {
+            $this->index['was'] = $index['was'];
+        }
+        if (!empty($index['unique'])) {
+            $this->index['unique'] = $index['unique'];
+        }
+        if (!empty($index['primary'])) {
+            $this->index['primary'] = $index['primary'];
+        }
+        if (!empty($index['field'])) {
+            foreach ($index['field'] as $field) {
+                if (!empty($field['name'])) {
+                    $this->field_name = $field['name'];
+                } else {
+                    $this->field_name = '';
+                }
+                $this->field = array(
+                    'sorting' => '',
+                    'length' => ''
+                );
+
+                if (!empty($field['sorting'])) {
+                    $this->field['sorting'] = $field['sorting'];
+                }
+                if (!empty($field['length'])) {
+                    $this->field['length'] = $field['length'];
+                }
+
+                $result = $this->val->validateIndexField($this->index['fields'], $this->field, $this->field_name);
+                if (PEAR::isError($result)) {
+                    return $this->raiseError($result->getUserinfo());
+                }
+
+                $this->index['fields'][$this->field_name] = $this->field;
+            }
+        }
+
+        $result = $this->val->validateIndex($this->table['indexes'], $this->index, $this->index_name);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result->getUserinfo());
+        } else {
+            $this->table['indexes'][$this->index_name] = $this->index;
+        }
+
+        return MDB2_OK;
+    }
+
+    function fixTableConstraintKeys($constraint) 
+    {
+        $this->constraint = array(
+            'was' => '',
+            'match' => '',
+            'ondelete' => '',
+            'onupdate' => '',
+            'deferrable' => '',
+            'initiallydeferred' => '',
+            'foreign' => true,
+            'fields' => array(),
+            'references' => array('table' => '', 'fields' => array())
+        );
+
+        if (!empty($constraint['name'])) {
+            $this->constraint_name = $constraint['name'];
+        } else {
+            $this->constraint_name = '';
+        }
+        if (!empty($constraint['was'])) {
+            $this->constraint['was'] = $constraint['was'];
+        }
+        if (!empty($constraint['match'])) {
+            $this->constraint['match'] = $constraint['match'];
+        }
+        if (!empty($constraint['ondelete'])) {
+            $this->constraint['ondelete'] = $constraint['ondelete'];
+        }
+        if (!empty($constraint['onupdate'])) {
+            $this->constraint['onupdate'] = $constraint['onupdate'];
+        }
+        if (!empty($constraint['deferrable'])) {
+            $this->constraint['deferrable'] = $constraint['deferrable'];
+        }
+        if (!empty($constraint['initiallydeferred'])) {
+            $this->constraint['initiallydeferred'] = $constraint['initiallydeferred'];
+        }
+        if (!empty($constraint['field']) && is_array($constraint['field'])) {
+            foreach ($constraint['field'] as $field) {
+                $result = $this->val->validateConstraintField($this->constraint['fields'], $field);
+                if (PEAR::isError($result)) {
+                    return $this->raiseError($result->getUserinfo());
+                }
+
+                $this->constraint['fields'][$field] = '';
+            }
+        }
+
+        if (!empty($constraint['references']) && is_array($constraint['references'])) {
+            /**
+             * As we forced 'table' to be enumerated
+             * we have to fix it on the foreign-references-table context
+             */
+            if (!empty($constraint['references']['table']) && is_array($constraint['references']['table'])) {
+                $this->constraint['references']['table'] = $constraint['references']['table'][0];
+            }
+
+            if (!empty($constraint['references']['field']) && is_array($constraint['references']['field'])) {
+                foreach ($constraint['references']['field'] as $field) {
+                    $result = $this->val->validateConstraintReferencedField($this->constraint['references']['fields'], $field);
+                    if (PEAR::isError($result)) {
+                        return $this->raiseError($result->getUserinfo());
+                    }
+
+                    $this->constraint['references']['fields'][$field] = '';
+                }
+            }
+        }
+
+        $result = $this->val->validateConstraint($this->table['constraints'], $this->constraint, $this->constraint_name);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result->getUserinfo());
+        } else {
+            $this->table['constraints'][$this->constraint_name] = $this->constraint;
+        }
+
+        return MDB2_OK;
+    }
+
+    function fixTableInitializationKeys($element, $type = '')
+    {
+        if (!empty($element['select']) && is_array($element['select'])) {
+            $this->fixTableInitializationDataKeys($element['select']);
+            $this->init = array( 'select' => $this->init );
+        } else {
+            $this->fixTableInitializationDataKeys($element);
+        }
+
+        $this->table['initialization'][] = array( 'type' => $type, 'data' => $this->init );
+    }
+
+    function fixTableInitializationDataKeys($element)
+    {
+        $this->init = array();
+        if (!empty($element['field']) && is_array($element['field'])) {
+            foreach ($element['field'] as $field) {
+                $name = $field['name'];
+                unset($field['name']);
+
+                $this->setExpression($field);
+                $this->init['field'][] = array( 'name' => $name, 'group' => $field );
+            }
+        }
+        /**
+         * As we forced 'table' to be enumerated
+         * we have to fix it on the insert-select context
+         */
+        if (!empty($element['table']) && is_array($element['table'])) {
+            $this->init['table'] = $element['table'][0];
+        }
+        if (!empty($element['where']) && is_array($element['where'])) {
+            $this->init['where'] = $element['where'];
+            $this->setExpression($this->init['where']);
+        }
+    }
+
+    function setExpression(&$arr)
+    {
+        $element = each($arr);
+
+        $arr = array( 'type' => $element['key'] );
+
+        $element = $element['value'];
+
+        switch ($arr['type']) {
+        case 'null':
+            break;
+        case 'value':
+        case 'column':
+            $arr['data'] = $element;
+            break;
+        case 'function':
+            if (!empty($element)
+                && is_array($element)
+            ) {
+                $arr['data'] = array( 'name' => $element['name'] );
+                unset($element['name']);
+
+                foreach ($element as $type => $value) {
+                    if (!empty($value)) {
+                        if (is_array($value)) {
+                            foreach ($value as $argument) {
+                                $argument = array( $type => $argument );
+                                $this->setExpression($argument);
+                                $arr['data']['arguments'][] = $argument;
+                            }
+                        } else {
+                            $arr['data']['arguments'][] = array( 'type' => $type, 'data' => $value );
+                        }
+                    }
+                }
+            }
+            break;
+        case 'expression':
+            $arr['data'] = array( 'operants' => array(), 'operator' => $element['operator'] );
+            unset($element['operator']);
+
+            foreach ($element as $k => $v) {
+                $argument = array( $k => $v );
+                $this->setExpression($argument);
+                $arr['data']['operants'][] = $argument;
+            }
+            break;
+        }
+    }
+
+    function fixSequenceKeys($sequence)
+    {
+        $this->sequence = array(
+            'was' => '',
+            'start' => '',
+            'description' => '',
+            'comments' => '',
+            'on' => array('table' => '', 'field' => '')
+        );
+
+        if (!empty($sequence['name'])) {
+            $this->sequence_name = $sequence['name'];
+        } else {
+            $this->sequence_name = '';
+        }
+        if (!empty($sequence['was'])) {
+            $this->sequence['was'] = $sequence['was'];
+        }
+        if (!empty($sequence['start'])) {
+            $this->sequence['start'] = $sequence['start'];
+        }
+        if (!empty($sequence['description'])) {
+            $this->sequence['description'] = $sequence['description'];
+        }
+        if (!empty($sequence['comments'])) {
+            $this->sequence['comments'] = $sequence['comments'];
+        }
+        if (!empty($sequence['on']) && is_array($sequence['on'])) {
+            /**
+             * As we forced 'table' to be enumerated
+             * we have to fix it on the sequence-on-table context
+             */
+            if (!empty($sequence['on']['table']) && is_array($sequence['on']['table'])) {
+                $this->sequence['on']['table'] = $sequence['on']['table'][0];
+            }
+
+            /**
+             * As we forced 'field' to be enumerated
+             * we have to fix it on the sequence-on-field context
+             */
+            if (!empty($sequence['on']['field']) && is_array($sequence['on']['field'])) {
+                $this->sequence['on']['field'] = $sequence['on']['field'][0];
+            }
+        }
+
+        $result = $this->val->validateSequence($this->database_definition['sequences'], $this->sequence, $this->sequence_name);
+        if (PEAR::isError($result)) {
+            return $this->raiseError($result->getUserinfo());
+        } else {
+            $this->database_definition['sequences'][$this->sequence_name] = $this->sequence;
+        }
+
+        return MDB2_OK;
+    }
+
+    function &raiseError($msg = null, $ecode = MDB2_SCHEMA_ERROR_PARSE)
+    {
+        if (is_null($this->error)) {
+            $error = 'Parser error: '.$msg."\n";
+
+            $this->error =& MDB2_Schema::raiseError($ecode, null, null, $error);
+        }
+        return $this->error;
+    }
+}
+
+?>
diff --git a/3rdparty/MDB2/Schema/Reserved/ibase.php b/3rdparty/MDB2/Schema/Reserved/ibase.php
new file mode 100644
index 0000000000000000000000000000000000000000..b208abc83a383c6a4d792b7c25b9bbcedb10b6a8
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Reserved/ibase.php
@@ -0,0 +1,436 @@
+<?php
+// {{{ Disclaimer, Licence, copyrights
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Lorenzo Alberton <l.alberton@quipo.it>                       |
+// +----------------------------------------------------------------------+
+//
+// }}}
+// {{{ $GLOBALS['_MDB2_Schema_Reserved']['ibase']
+/**
+ * Has a list of reserved words of Interbase/Firebird
+ *
+ * @package MDB2_Schema
+ * @category Database
+ * @access protected
+ * @author Lorenzo Alberton <l.alberton@quipo.it>
+ */
+$GLOBALS['_MDB2_Schema_Reserved']['ibase'] = array(
+    'ABS',
+    'ABSOLUTE',
+    'ACTION',
+    'ACTIVE',
+    'ADD',
+    'ADMIN',
+    'AFTER',
+    'ALL',
+    'ALLOCATE',
+    'ALTER',
+    'AND',
+    'ANY',
+    'ARE',
+    'AS',
+    'ASC',
+    'ASCENDING',
+    'ASSERTION',
+    'AT',
+    'AUTHORIZATION',
+    'AUTO',
+    'AUTODDL',
+    'AVG',
+    'BACKUP',
+    'BASE_NAME',
+    'BASED',
+    'BASENAME',
+    'BEFORE',
+    'BEGIN',
+    'BETWEEN',
+    'BIGINT',
+    'BIT',
+    'BIT_LENGTH',
+    'BLOB',
+    'BLOCK',
+    'BLOBEDIT',
+    'BOOLEAN',
+    'BOTH',
+    'BOTH',
+    'BREAK',
+    'BUFFER',
+    'BY',
+    'CACHE',
+    'CASCADE',
+    'CASCADED',
+    'CASE',
+    'CASE',
+    'CAST',
+    'CATALOG',
+    'CHAR',
+    'CHAR_LENGTH',
+    'CHARACTER',
+    'CHARACTER_LENGTH',
+    'CHECK',
+    'CHECK_POINT_LEN',
+    'CHECK_POINT_LENGTH',
+    'CLOSE',
+    'COALESCE',
+    'COLLATE',
+    'COLLATION',
+    'COLUMN',
+    'COMMENT',
+    'COMMIT',
+    'COMMITTED',
+    'COMPILETIME',
+    'COMPUTED',
+    'CONDITIONAL',
+    'CONNECT',
+    'CONNECTION',
+    'CONSTRAINT',
+    'CONSTRAINTS',
+    'CONTAINING',
+    'CONTINUE',
+    'CONVERT',
+    'CORRESPONDING',
+    'COUNT',
+    'CREATE',
+    'CROSS',
+    'CSTRING',
+    'CURRENT',
+    'CURRENT_CONNECTION',
+    'CURRENT_DATE',
+    'CURRENT_ROLE',
+    'CURRENT_TIME',
+    'CURRENT_TIMESTAMP',
+    'CURRENT_TRANSACTION',
+    'CURRENT_USER',
+    'DATABASE',
+    'DATE',
+    'DAY',
+    'DB_KEY',
+    'DEALLOCATE',
+    'DEBUG',
+    'DEC',
+    'DECIMAL',
+    'DECLARE',
+    'DEFAULT',
+    'DEFERRABLE',
+    'DEFERRED',
+    'DELETE',
+    'DELETING',
+    'DESC',
+    'DESCENDING',
+    'DESCRIBE',
+    'DESCRIPTOR',
+    'DIAGNOSTICS',
+    'DIFFERENCE',
+    'DISCONNECT',
+    'DISPLAY',
+    'DISTINCT',
+    'DO',
+    'DOMAIN',
+    'DOUBLE',
+    'DROP',
+    'ECHO',
+    'EDIT',
+    'ELSE',
+    'END',
+    'END-EXEC',
+    'ENTRY_POINT',
+    'ESCAPE',
+    'EVENT',
+    'EXCEPT',
+    'EXCEPTION',
+    'EXEC',
+    'EXECUTE',
+    'EXISTS',
+    'EXIT',
+    'EXTERN',
+    'EXTERNAL',
+    'EXTRACT',
+    'FALSE',
+    'FETCH',
+    'FILE',
+    'FILTER',
+    'FIRST',
+    'FLOAT',
+    'FOR',
+    'FOREIGN',
+    'FOUND',
+    'FREE_IT',
+    'FROM',
+    'FULL',
+    'FUNCTION',
+    'GDSCODE',
+    'GEN_ID',
+    'GENERATOR',
+    'GET',
+    'GLOBAL',
+    'GO',
+    'GOTO',
+    'GRANT',
+    'GROUP',
+    'GROUP_COMMIT_WAIT',
+    'GROUP_COMMIT_WAIT_TIME',
+    'HAVING',
+    'HELP',
+    'HOUR',
+    'IDENTITY',
+    'IF',
+    'IIF',
+    'IMMEDIATE',
+    'IN',
+    'INACTIVE',
+    'INDEX',
+    'INDICATOR',
+    'INIT',
+    'INITIALLY',
+    'INNER',
+    'INPUT',
+    'INPUT_TYPE',
+    'INSENSITIVE',
+    'INSERT',
+    'INSERTING',
+    'INT',
+    'INTEGER',
+    'INTERSECT',
+    'INTERVAL',
+    'INTO',
+    'IS',
+    'ISOLATION',
+    'ISQL',
+    'JOIN',
+    'KEY',
+    'LANGUAGE',
+    'LAST',
+    'LC_MESSAGES',
+    'LC_TYPE',
+    'LEADING',
+    'LEADING',
+    'LEADING',
+    'LEAVE',
+    'LEFT',
+    'LENGTH',
+    'LEV',
+    'LEVEL',
+    'LIKE',
+    'LOCAL',
+    'LOCK',
+    'LOG_BUF_SIZE',
+    'LOG_BUFFER_SIZE',
+    'LOGFILE',
+    'LONG',
+    'LOWER',
+    'MANUAL',
+    'MATCH',
+    'MAX',
+    'MAX_SEGMENT',
+    'MAXIMUM',
+    'MAXIMUM_SEGMENT',
+    'MERGE',
+    'MESSAGE',
+    'MIN',
+    'MINIMUM',
+    'MINUTE',
+    'MODULE',
+    'MODULE_NAME',
+    'MONTH',
+    'NAMES',
+    'NATIONAL',
+    'NATURAL',
+    'NCHAR',
+    'NEXT',
+    'NO',
+    'NOAUTO',
+    'NOT',
+    'NULL',
+    'NULLIF',
+    'NULLS',
+    'NUM_LOG_BUFFERS',
+    'NUM_LOG_BUFS',
+    'NUMERIC',
+    'OCTET_LENGTH',
+    'OF',
+    'ON',
+    'ONLY',
+    'OPEN',
+    'OPTION',
+    'OR',
+    'ORDER',
+    'OUTER',
+    'OUTPUT',
+    'OUTPUT_TYPE',
+    'OVERFLOW',
+    'OVERLAPS',
+    'PAD',
+    'PAGE',
+    'PAGE_SIZE',
+    'PAGELENGTH',
+    'PAGES',
+    'PARAMETER',
+    'PARTIAL',
+    'PASSWORD',
+    'PERCENT',
+    'PLAN',
+    'POSITION',
+    'POST_EVENT',
+    'PRECISION',
+    'PREPARE',
+    'PRESERVE',
+    'PRIMARY',
+    'PRIOR',
+    'PRIVILEGES',
+    'PROCEDURE',
+    'PUBLIC',
+    'QUIT',
+    'RAW_PARTITIONS',
+    'RDB$DB_KEY',
+    'READ',
+    'REAL',
+    'RECORD_VERSION',
+    'RECREATE',
+    'RECREATE ROW_COUNT',
+    'REFERENCES',
+    'RELATIVE',
+    'RELEASE',
+    'RESERV',
+    'RESERVING',
+    'RESTART',
+    'RESTRICT',
+    'RETAIN',
+    'RETURN',
+    'RETURNING',
+    'RETURNING_VALUES',
+    'RETURNS',
+    'REVOKE',
+    'RIGHT',
+    'ROLE',
+    'ROLLBACK',
+    'ROW_COUNT',
+    'ROWS',
+    'RUNTIME',
+    'SAVEPOINT',
+    'SCALAR_ARRAY',
+    'SCHEMA',
+    'SCROLL',
+    'SECOND',
+    'SECTION',
+    'SELECT',
+    'SEQUENCE',
+    'SESSION',
+    'SESSION_USER',
+    'SET',
+    'SHADOW',
+    'SHARED',
+    'SHELL',
+    'SHOW',
+    'SINGULAR',
+    'SIZE',
+    'SKIP',
+    'SMALLINT',
+    'SNAPSHOT',
+    'SOME',
+    'SORT',
+    'SPACE',
+    'SQL',
+    'SQLCODE',
+    'SQLERROR',
+    'SQLSTATE',
+    'SQLWARNING',
+    'STABILITY',
+    'STARTING',
+    'STARTS',
+    'STATEMENT',
+    'STATIC',
+    'STATISTICS',
+    'SUB_TYPE',
+    'SUBSTRING',
+    'SUM',
+    'SUSPEND',
+    'SYSTEM_USER',
+    'TABLE',
+    'TEMPORARY',
+    'TERMINATOR',
+    'THEN',
+    'TIES',
+    'TIME',
+    'TIMESTAMP',
+    'TIMEZONE_HOUR',
+    'TIMEZONE_MINUTE',
+    'TO',
+    'TRAILING',
+    'TRANSACTION',
+    'TRANSLATE',
+    'TRANSLATION',
+    'TRIGGER',
+    'TRIM',
+    'TRUE',
+    'TYPE',
+    'UNCOMMITTED',
+    'UNION',
+    'UNIQUE',
+    'UNKNOWN',
+    'UPDATE',
+    'UPDATING',
+    'UPPER',
+    'USAGE',
+    'USER',
+    'USING',
+    'VALUE',
+    'VALUES',
+    'VARCHAR',
+    'VARIABLE',
+    'VARYING',
+    'VERSION',
+    'VIEW',
+    'WAIT',
+    'WEEKDAY',
+    'WHEN',
+    'WHENEVER',
+    'WHERE',
+    'WHILE',
+    'WITH',
+    'WORK',
+    'WRITE',
+    'YEAR',
+    'YEARDAY',
+    'ZONE',
+);
+// }}}
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Schema/Reserved/mssql.php b/3rdparty/MDB2/Schema/Reserved/mssql.php
new file mode 100644
index 0000000000000000000000000000000000000000..74ac688578015f7c552854918cbe97ee4977a96e
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Reserved/mssql.php
@@ -0,0 +1,258 @@
+<?php
+// {{{ Disclaimer, Licence, copyrights
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: David Coallier <davidc@php.net>                              |
+// +----------------------------------------------------------------------+
+// }}}
+// {{{ $GLOBALS['_MDB2_Schema_Reserved']['mssql']
+/**
+ * Has a list of all the reserved words for mssql.
+ *
+ * @package  MDB2_Schema
+ * @category Database
+ * @access   protected
+ * @author   David Coallier <davidc@php.net>
+ */
+$GLOBALS['_MDB2_Schema_Reserved']['mssql'] = array(
+    'ADD',
+    'CURRENT_TIMESTAMP',
+    'GROUP',
+    'OPENQUERY',
+    'SERIALIZABLE',
+    'ALL',
+    'CURRENT_USER',
+    'HAVING',
+    'OPENROWSET',
+    'SESSION_USER',
+    'ALTER',
+    'CURSOR',
+    'HOLDLOCK',
+    'OPTION',
+    'SET',
+    'AND',
+    'DATABASE',
+    'IDENTITY',
+    'OR',
+    'SETUSER',
+    'ANY',
+    'DBCC',
+    'IDENTITYCOL',
+    'ORDER',
+    'SHUTDOWN',
+    'AS',
+    'DEALLOCATE',
+    'IDENTITY_INSERT',
+    'OUTER',
+    'SOME',
+    'ASC',
+    'DECLARE',
+    'IF',
+    'OVER',
+    'STATISTICS',
+    'AUTHORIZATION',
+    'DEFAULT',
+    'IN',
+    'PERCENT',
+    'SUM',
+    'AVG',
+    'DELETE',
+    'INDEX',
+    'PERM',
+    'SYSTEM_USER',
+    'BACKUP',
+    'DENY',
+    'INNER',
+    'PERMANENT',
+    'TABLE',
+    'BEGIN',
+    'DESC',
+    'INSERT',
+    'PIPE',
+    'TAPE',
+    'BETWEEN',
+    'DISK',
+    'INTERSECT',
+    'PLAN',
+    'TEMP',
+    'BREAK',
+    'DISTINCT',
+    'INTO',
+    'PRECISION',
+    'TEMPORARY',
+    'BROWSE',
+    'DISTRIBUTED',
+    'IS',
+    'PREPARE',
+    'TEXTSIZE',
+    'BULK',
+    'DOUBLE',
+    'ISOLATION',
+    'PRIMARY',
+    'THEN',
+    'BY',
+    'DROP',
+    'JOIN',
+    'PRINT',
+    'TO',
+    'CASCADE',
+    'DUMMY',
+    'KEY',
+    'PRIVILEGES',
+    'TOP',
+    'CASE',
+    'DUMP',
+    'KILL',
+    'PROC',
+    'TRAN',
+    'CHECK',
+    'ELSE',
+    'LEFT',
+    'PROCEDURE',
+    'TRANSACTION',
+    'CHECKPOINT',
+    'END',
+    'LEVEL',
+    'PROCESSEXIT',
+    'TRIGGER',
+    'CLOSE',
+    'ERRLVL',
+    'LIKE',
+    'PUBLIC',
+    'TRUNCATE',
+    'CLUSTERED',
+    'ERROREXIT',
+    'LINENO',
+    'RAISERROR',
+    'TSEQUAL',
+    'COALESCE',
+    'ESCAPE',
+    'LOAD',
+    'READ',
+    'UNCOMMITTED',
+    'COLUMN',
+    'EXCEPT',
+    'MAX',
+    'READTEXT',
+    'UNION',
+    'COMMIT',
+    'EXEC',
+    'MIN',
+    'RECONFIGURE',
+    'UNIQUE',
+    'COMMITTED',
+    'EXECUTE',
+    'MIRROREXIT',
+    'REFERENCES',
+    'UPDATE',
+    'COMPUTE',
+    'EXISTS',
+    'NATIONAL',
+    'REPEATABLE',
+    'UPDATETEXT',
+    'CONFIRM',
+    'EXIT',
+    'NOCHECK',
+    'REPLICATION',
+    'USE',
+    'CONSTRAINT',
+    'FETCH',
+    'NONCLUSTERED',
+    'RESTORE',
+    'USER',
+    'CONTAINS',
+    'FILE',
+    'NOT',
+    'RESTRICT',
+    'VALUES',
+    'CONTAINSTABLE',
+    'FILLFACTOR',
+    'NULL',
+    'RETURN',
+    'VARYING',
+    'CONTINUE',
+    'FLOPPY',
+    'NULLIF',
+    'REVOKE',
+    'VIEW',
+    'CONTROLROW',
+    'FOR',
+    'OF',
+    'RIGHT',
+    'WAITFOR',
+    'CONVERT',
+    'FOREIGN',
+    'OFF',
+    'ROLLBACK',
+    'WHEN',
+    'COUNT',
+    'FREETEXT',
+    'OFFSETS',
+    'ROWCOUNT',
+    'WHERE',
+    'CREATE',
+    'FREETEXTTABLE',
+    'ON',
+    'ROWGUIDCOL',
+    'WHILE',
+    'CROSS',
+    'FROM',
+    'ONCE',
+    'RULE',
+    'WITH',
+    'CURRENT',
+    'FULL',
+    'ONLY',
+    'SAVE',
+    'WORK',
+    'CURRENT_DATE',
+    'GOTO',
+    'OPEN',
+    'SCHEMA',
+    'WRITETEXT',
+    'CURRENT_TIME',
+    'GRANT',
+    'OPENDATASOURCE',
+    'SELECT',
+);
+//}}}
+
+?>
diff --git a/3rdparty/MDB2/Schema/Reserved/mysql.php b/3rdparty/MDB2/Schema/Reserved/mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f0575e0bb1018d21ad7cdc1f50c124f8f5b98f6
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Reserved/mysql.php
@@ -0,0 +1,284 @@
+<?php
+// {{{ Disclaimer, Licence, copyrights
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: David Coallier <davidc@php.net>                              |
+// +----------------------------------------------------------------------+
+//
+// $Id: mysql.php,v 1.3 2006/03/01 12:16:40 lsmith Exp $
+// }}}
+// {{{ $GLOBALS['_MDB2_Schema_Reserved']['mysql']
+/**
+ * Has a list of reserved words of mysql
+ *
+ * @package MDB2_Schema
+ * @category Database
+ * @access protected
+ * @author David Coalier <davidc@php.net>
+ */
+$GLOBALS['_MDB2_Schema_Reserved']['mysql'] = array(
+    'ADD',
+    'ALL',
+    'ALTER',
+    'ANALYZE',
+    'AND',
+    'AS',
+    'ASC',
+    'ASENSITIVE',
+    'BEFORE',
+    'BETWEEN',
+    'BIGINT',
+    'BINARY',
+    'BLOB',
+    'BOTH',
+    'BY',
+    'CALL',
+    'CASCADE',
+    'CASE',
+    'CHANGE',
+    'CHAR',
+    'CHARACTER',
+    'CHECK',
+    'COLLATE',
+    'COLUMN',
+    'CONDITION',
+    'CONNECTION',
+    'CONSTRAINT',
+    'CONTINUE',
+    'CONVERT',
+    'CREATE',
+    'CROSS',
+    'CURRENT_DATE',
+    'CURRENT_TIME',
+    'CURRENT_TIMESTAMP',
+    'CURRENT_USER',
+    'CURSOR',
+    'DATABASE',
+    'DATABASES',
+    'DAY_HOUR',
+    'DAY_MICROSECOND',
+    'DAY_MINUTE',
+    'DAY_SECOND',
+    'DEC',
+    'DECIMAL',
+    'DECLARE',
+    'DEFAULT',
+    'DELAYED',
+    'DELETE',
+    'DESC',
+    'DESCRIBE',
+    'DETERMINISTIC',
+    'DISTINCT',
+    'DISTINCTROW',
+    'DIV',
+    'DOUBLE',
+    'DROP',
+    'DUAL',
+    'EACH',
+    'ELSE',
+    'ELSEIF',
+    'ENCLOSED',
+    'ESCAPED',
+    'EXISTS',
+    'EXIT',
+    'EXPLAIN',
+    'FALSE',
+    'FETCH',
+    'FLOAT',
+    'FLOAT4',
+    'FLOAT8',
+    'FOR',
+    'FORCE',
+    'FOREIGN',
+    'FROM',
+    'FULLTEXT',
+    'GOTO',
+    'GRANT',
+    'GROUP',
+    'HAVING',
+    'HIGH_PRIORITY',
+    'HOUR_MICROSECOND',
+    'HOUR_MINUTE',
+    'HOUR_SECOND',
+    'IF',
+    'IGNORE',
+    'IN',
+    'INDEX',
+    'INFILE',
+    'INNER',
+    'INOUT',
+    'INSENSITIVE',
+    'INSERT',
+    'INT',
+    'INT1',
+    'INT2',
+    'INT3',
+    'INT4',
+    'INT8',
+    'INTEGER',
+    'INTERVAL',
+    'INTO',
+    'IS',
+    'ITERATE',
+    'JOIN',
+    'KEY',
+    'KEYS',
+    'KILL',
+    'LABEL',
+    'LEADING',
+    'LEAVE',
+    'LEFT',
+    'LIKE',
+    'LIMIT',
+    'LINES',
+    'LOAD',
+    'LOCALTIME',
+    'LOCALTIMESTAMP',
+    'LOCK',
+    'LONG',
+    'LONGBLOB',
+    'LONGTEXT',
+    'LOOP',
+    'LOW_PRIORITY',
+    'MATCH',
+    'MEDIUMBLOB',
+    'MEDIUMINT',
+    'MEDIUMTEXT',
+    'MIDDLEINT',
+    'MINUTE_MICROSECOND',
+    'MINUTE_SECOND',
+    'MOD',
+    'MODIFIES',
+    'NATURAL',
+    'NOT',
+    'NO_WRITE_TO_BINLOG',
+    'NULL',
+    'NUMERIC',
+    'ON',
+    'OPTIMIZE',
+    'OPTION',
+    'OPTIONALLY',
+    'OR',
+    'ORDER',
+    'OUT',
+    'OUTER',
+    'OUTFILE',
+    'PRECISION',
+    'PRIMARY',
+    'PROCEDURE',
+    'PURGE',
+    'RAID0',
+    'READ',
+    'READS',
+    'REAL',
+    'REFERENCES',
+    'REGEXP',
+    'RELEASE',
+    'RENAME',
+    'REPEAT',
+    'REPLACE',
+    'REQUIRE',
+    'RESTRICT',
+    'RETURN',
+    'REVOKE',
+    'RIGHT',
+    'RLIKE',
+    'SCHEMA',
+    'SCHEMAS',
+    'SECOND_MICROSECOND',
+    'SELECT',
+    'SENSITIVE',
+    'SEPARATOR',
+    'SET',
+    'SHOW',
+    'SMALLINT',
+    'SONAME',
+    'SPATIAL',
+    'SPECIFIC',
+    'SQL',
+    'SQLEXCEPTION',
+    'SQLSTATE',
+    'SQLWARNING',
+    'SQL_BIG_RESULT',
+    'SQL_CALC_FOUND_ROWS',
+    'SQL_SMALL_RESULT',
+    'SSL',
+    'STARTING',
+    'STRAIGHT_JOIN',
+    'TABLE',
+    'TERMINATED',
+    'THEN',
+    'TINYBLOB',
+    'TINYINT',
+    'TINYTEXT',
+    'TO',
+    'TRAILING',
+    'TRIGGER',
+    'TRUE',
+    'UNDO',
+    'UNION',
+    'UNIQUE',
+    'UNLOCK',
+    'UNSIGNED',
+    'UPDATE',
+    'USAGE',
+    'USE',
+    'USING',
+    'UTC_DATE',
+    'UTC_TIME',
+    'UTC_TIMESTAMP',
+    'VALUES',
+    'VARBINARY',
+    'VARCHAR',
+    'VARCHARACTER',
+    'VARYING',
+    'WHEN',
+    'WHERE',
+    'WHILE',
+    'WITH',
+    'WRITE',
+    'X509',
+    'XOR',
+    'YEAR_MONTH',
+    'ZEROFILL',
+    );
+    // }}}
+?>
diff --git a/3rdparty/MDB2/Schema/Reserved/oci8.php b/3rdparty/MDB2/Schema/Reserved/oci8.php
new file mode 100644
index 0000000000000000000000000000000000000000..57fe12ddcab96ac1835ce133c68291add516a2f3
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Reserved/oci8.php
@@ -0,0 +1,171 @@
+<?php
+// {{{ Disclaimer, Licence, copyrights
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: David Coallier <davidc@php.net>                              |
+// +----------------------------------------------------------------------+
+// }}}
+// {{{ $GLOBALS['_MDB2_Schema_Reserved']['oci8']
+/**
+ * Has a list of all the reserved words for oracle.
+ *
+ * @package  MDB2_Schema
+ * @category Database
+ * @access   protected
+ * @author   David Coallier <davidc@php.net>
+ */
+$GLOBALS['_MDB2_Schema_Reserved']['oci8'] = array(
+    'ACCESS',
+    'ELSE',
+    'MODIFY',
+    'START',
+    'ADD',
+    'EXCLUSIVE',
+    'NOAUDIT',
+    'SELECT',
+    'ALL',
+    'EXISTS',
+    'NOCOMPRESS',
+    'SESSION',
+    'ALTER',
+    'FILE',
+    'NOT',
+    'SET',
+    'AND',
+    'FLOAT',
+    'NOTFOUND ',
+    'SHARE',
+    'ANY',
+    'FOR',
+    'NOWAIT',
+    'SIZE',
+    'ARRAYLEN',
+    'FROM',
+    'NULL',
+    'SMALLINT',
+    'AS',
+    'GRANT',
+    'NUMBER',
+    'SQLBUF',
+    'ASC',
+    'GROUP',
+    'OF',
+    'SUCCESSFUL',
+    'AUDIT',
+    'HAVING',
+    'OFFLINE ',
+    'SYNONYM',
+    'BETWEEN',
+    'IDENTIFIED',
+    'ON',
+    'SYSDATE',
+    'BY',
+    'IMMEDIATE',
+    'ONLINE',
+    'TABLE',
+    'CHAR',
+    'IN',
+    'OPTION',
+    'THEN',
+    'CHECK',
+    'INCREMENT',
+    'OR',
+    'TO',
+    'CLUSTER',
+    'INDEX',
+    'ORDER',
+    'TRIGGER',
+    'COLUMN',
+    'INITIAL',
+    'PCTFREE',
+    'UID',
+    'COMMENT',
+    'INSERT',
+    'PRIOR',
+    'UNION',
+    'COMPRESS',
+    'INTEGER',
+    'PRIVILEGES',
+    'UNIQUE',
+    'CONNECT',
+    'INTERSECT',
+    'PUBLIC',
+    'UPDATE',
+    'CREATE',
+    'INTO',
+    'RAW',
+    'USER',
+    'CURRENT',
+    'IS',
+    'RENAME',
+    'VALIDATE',
+    'DATE',
+    'LEVEL',
+    'RESOURCE',
+    'VALUES',
+    'DECIMAL',
+    'LIKE',
+    'REVOKE',
+    'VARCHAR',
+    'DEFAULT',
+    'LOCK',
+    'ROW',
+    'VARCHAR2',
+    'DELETE',
+    'LONG',
+    'ROWID',
+    'VIEW',
+    'DESC',
+    'MAXEXTENTS',
+    'ROWLABEL',
+    'WHENEVER',
+    'DISTINCT',
+    'MINUS',
+    'ROWNUM',
+    'WHERE',
+    'DROP',
+    'MODE',
+    'ROWS',
+    'WITH',
+);
+// }}}
+
+?>
diff --git a/3rdparty/MDB2/Schema/Reserved/pgsql.php b/3rdparty/MDB2/Schema/Reserved/pgsql.php
new file mode 100644
index 0000000000000000000000000000000000000000..d358e9c12f033a42b806fb5645a4caa9d1a74ed5
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Reserved/pgsql.php
@@ -0,0 +1,147 @@
+<?php
+// {{{ Disclaimer, Licence, copyrights
+// +----------------------------------------------------------------------+
+// | PHP versions 4 and 5                                                 |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1998-2006 Manuel Lemos, Tomas V.V.Cox,                 |
+// | Stig. S. Bakken, Lukas Smith                                         |
+// | 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: Marcelo Santos Araujo <msaraujo@php.net>                     |
+// +----------------------------------------------------------------------+
+//
+// }}}
+// {{{ $GLOBALS['_MDB2_Schema_Reserved']['pgsql']
+/**
+ * Has a list of reserved words of pgsql
+ *
+ * @package MDB2_Schema
+ * @category Database
+ * @access protected
+ * @author Marcelo Santos Araujo <msaraujo@php.net>
+ */
+$GLOBALS['_MDB2_Schema_Reserved']['pgsql'] = array(
+    'ALL',
+    'ANALYSE',
+    'ANALYZE',
+    'AND',
+    'ANY',
+    'AS',
+    'ASC',
+    'AUTHORIZATION',
+    'BETWEEN',
+    'BINARY',
+    'BOTH',
+    'CASE',
+    'CAST',
+    'CHECK',
+    'COLLATE',
+    'COLUMN',
+    'CONSTRAINT',
+    'CREATE',
+    'CURRENT_DATE',
+    'CURRENT_TIME',
+    'CURRENT_TIMESTAMP',
+    'CURRENT_USER',
+    'DEFAULT',
+    'DEFERRABLE',
+    'DESC',
+    'DISTINCT',
+    'DO',
+    'ELSE',
+    'END',
+    'EXCEPT',
+    'FALSE',
+    'FOR',
+    'FOREIGN',
+    'FREEZE',
+    'FROM',
+    'FULL',
+    'GRANT',
+    'GROUP',
+    'HAVING',
+    'ILIKE',
+    'IN',
+    'INITIALLY',
+    'INNER',
+    'INTERSECT',
+    'INTO',
+    'IS',
+    'ISNULL',
+    'JOIN',
+    'LEADING',
+    'LEFT',
+    'LIKE',
+    'LIMIT',
+    'LOCALTIME',
+    'LOCALTIMESTAMP',
+    'NATURAL',
+    'NEW',
+    'NOT',
+    'NOTNULL',
+    'NULL',
+    'OFF',
+    'OFFSET',
+    'OLD',
+    'ON',
+    'ONLY',
+    'OR',
+    'ORDER',
+    'OUTER',
+    'OVERLAPS',
+    'PLACING',
+    'PRIMARY',
+    'REFERENCES',
+    'SELECT',
+    'SESSION_USER',
+    'SIMILAR',
+    'SOME',
+    'TABLE',
+    'THEN',
+    'TO',
+    'TRAILING',
+    'TRUE',
+    'UNION',
+    'UNIQUE',
+    'USER',
+    'USING',
+    'VERBOSE',
+    'WHEN',
+    'WHERE'
+);
+// }}}
+?>
+
diff --git a/3rdparty/MDB2/Schema/Tool.php b/3rdparty/MDB2/Schema/Tool.php
new file mode 100644
index 0000000000000000000000000000000000000000..9689a0f6d73ef6ee01d01246a1f4e1b449af6333
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Tool.php
@@ -0,0 +1,560 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
+ * Stig. S. Bakken, Lukas Smith, Igor Feghali
+ * All rights reserved.
+ *
+ * MDB2_Schema enables users to maintain RDBMS independant schema files
+ * in XML that can be used to manipulate both data and database schemas
+ * 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, Igor Feghali 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: Christian Weiske <cweiske@php.net>
+ * $Id: Tool.php,v 1.6 2008/12/13 00:26:07 clockwerx Exp $
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Christian Weiske <cweiske@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @version  CVS: $Id: Tool.php,v 1.6 2008/12/13 00:26:07 clockwerx Exp $
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+
+require_once 'MDB2/Schema.php';
+require_once 'MDB2/Schema/Tool/ParameterException.php';
+
+/**
+* Command line tool to work with database schemas
+*
+* Functionality:
+* - dump a database schema to stdout
+* - import schema into database
+* - create a diff between two schemas
+* - apply diff to database
+*
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Christian Weiske <cweiske@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema_Tool
+{
+    /**
+    * Run the schema tool
+    *
+    * @param array $args Array of command line arguments
+    */
+    public function __construct($args)
+    {
+        $strAction = $this->getAction($args);
+        try {
+            $this->{'do' . ucfirst($strAction)}($args);
+        } catch (MDB2_Schema_Tool_ParameterException $e) {
+            $this->{'doHelp' . ucfirst($strAction)}($e->getMessage());
+        }
+    }//public function __construct($args)
+
+
+
+    /**
+    * Runs the tool with command line arguments
+    *
+    * @return void
+    */
+    public static function run()
+    {
+        $args = $GLOBALS['argv'];
+        array_shift($args);
+
+        try {
+            $tool = new MDB2_Schema_Tool($args);
+        } catch (Exception $e) {
+            self::toStdErr($e->getMessage() . "\n");
+        }
+    }//public static function run()
+
+
+
+    /**
+    * Reads the first parameter from the argument array and
+    * returns the action.
+    *
+    * @param array &$args Command line parameters
+    *
+    * @return string Action to execute
+    */
+    protected function getAction(&$args)
+    {
+        if (count($args) == 0) {
+            return 'help';
+        }
+        $arg = array_shift($args);
+        switch ($arg) {
+        case 'h':
+        case 'help':
+        case '-h':
+        case '--help':
+            return 'help';
+        case 'd':
+        case 'dump':
+        case '-d':
+        case '--dump':
+            return 'dump';
+        case 'l':
+        case 'load':
+        case '-l':
+        case '--load':
+            return 'load';
+        case 'i':
+        case 'diff':
+        case '-i':
+        case '--diff':
+            return 'diff';
+        case 'a':
+        case 'apply':
+        case '-a':
+        case '--apply':
+            return 'apply';
+        case 'n':
+        case 'init':
+        case '-i':
+        case '--init':
+            return 'init';
+        default:
+            throw new MDB2_Schema_Tool_ParameterException("Unknown mode \"$arg\"");
+        }
+    }//protected function getAction(&$args)
+
+
+
+    /**
+    * Writes the message to stderr
+    *
+    * @param string $msg Message to print
+    *
+    * @return void
+    */
+    protected static function toStdErr($msg)
+    {
+        file_put_contents('php://stderr', $msg);
+    }//protected static function toStdErr($msg)
+
+
+
+    /**
+    * Displays generic help to stdout
+    *
+    * @return void
+    */
+    protected function doHelp()
+    {
+        self::toStdErr(<<<EOH
+Usage: mdb2_schematool mode parameters
+
+Works with database schemas
+
+mode: (- and -- are optional)
+ h,  help     Show this help screen
+ d,  dump     Dump a schema to stdout
+ l,  load     Load a schema into database
+ i,  diff     Create a diff between two schemas and dump it to stdout
+ a,  apply    Apply a diff to a database
+ n,  init     Initialize a database with data
+
+EOH
+        );
+    }//protected function doHelp()
+
+
+
+    /**
+    * Displays the help screen for "dump" command
+    *
+    * @return void
+    */
+    protected function doHelpDump()
+    {
+        self::toStdErr( <<<EOH
+Usage: mdb2_schematool dump [all|data|schema] [-p] DSN
+
+Dumps a database schema to stdout
+
+If dump type is not specified, defaults to "schema".
+
+DSN: Data source name in the form of
+ driver://user:password@host/database
+
+User and password may be omitted.
+Using -p reads password from stdin which is more secure than passing it in the parameter.
+
+EOH
+        );
+    }//protected function doHelpDump()
+
+
+
+    /**
+    * Displays the help screen for "init" command
+    *
+    * @return void
+    */
+    protected function doHelpInit()
+    {
+        self::toStdErr( <<<EOH
+Usage: mdb2_schematool init source [-p] destination
+
+Initializes a database with data
+ (Inserts data on a previous created database at destination)
+
+source should be a schema file containing data,
+destination should be a DSN
+
+DSN: Data source name in the form of
+ driver://user:password@host/database
+
+User and password may be omitted.
+Using -p reads password from stdin which is more secure than passing it in the parameter.
+
+EOH
+        );
+    }//protected function doHelpInit()
+
+
+
+    /**
+    * Displays the help screen for "load" command
+    *
+    * @return void
+    */
+    protected function doHelpLoad()
+    {
+        self::toStdErr( <<<EOH
+Usage: mdb2_schematool load [-p] source [-p] destination
+
+Loads a database schema from source to destination
+ (Creates the database schema at destination)
+
+source can be a DSN or a schema file,
+destination should be a DSN
+
+DSN: Data source name in the form of
+ driver://user:password@host/database
+
+User and password may be omitted.
+Using -p reads password from stdin which is more secure than passing it in the parameter.
+
+EOH
+        );
+    }//protected function doHelpLoad()
+
+
+
+    /**
+    * Returns an array of options for MDB2_Schema constructor
+    *
+    * @return array Options for MDB2_Schema constructor
+    */
+    protected function getSchemaOptions()
+    {
+        $options = array(
+            'log_line_break' => '<br>',
+            'idxname_format' => '%s',
+            'debug' => true,
+            'quote_identifier' => true,
+            'force_defaults' => false,
+            'portability' => true,
+            'use_transactions' => false,
+        );
+        return $options;
+    }//protected function getSchemaOptions()
+
+
+
+    /**
+    * Checks if the passed parameter is a PEAR_Error object
+    * and throws an exception in that case.
+    *
+    * @param mixed  $object   Some variable to check
+    * @param string $location Where the error occured
+    *
+    * @return void
+    */
+    protected function throwExceptionOnError($object, $location = '')
+    {
+        if (PEAR::isError($object)) {
+            //FIXME: exception class
+            //debug_print_backtrace();
+            throw new Exception('Error ' . $location
+                . "\n" . $object->getMessage()
+                . "\n" . $object->getUserInfo()
+            );
+        }
+    }//protected function throwExceptionOnError($object, $location = '')
+
+
+
+    /**
+    * Loads a file or a dsn from the arguments
+    *
+    * @param array &$args Array of arguments to the program
+    *
+    * @return array Array of ('file'|'dsn', $value)
+    */
+    protected function getFileOrDsn(&$args)
+    {
+        if (count($args) == 0) {
+            throw new MDB2_Schema_Tool_ParameterException('File or DSN expected');
+        }
+
+        $arg = array_shift($args);
+        if ($arg == '-p') {
+            $bAskPassword = true;
+            $arg          = array_shift($args);
+        } else {
+            $bAskPassword = false;
+        }
+
+        if (strpos($arg, '://') === false) {
+            if (file_exists($arg)) {
+                //File
+                return array('file', $arg);
+            } else {
+                throw new Exception('Schema file does not exist');
+            }
+        }
+
+        //read password if necessary
+        if ($bAskPassword) {
+            $password = $this->readPasswordFromStdin($arg);
+            $arg      = self::setPasswordIntoDsn($arg, $password);
+            self::toStdErr($arg);
+        }
+        return array('dsn', $arg);
+    }//protected function getFileOrDsn(&$args)
+
+
+
+    /**
+    * Takes a DSN data source name and integrates the given
+    * password into it.
+    *
+    * @param string $dsn      Data source name
+    * @param string $password Password
+    *
+    * @return string DSN with password
+    */
+    protected function setPasswordIntoDsn($dsn, $password)
+    {
+        //simple try to integrate password
+        if (strpos($dsn, '@') === false) {
+            //no @ -> no user and no password
+            return str_replace('://', '://:' . $password . '@', $dsn);
+        } else if (preg_match('|://[^:]+@|', $dsn)) {
+            //user only, no password
+            return str_replace('@', ':' . $password . '@', $dsn);
+        } else if (strpos($dsn, ':@') !== false) {
+            //abstract version
+            return str_replace(':@', ':' . $password . '@', $dsn);
+        }
+
+        return $dsn;
+    }//protected function setPasswordIntoDsn($dsn, $password)
+
+
+
+    /**
+    * Reads a password from stdin
+    *
+    * @param string $dsn DSN name to put into the message
+    *
+    * @return string Password
+    */
+    protected function readPasswordFromStdin($dsn)
+    {
+        $stdin = fopen('php://stdin', 'r');
+        self::toStdErr('Please insert password for ' . $dsn . "\n");
+        $password = '';
+        $breakme  = false;
+        while (false !== ($char = fgetc($stdin))) {
+            if (ord($char) == 10 || $char == "\n" || $char == "\r") {
+                break;
+            }
+            $password .= $char;
+        }
+        fclose($stdin);
+
+        return trim($password);
+    }//protected function readPasswordFromStdin()
+
+
+
+    /**
+    * Creates a database schema dump and sends it to stdout
+    *
+    * @param array $args Command line arguments
+    *
+    * @return void
+    */
+    protected function doDump($args)
+    {
+        $dump_what = MDB2_SCHEMA_DUMP_STRUCTURE;
+        $arg = '';
+        if (count($args)) {
+            $arg = $args[0];
+        }
+
+        switch (strtolower($arg)) {
+        case 'all':
+            $dump_what = MDB2_SCHEMA_DUMP_ALL;
+            array_shift($args);
+            break;
+        case 'data':
+            $dump_what = MDB2_SCHEMA_DUMP_CONTENT;
+            array_shift($args);
+            break;
+        case 'schema':
+            array_shift($args);
+        }
+
+        list($type, $dsn) = $this->getFileOrDsn($args);
+        if ($type == 'file') {
+            throw new MDB2_Schema_Tool_ParameterException(
+                'Dumping a schema file as a schema file does not make much sense'
+            );
+        }
+
+        $schema = MDB2_Schema::factory($dsn, $this->getSchemaOptions());
+        $this->throwExceptionOnError($schema);
+
+        $definition = $schema->getDefinitionFromDatabase();
+        $this->throwExceptionOnError($definition);
+
+
+        $dump_options = array(
+            'output_mode' => 'file',
+            'output' => 'php://stdout',
+            'end_of_line' => "\r\n"
+        );
+        $op = $schema->dumpDatabase(
+            $definition, $dump_options, $dump_what
+        );
+        $this->throwExceptionOnError($op);
+
+        $schema->disconnect();
+    }//protected function doDump($args)
+
+
+
+    /**
+    * Loads a database schema
+    *
+    * @param array $args Command line arguments
+    *
+    * @return void
+    */
+    protected function doLoad($args)
+    {
+        list($typeSource, $dsnSource) = $this->getFileOrDsn($args);
+        list($typeDest,   $dsnDest)   = $this->getFileOrDsn($args);
+
+        if ($typeDest == 'file') {
+            throw new MDB2_Schema_Tool_ParameterException(
+                'A schema can only be loaded into a database, not a file'
+            );
+        }
+
+
+        $schemaDest = MDB2_Schema::factory($dsnDest, $this->getSchemaOptions());
+        $this->throwExceptionOnError($schemaDest);
+
+        //load definition
+        if ($typeSource == 'file') {
+            $definition = $schemaDest->parseDatabaseDefinitionFile($dsnSource);
+            $where      = 'loading schema file';
+        } else {
+            $schemaSource = MDB2_Schema::factory($dsnSource, $this->getSchemaOptions());
+            $this->throwExceptionOnError($schemaSource, 'connecting to source database');
+
+            $definition = $schemaSource->getDefinitionFromDatabase();
+            $where      = 'loading definition from database';
+        }
+        $this->throwExceptionOnError($definition, $where);
+
+
+        //create destination database from definition
+        $simulate = false;
+        $op       = $schemaDest->createDatabase($definition, array(), $simulate);
+        $this->throwExceptionOnError($op, 'creating the database');
+    }//protected function doLoad($args)
+
+
+
+    /**
+    * Initializes a database with data
+    *
+    * @param array $args Command line arguments
+    *
+    * @return void
+    */
+    protected function doInit($args)
+    {
+        list($typeSource, $dsnSource) = $this->getFileOrDsn($args);
+        list($typeDest,   $dsnDest)   = $this->getFileOrDsn($args);
+
+        if ($typeSource != 'file') {
+            throw new MDB2_Schema_Tool_ParameterException(
+                'Data must come from a source file'
+            );
+        }
+
+        if ($typeDest != 'dsn') {
+            throw new MDB2_Schema_Tool_ParameterException(
+                'A schema can only be loaded into a database, not a file'
+            );
+        }
+
+        $schemaDest = MDB2_Schema::factory($dsnDest, $this->getSchemaOptions());
+        $this->throwExceptionOnError($schemaDest, 'connecting to destination database');
+
+        $definition = $schemaDest->getDefinitionFromDatabase();
+        $this->throwExceptionOnError($definition, 'loading definition from database');
+
+        $op = $schemaDest->writeInitialization($dsnSource, $definition);
+        $this->throwExceptionOnError($op, 'initializing database');
+    }//protected function doInit($args)
+
+
+}//class MDB2_Schema_Tool
+
+?>
diff --git a/3rdparty/MDB2/Schema/Tool/ParameterException.php b/3rdparty/MDB2/Schema/Tool/ParameterException.php
new file mode 100644
index 0000000000000000000000000000000000000000..fab1e03e250de7206d7ccf7f195354fb8edfc4a2
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Tool/ParameterException.php
@@ -0,0 +1,6 @@
+<?php
+
+class MDB2_Schema_Tool_ParameterException extends Exception
+{}
+
+?>
\ No newline at end of file
diff --git a/3rdparty/MDB2/Schema/Validate.php b/3rdparty/MDB2/Schema/Validate.php
new file mode 100644
index 0000000000000000000000000000000000000000..217cf51b9592545c892d16e2108912f7e8606750
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Validate.php
@@ -0,0 +1,917 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
+ * Stig. S. Bakken, Lukas Smith, Igor Feghali
+ * All rights reserved.
+ *
+ * MDB2_Schema enables users to maintain RDBMS independant schema files
+ * in XML that can be used to manipulate both data and database schemas
+ * 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, Igor Feghali 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: Christian Dickmann <dickmann@php.net>
+ * Author: Igor Feghali <ifeghali@php.net>
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Christian Dickmann <dickmann@php.net>
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @version  CVS: $Id: Validate.php,v 1.42 2008/11/30 03:34:00 clockwerx Exp $
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+
+/**
+ * Validates an XML schema file
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema_Validate
+{
+    // {{{ properties
+
+    var $fail_on_invalid_names = true;
+
+    var $valid_types = array();
+
+    var $force_defaults = true;
+
+    // }}}
+    // {{{ constructor
+
+    function __construct($fail_on_invalid_names = true, $valid_types = array(), $force_defaults = true)
+    {
+        if (empty($GLOBALS['_MDB2_Schema_Reserved'])) {
+            $GLOBALS['_MDB2_Schema_Reserved'] = array();
+        }
+
+        if (is_array($fail_on_invalid_names)) {
+            $this->fail_on_invalid_names = array_intersect($fail_on_invalid_names,
+                                                           array_keys($GLOBALS['_MDB2_Schema_Reserved']));
+        } elseif ($fail_on_invalid_names === true) {
+            $this->fail_on_invalid_names = array_keys($GLOBALS['_MDB2_Schema_Reserved']);
+        } else {
+            $this->fail_on_invalid_names = array();
+        }
+        $this->valid_types    = $valid_types;
+        $this->force_defaults = $force_defaults;
+    }
+
+    // }}}
+    // {{{ raiseError()
+
+    function &raiseError($ecode, $msg = null)
+    {
+        $error =& MDB2_Schema::raiseError($ecode, null, null, $msg);
+        return $error;
+    }
+
+    // }}}
+    // {{{ isBoolean()
+
+    /**
+     * Verifies if a given value can be considered boolean. If yes, set value
+     * to true or false according to its actual contents and return true. If
+     * not, keep its contents untouched and return false.
+     *
+     * @param mixed &$value value to be checked
+     *
+     * @return bool
+     *
+     * @access public
+     * @static
+     */
+    function isBoolean(&$value)
+    {
+        if (is_bool($value)) {
+            return true;
+        }
+
+        if ($value === 0 || $value === 1 || $value === '') {
+            $value = (bool)$value;
+            return true;
+        }
+
+        if (!is_string($value)) {
+            return false;
+        }
+
+        switch ($value) {
+        case '0':
+        case 'N':
+        case 'n':
+        case 'no':
+        case 'false':
+            $value = false;
+            break;
+        case '1':
+        case 'Y':
+        case 'y':
+        case 'yes':
+        case 'true':
+            $value = true;
+            break;
+        default:
+            return false;
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ validateTable()
+
+    /* Definition */
+    /**
+     * Checks whether the definition of a parsed table is valid. Modify table
+     * definition when necessary.
+     *
+     * @param array  $tables     multi dimensional array that contains the
+     *                           tables of current database.
+     * @param array  &$table     multi dimensional array that contains the
+     *                           structure and optional data of the table.
+     * @param string $table_name name of the parsed table
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateTable($tables, &$table, $table_name)
+    {
+        /* Have we got a name? */
+        if (!$table_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'a table has to have a name');
+        }
+
+        /* Table name duplicated? */
+        if (is_array($tables) && isset($tables[$table_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'table "'.$table_name.'" already exists');
+        }
+
+        /* Table name reserved? */
+        if (is_array($this->fail_on_invalid_names)) {
+            $name = strtoupper($table_name);
+            foreach ($this->fail_on_invalid_names as $rdbms) {
+                if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                        'table name "'.$table_name.'" is a reserved word in: '.$rdbms);
+                }
+            }
+        }
+
+        /* Was */
+        if (empty($table['was'])) {
+            $table['was'] = $table_name;
+        }
+
+        /* Have we got fields? */
+        if (empty($table['fields']) || !is_array($table['fields'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'tables need one or more fields');
+        }
+
+        /* Autoincrement */
+        $autoinc = $primary = false;
+        foreach ($table['fields'] as $field_name => $field) {
+            if (!empty($field['autoincrement'])) {
+                if ($autoinc) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                        'there was already an autoincrement field in "'.$table_name.'" before "'.$field_name.'"');
+                }
+                $autoinc = $field_name;
+            }
+        }
+
+        /*
+         * Checking Indexes
+         * this have to be done here otherwise we can't
+         * guarantee that all table fields were already
+         * defined in the moment we are parsing indexes
+         */
+        if (!empty($table['indexes']) && is_array($table['indexes'])) {
+            foreach ($table['indexes'] as $name => $index) {
+                $skip_index = false;
+                if (!empty($index['primary'])) {
+                    /*
+                     * Lets see if we should skip this index since there is
+                     * already an auto increment on this field this implying
+                     * a primary key index.
+                     */
+                    if (count($index['fields']) == '1'
+                        && $autoinc
+                        && array_key_exists($autoinc, $index['fields'])) {
+                        $skip_index = true;
+                    } elseif ($autoinc || $primary) {
+                        return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                            'there was already an primary index or autoincrement field in "'.$table_name.'" before "'.$name.'"');
+                    } else {
+                        $primary = true;
+                    }
+                }
+
+                if (!$skip_index && is_array($index['fields'])) {
+                    foreach ($index['fields'] as $field_name => $field) {
+                        if (!isset($table['fields'][$field_name])) {
+                            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                                'index field "'.$field_name.'" does not exist');
+                        }
+                        if (!empty($index['primary'])
+                            && !$table['fields'][$field_name]['notnull']
+                        ) {
+                            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                                'all primary key fields must be defined notnull in "'.$table_name.'"');
+                        }
+                    }
+                } else {
+                    unset($table['indexes'][$name]);
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateField()
+
+    /**
+     * Checks whether the definition of a parsed field is valid. Modify field
+     * definition when necessary.
+     *
+     * @param array  $fields     multi dimensional array that contains the
+     *                           fields of current table.
+     * @param array  &$field     multi dimensional array that contains the
+     *                           structure of the parsed field.
+     * @param string $field_name name of the parsed field
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateField($fields, &$field, $field_name)
+    {
+        /* Have we got a name? */
+        if (!$field_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field name missing');
+        }
+
+        /* Field name duplicated? */
+        if (is_array($fields) && isset($fields[$field_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "'.$field_name.'" already exists');
+        }
+
+        /* Field name reserverd? */
+        if (is_array($this->fail_on_invalid_names)) {
+            $name = strtoupper($field_name);
+            foreach ($this->fail_on_invalid_names as $rdbms) {
+                if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                        'field name "'.$field_name.'" is a reserved word in: '.$rdbms);
+                }
+            }
+        }
+
+        /* Type check */
+        if (empty($field['type'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'no field type specified');
+        }
+        if (!empty($this->valid_types) && !array_key_exists($field['type'], $this->valid_types)) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'no valid field type ("'.$field['type'].'") specified');
+        }
+
+        /* Unsigned */
+        if (array_key_exists('unsigned', $field) && !$this->isBoolean($field['unsigned'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'unsigned has to be a boolean value');
+        }
+
+        /* Fixed */
+        if (array_key_exists('fixed', $field) && !$this->isBoolean($field['fixed'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'fixed has to be a boolean value');
+        }
+
+        /* Length */
+        if (array_key_exists('length', $field) && $field['length'] <= 0) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'length has to be an integer greater 0');
+        }
+
+        // if it's a DECIMAL datatype, check if a 'scale' value is provided:
+        // <length>8,4</length> should be translated to DECIMAL(8,4)
+        if (is_float($this->valid_types[$field['type']])
+            && !empty($field['length'])
+            && strpos($field['length'], ',') !== false
+        ) {
+            list($field['length'], $field['scale']) = explode(',', $field['length']);
+        }
+
+        /* Was */
+        if (empty($field['was'])) {
+            $field['was'] = $field_name;
+        }
+
+        /* Notnull */
+        if (empty($field['notnull'])) {
+            $field['notnull'] = false;
+        }
+        if (!$this->isBoolean($field['notnull'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "notnull" has to be a boolean value');
+        }
+
+        /* Default */
+        if ($this->force_defaults
+            && !array_key_exists('default', $field)
+            && $field['type'] != 'clob' && $field['type'] != 'blob'
+        ) {
+            $field['default'] = $this->valid_types[$field['type']];
+        }
+        if (array_key_exists('default', $field)) {
+            if ($field['type'] == 'clob' || $field['type'] == 'blob') {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field['type'].'"-fields are not allowed to have a default value');
+            }
+            if ($field['default'] === '' && !$field['notnull']) {
+                $field['default'] = null;
+            }
+        }
+        if (isset($field['default'])
+            && PEAR::isError($result = $this->validateDataFieldValue($field, $field['default'], $field_name))
+        ) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'default value of "'.$field_name.'" is incorrect: '.$result->getUserinfo());
+        }
+
+        /* Autoincrement */
+        if (!empty($field['autoincrement'])) {
+            if (!$field['notnull']) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    'all autoincrement fields must be defined notnull');
+            }
+
+            if (empty($field['default'])) {
+                $field['default'] = '0';
+            } elseif ($field['default'] !== '0' && $field['default'] !== 0) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    'all autoincrement fields must be defined default "0"');
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateIndex()
+
+    /**
+     * Checks whether a parsed index is valid. Modify index definition when
+     * necessary.
+     *
+     * @param array  $table_indexes multi dimensional array that contains the
+     *                              indexes of current table.
+     * @param array  &$index        multi dimensional array that contains the
+     *                              structure of the parsed index.
+     * @param string $index_name    name of the parsed index
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateIndex($table_indexes, &$index, $index_name)
+    {
+        if (!$index_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'an index has to have a name');
+        }
+        if (is_array($table_indexes) && isset($table_indexes[$index_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'index "'.$index_name.'" already exists');
+        }
+        if (array_key_exists('unique', $index) && !$this->isBoolean($index['unique'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "unique" has to be a boolean value');
+        }
+        if (array_key_exists('primary', $index) && !$this->isBoolean($index['primary'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "primary" has to be a boolean value');
+        }
+
+        /* Have we got fields? */
+        if (empty($index['fields']) || !is_array($index['fields'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'indexes need one or more fields');
+        }
+
+        if (empty($index['was'])) {
+            $index['was'] = $index_name;
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateIndexField()
+
+    /**
+     * Checks whether a parsed index-field is valid. Modify its definition when
+     * necessary.
+     *
+     * @param array  $index_fields multi dimensional array that contains the
+     *                             fields of current index.
+     * @param array  &$field       multi dimensional array that contains the
+     *                             structure of the parsed index-field.
+     * @param string $field_name   name of the parsed index-field
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateIndexField($index_fields, &$field, $field_name)
+    {
+        if (is_array($index_fields) && isset($index_fields[$field_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'index field "'.$field_name.'" already exists');
+        }
+        if (!$field_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'the index-field-name is required');
+        }
+        if (empty($field['sorting'])) {
+            $field['sorting'] = 'ascending';
+        } elseif ($field['sorting'] !== 'ascending' && $field['sorting'] !== 'descending') {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'sorting type unknown');
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateConstraint()
+
+    /**
+     * Checks whether a parsed foreign key is valid. Modify its definition when
+     * necessary.
+     *
+     * @param array  $table_constraints multi dimensional array that contains the
+     *                                  constraints of current table.
+     * @param array  &$constraint       multi dimensional array that contains the
+     *                                  structure of the parsed foreign key.
+     * @param string $constraint_name   name of the parsed foreign key
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateConstraint($table_constraints, &$constraint, $constraint_name)
+    {
+        if (!$constraint_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'a foreign key has to have a name');
+        }
+        if (is_array($table_constraints) && isset($table_constraints[$constraint_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'foreign key "'.$constraint_name.'" already exists');
+        }
+
+        /* Have we got fields? */
+        if (empty($constraint['fields']) || !is_array($constraint['fields'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'foreign key "'.$constraint_name.'" need one or more fields');
+        }
+
+        /* Have we got referenced fields? */
+        if (empty($constraint['references']) || !is_array($constraint['references'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'foreign key "'.$constraint_name.'" need to reference one or more fields');
+        }
+
+        /* Have we got referenced table? */
+        if (empty($constraint['references']['table'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'foreign key "'.$constraint_name.'" need to reference a table');
+        }
+
+        if (empty($constraint['was'])) {
+            $constraint['was'] = $constraint_name;
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateConstraintField()
+
+    /**
+     * Checks whether a foreign-field is valid.
+     *
+     * @param array  $constraint_fields multi dimensional array that contains the
+     *                                  fields of current foreign key.
+     * @param string $field_name        name of the parsed foreign-field
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateConstraintField($constraint_fields, $field_name)
+    {
+        if (!$field_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'empty value for foreign-field');
+        }
+        if (is_array($constraint_fields) && isset($constraint_fields[$field_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'foreign field "'.$field_name.'" already exists');
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateConstraintReferencedField()
+
+    /**
+     * Checks whether a foreign-referenced field is valid.
+     *
+     * @param array  $referenced_fields multi dimensional array that contains the
+     *                                  fields of current foreign key.
+     * @param string $field_name        name of the parsed foreign-field
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateConstraintReferencedField($referenced_fields, $field_name)
+    {
+        if (!$field_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'empty value for referenced foreign-field');
+        }
+        if (is_array($referenced_fields) && isset($referenced_fields[$field_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'foreign field "'.$field_name.'" already referenced');
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateSequence()
+
+    /**
+     * Checks whether the definition of a parsed sequence is valid. Modify
+     * sequence definition when necessary.
+     *
+     * @param array  $sequences     multi dimensional array that contains the
+     *                              sequences of current database.
+     * @param array  &$sequence     multi dimensional array that contains the
+     *                              structure of the parsed sequence.
+     * @param string $sequence_name name of the parsed sequence
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateSequence($sequences, &$sequence, $sequence_name)
+    {
+        if (!$sequence_name) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'a sequence has to have a name');
+        }
+
+        if (is_array($sequences) && isset($sequences[$sequence_name])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'sequence "'.$sequence_name.'" already exists');
+        }
+
+        if (is_array($this->fail_on_invalid_names)) {
+            $name = strtoupper($sequence_name);
+            foreach ($this->fail_on_invalid_names as $rdbms) {
+                if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                        'sequence name "'.$sequence_name.'" is a reserved word in: '.$rdbms);
+                }
+            }
+        }
+
+        if (empty($sequence['was'])) {
+            $sequence['was'] = $sequence_name;
+        }
+
+        if (!empty($sequence['on'])
+            && (empty($sequence['on']['table']) || empty($sequence['on']['field']))
+        ) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'sequence "'.$sequence_name.'" on a table was not properly defined');
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateDatabase()
+
+    /**
+     * Checks whether a parsed database is valid. Modify its structure and
+     * data when necessary.
+     *
+     * @param array &$database multi dimensional array that contains the
+     *                         structure and optional data of the database.
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateDatabase(&$database)
+    {
+        /* Have we got a name? */
+        if (!is_array($database) || !isset($database['name']) || !$database['name']) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'a database has to have a name');
+        }
+
+        /* Database name reserved? */
+        if (is_array($this->fail_on_invalid_names)) {
+            $name = strtoupper($database['name']);
+            foreach ($this->fail_on_invalid_names as $rdbms) {
+                if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                        'database name "'.$database['name'].'" is a reserved word in: '.$rdbms);
+                }
+            }
+        }
+
+        /* Create */
+        if (isset($database['create'])
+            && !$this->isBoolean($database['create'])
+        ) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "create" has to be a boolean value');
+        }
+
+        /* Overwrite */
+        if (isset($database['overwrite'])
+            && $database['overwrite'] !== ''
+            && !$this->isBoolean($database['overwrite'])
+        ) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "overwrite" has to be a boolean value');
+        }
+
+        /*
+         * This have to be done here otherwise we can't guarantee that all
+         * tables were already defined in the moment we are parsing constraints
+         */
+        if (isset($database['tables'])) {
+            foreach ($database['tables'] as $table_name => $table) {
+                if (!empty($table['constraints'])) {
+                    foreach ($table['constraints'] as $constraint_name => $constraint) {
+                        $referenced_table_name = $constraint['references']['table'];
+
+                        if (!isset($database['tables'][$referenced_table_name])) {
+                            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                                'referenced table "'.$referenced_table_name.'" of foreign key "'.$constraint_name.'" of table "'.$table_name.'" does not exist');
+                        }
+
+                        if (empty($constraint['references']['fields'])) {
+                            $referenced_table = $database['tables'][$referenced_table_name];
+
+                            $primary = false;
+
+                            if (!empty($referenced_table['indexes'])) {
+                                foreach ($referenced_table['indexes'] as $index_name => $index) {
+                                    if (array_key_exists('primary', $index)
+                                        && $index['primary']
+                                    ) {
+                                        $primary = array();
+                                        foreach ($index['fields'] as $field_name => $field) {
+                                            $primary[$field_name] = '';
+                                        }
+                                        break;
+                                    }
+                                }
+                            }
+
+                            if (!$primary) {
+                                foreach ($referenced_table['fields'] as $field_name => $field) {
+                                    if (array_key_exists('autoincrement', $field)
+                                        && $field['autoincrement']
+                                    ) {
+                                        $primary = array( $field_name => '' );
+                                        break;
+                                    }
+                                }
+                            }
+
+                            if (!$primary) {
+                                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                                    'referenced table "'.$referenced_table_name.'" has no primary key and no referenced field was specified for foreign key "'.$constraint_name.'" of table "'.$table_name.'"');
+                            }
+
+                            $constraint['references']['fields'] = $primary;
+                        }
+
+                        /* the same number of referencing and referenced fields ? */
+                        if (count($constraint['fields']) != count($constraint['references']['fields'])) {
+                            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                                'The number of fields in the referenced key must match those of the foreign key "'.$constraint_name.'"');
+                        }
+
+                        $database['tables'][$table_name]['constraints'][$constraint_name]['references']['fields'] = $constraint['references']['fields'];
+                    }
+                }
+            }
+        }
+
+        /*
+         * This have to be done here otherwise we can't guarantee that all
+         * tables were already defined in the moment we are parsing sequences
+         */
+        if (isset($database['sequences'])) {
+            foreach ($database['sequences'] as $seq_name => $seq) {
+                if (!empty($seq['on'])
+                    && empty($database['tables'][$seq['on']['table']]['fields'][$seq['on']['field']])
+                ) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                        'sequence "'.$seq_name.'" was assigned on unexisting field/table');
+                }
+            }
+        }
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateDataField()
+
+    /* Data Manipulation */
+    /**
+     * Checks whether a parsed DML-field is valid. Modify its structure when
+     * necessary. This is called when validating INSERT and
+     * UPDATE.
+     *
+     * @param array  $table_fields       multi dimensional array that contains the
+     *                                   definition for current table's fields.
+     * @param array  $instruction_fields multi dimensional array that contains the
+     *                                   parsed fields of the current DML instruction.
+     * @param string &$field             array that contains the parsed instruction field
+     *
+     * @return bool|error object
+     *
+     * @access public
+     */
+    function validateDataField($table_fields, $instruction_fields, &$field)
+    {
+        if (!$field['name']) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field-name has to be specified');
+        }
+
+        if (is_array($instruction_fields) && isset($instruction_fields[$field['name']])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'field "'.$field['name'].'" already initialized');
+        }
+
+        if (is_array($table_fields) && !isset($table_fields[$field['name']])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                '"'.$field['name'].'" is not defined');
+        }
+
+        if (!isset($field['group']['type'])) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                '"'.$field['name'].'" has no initial value');
+        }
+
+        if (isset($field['group']['data'])
+            && $field['group']['type'] == 'value'
+            && $field['group']['data'] !== ''
+            && PEAR::isError($result = $this->validateDataFieldValue($table_fields[$field['name']], $field['group']['data'], $field['name']))
+        ) {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                'value of "'.$field['name'].'" is incorrect: '.$result->getUserinfo());
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ validateDataFieldValue()
+
+    /**
+     * Checks whether a given value is compatible with a table field. This is
+     * done when parsing a field for a INSERT or UPDATE instruction.
+     *
+     * @param array  $field_def    multi dimensional array that contains the
+     *                             definition for current table's fields.
+     * @param string &$field_value value to fill the parsed field
+     * @param string $field_name   name of the parsed field
+     *
+     * @return bool|error object
+     *
+     * @access public
+     * @see MDB2_Schema_Validate::validateInsertField()
+     */
+    function validateDataFieldValue($field_def, &$field_value, $field_name)
+    {
+        switch ($field_def['type']) {
+        case 'text':
+        case 'clob':
+            if (!empty($field_def['length']) && strlen($field_value) > $field_def['length']) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is larger than "'.$field_def['length'].'"');
+            }
+            break;
+        case 'blob':
+            $field_value = pack('H*', $field_value);
+            if (!empty($field_def['length']) && strlen($field_value) > $field_def['length']) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is larger than "'.$field_def['type'].'"');
+            }
+            break;
+        case 'integer':
+            if ($field_value != ((int)$field_value)) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
+            }
+            //$field_value = (int)$field_value;
+            if (!empty($field_def['unsigned']) && $field_def['unsigned'] && $field_value < 0) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" signed instead of unsigned');
+            }
+            break;
+        case 'boolean':
+            if (!$this->isBoolean($field_value)) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
+            }
+            break;
+        case 'date':
+            if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/', $field_value)
+                && $field_value !== 'CURRENT_DATE'
+            ) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
+            }
+            break;
+        case 'timestamp':
+            if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $field_value)
+                && strcasecmp($field_value, 'now()') != 0
+                && $field_value !== 'CURRENT_TIMESTAMP'
+            ) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
+            }
+            break;
+        case 'time':
+            if (!preg_match("/([0-9]{2}):([0-9]{2}):([0-9]{2})/", $field_value)
+                && $field_value !== 'CURRENT_TIME'
+            ) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
+            }
+            break;
+        case 'float':
+        case 'double':
+            if ($field_value != (double)$field_value) {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
+                    '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
+            }
+            //$field_value = (double)$field_value;
+            break;
+        }
+        return MDB2_OK;
+    }
+}
+
+?>
diff --git a/3rdparty/MDB2/Schema/Writer.php b/3rdparty/MDB2/Schema/Writer.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ae4918dc1d086af46f6e297a72c7af9d4105b1f
--- /dev/null
+++ b/3rdparty/MDB2/Schema/Writer.php
@@ -0,0 +1,581 @@
+<?php
+/**
+ * PHP versions 4 and 5
+ *
+ * Copyright (c) 1998-2008 Manuel Lemos, Tomas V.V.Cox,
+ * Stig. S. Bakken, Lukas Smith, Igor Feghali
+ * All rights reserved.
+ *
+ * MDB2_Schema enables users to maintain RDBMS independant schema files
+ * in XML that can be used to manipulate both data and database schemas
+ * 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, Igor Feghali 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: Lukas Smith <smith@pooteeweet.org>
+ * Author: Igor Feghali <ifeghali@php.net>
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ * @author   Igor Feghali <ifeghali@php.net>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @version  CVS: $Id: Writer.php,v 1.40 2008/11/30 03:34:00 clockwerx Exp $
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+
+/**
+ * Writes an XML schema file
+ *
+ * @category Database
+ * @package  MDB2_Schema
+ * @author   Lukas Smith <smith@pooteeweet.org>
+ * @license  BSD http://www.opensource.org/licenses/bsd-license.php
+ * @link     http://pear.php.net/packages/MDB2_Schema
+ */
+class MDB2_Schema_Writer
+{
+    // {{{ properties
+
+    var $valid_types = array();
+
+    // }}}
+    // {{{ constructor
+
+    function __construct($valid_types = array())
+    {
+        $this->valid_types = $valid_types;
+    }
+
+    function MDB2_Schema_Writer($valid_types = array())
+    {
+        $this->__construct($valid_types);
+    }
+
+    // }}}
+    // {{{ raiseError()
+
+    /**
+     * This method is used to communicate an error and invoke error
+     * callbacks etc.  Basically a wrapper for PEAR::raiseError
+     * without the message string.
+     *
+     * @param int|PEAR_Error $code    integer error code or and PEAR_Error instance
+     * @param int            $mode    error mode, see PEAR_Error docs
+     *                                error level (E_USER_NOTICE etc).  If error mode is
+     *                                PEAR_ERROR_CALLBACK, this is the callback function,
+     *                                either as a function name, or as an array of an
+     *                                object and method name.  For other error modes this
+     *                                parameter is ignored.
+     * @param string         $options Extra debug information.  Defaults to the last
+     *                                query and native error code.
+     *
+     * @return object  a PEAR error object
+     * @access  public
+     * @see PEAR_Error
+     */
+    function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
+    {
+        $error =& MDB2_Schema::raiseError($code, $mode, $options, $userinfo);
+        return $error;
+    }
+
+    // }}}
+    // {{{ _escapeSpecialChars()
+
+    /**
+     * add escapecharacters to all special characters in a string
+     *
+     * @param string $string string that should be escaped
+     *
+     * @return string escaped string
+     * @access protected
+     */
+    function _escapeSpecialChars($string)
+    {
+        if (!is_string($string)) {
+            $string = strval($string);
+        }
+
+        $escaped = '';
+        for ($char = 0, $count = strlen($string); $char < $count; $char++) {
+            switch ($string[$char]) {
+            case '&':
+                $escaped .= '&amp;';
+                break;
+            case '>':
+                $escaped .= '&gt;';
+                break;
+            case '<':
+                $escaped .= '&lt;';
+                break;
+            case '"':
+                $escaped .= '&quot;';
+                break;
+            case '\'':
+                $escaped .= '&apos;';
+                break;
+            default:
+                $code = ord($string[$char]);
+                if ($code < 32 || $code > 127) {
+                    $escaped .= "&#$code;";
+                } else {
+                    $escaped .= $string[$char];
+                }
+                break;
+            }
+        }
+        return $escaped;
+    }
+
+    // }}}
+    // {{{ _dumpBoolean()
+
+    /**
+     * dump the structure of a sequence
+     *
+     * @param string $boolean boolean value or variable definition
+     *
+     * @return string with xml boolea definition
+     * @access private
+     */
+    function _dumpBoolean($boolean)
+    {
+        if (is_string($boolean)) {
+            if ($boolean !== 'true' || $boolean !== 'false'
+                || preg_match('/<variable>.*</variable>/', $boolean)
+            ) {
+                return $boolean;
+            }
+        }
+        return $boolean ? 'true' : 'false';
+    }
+
+    // }}}
+    // {{{ dumpSequence()
+
+    /**
+     * dump the structure of a sequence
+     *
+     * @param string  $sequence_definition sequence definition
+     * @param string  $sequence_name       sequence name
+     * @param string  $eol                 end of line characters
+     * @param integer $dump                determines what data to dump
+     *                      MDB2_SCHEMA_DUMP_ALL       : the entire db
+     *                      MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db
+     *                      MDB2_SCHEMA_DUMP_CONTENT   : only the content of the db
+     *
+     * @return mixed string xml sequence definition on success, or a error object
+     * @access public
+     */
+    function dumpSequence($sequence_definition, $sequence_name, $eol, $dump = MDB2_SCHEMA_DUMP_ALL)
+    {
+        $buffer = "$eol <sequence>$eol  <name>$sequence_name</name>$eol";
+        if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT) {
+            if (!empty($sequence_definition['start'])) {
+                $start   = $sequence_definition['start'];
+                $buffer .= "  <start>$start</start>$eol";
+            }
+        }
+
+        if (!empty($sequence_definition['on'])) {
+            $buffer .= "  <on>$eol";
+            $buffer .= "   <table>".$sequence_definition['on']['table'];
+            $buffer .= "</table>$eol   <field>".$sequence_definition['on']['field'];
+            $buffer .= "</field>$eol  </on>$eol";
+        }
+        $buffer .= " </sequence>$eol";
+
+        return $buffer;
+    }
+
+    // }}}
+    // {{{ dumpDatabase()
+
+    /**
+     * Dump a previously parsed database structure in the Metabase schema
+     * XML based format suitable for the Metabase parser. This function
+     * may optionally dump the database definition with initialization
+     * commands that specify the data that is currently present in the tables.
+     *
+     * @param array   $database_definition unknown
+     * @param array   $arguments           associative array that takes pairs of tag
+     *              names and values that define dump options.
+     *                 array (
+     *                     'output_mode'    =>    String
+     *                         'file' :   dump into a file
+     *                         default:   dump using a function
+     *                     'output'        =>    String
+     *                         depending on the 'Output_Mode'
+     *                                  name of the file
+     *                                  name of the function
+     *                     'end_of_line'        =>    String
+     *                         end of line delimiter that should be used
+     *                         default: "\n"
+     *                 );
+     * @param integer $dump                determines what data to dump
+     *                      MDB2_SCHEMA_DUMP_ALL       : the entire db
+     *                      MDB2_SCHEMA_DUMP_STRUCTURE : only the structure of the db
+     *                      MDB2_SCHEMA_DUMP_CONTENT   : only the content of the db
+     *
+     * @return mixed MDB2_OK on success, or a error object
+     * @access public
+     */
+    function dumpDatabase($database_definition, $arguments, $dump = MDB2_SCHEMA_DUMP_ALL)
+    {
+        if (!empty($arguments['output'])) {
+            if (!empty($arguments['output_mode']) && $arguments['output_mode'] == 'file') {
+                $fp = fopen($arguments['output'], 'w');
+                if ($fp === false) {
+                    return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null,
+                        'it was not possible to open output file');
+                }
+
+                $output = false;
+            } elseif (is_callable($arguments['output'])) {
+                $output = $arguments['output'];
+            } else {
+                return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null,
+                    'no valid output function specified');
+            }
+        } else {
+            return $this->raiseError(MDB2_SCHEMA_ERROR_WRITER, null, null,
+                'no output method specified');
+        }
+
+        $eol = isset($arguments['end_of_line']) ? $arguments['end_of_line'] : "\n";
+
+        $sequences = array();
+        if (!empty($database_definition['sequences'])
+            && is_array($database_definition['sequences'])
+        ) {
+            foreach ($database_definition['sequences'] as $sequence_name => $sequence) {
+                $table = !empty($sequence['on']) ? $sequence['on']['table'] :'';
+
+                $sequences[$table][] = $sequence_name;
+            }
+        }
+
+        $buffer  = '<?xml version="1.0" encoding="ISO-8859-1" ?>'.$eol;
+        $buffer .= "<database>$eol$eol <name>".$database_definition['name']."</name>";
+        $buffer .= "$eol <create>".$this->_dumpBoolean($database_definition['create'])."</create>";
+        $buffer .= "$eol <overwrite>".$this->_dumpBoolean($database_definition['overwrite'])."</overwrite>$eol";
+        $buffer .= "$eol <charset>".$database_definition['charset']."</charset>$eol";
+
+        if ($output) {
+            call_user_func($output, $buffer);
+        } else {
+            fwrite($fp, $buffer);
+        }
+
+        if (!empty($database_definition['tables']) && is_array($database_definition['tables'])) {
+            foreach ($database_definition['tables'] as $table_name => $table) {
+                $buffer = "$eol <table>$eol$eol  <name>$table_name</name>$eol";
+                if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_STRUCTURE) {
+                    $buffer .= "$eol  <declaration>$eol";
+                    if (!empty($table['fields']) && is_array($table['fields'])) {
+                        foreach ($table['fields'] as $field_name => $field) {
+                            if (empty($field['type'])) {
+                                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null,
+                                    'it was not specified the type of the field "'.
+                                    $field_name.'" of the table "'.$table_name.'"');
+                            }
+                            if (!empty($this->valid_types) && !array_key_exists($field['type'], $this->valid_types)) {
+                                return $this->raiseError(MDB2_SCHEMA_ERROR_UNSUPPORTED, null, null,
+                                    'type "'.$field['type'].'" is not yet supported');
+                            }
+                            $buffer .= "$eol   <field>$eol    <name>$field_name</name>$eol    <type>";
+                            $buffer .= $field['type']."</type>$eol";
+                            if (!empty($field['fixed']) && $field['type'] === 'text') {
+                                $buffer .= "    <fixed>".$this->_dumpBoolean($field['fixed'])."</fixed>$eol";
+                            }
+                            if (array_key_exists('default', $field)
+                                && $field['type'] !== 'clob' && $field['type'] !== 'blob'
+                            ) {
+                                $buffer .= '    <default>'.$this->_escapeSpecialChars($field['default'])."</default>$eol";
+                            }
+                            if (!empty($field['notnull'])) {
+                                $buffer .= "    <notnull>".$this->_dumpBoolean($field['notnull'])."</notnull>$eol";
+                            } else {
+                                $buffer .= "    <notnull>false</notnull>$eol";
+                            }
+                            if (!empty($field['autoincrement'])) {
+                                $buffer .= "    <autoincrement>" . $field['autoincrement'] ."</autoincrement>$eol";
+                            }
+                            if (!empty($field['unsigned'])) {
+                                $buffer .= "    <unsigned>".$this->_dumpBoolean($field['unsigned'])."</unsigned>$eol";
+                            }
+                            if (!empty($field['length'])) {
+                                $buffer .= '    <length>'.$field['length']."</length>$eol";
+                            }
+                            $buffer .= "   </field>$eol";
+                        }
+                    }
+
+                    if (!empty($table['indexes']) && is_array($table['indexes'])) {
+                        foreach ($table['indexes'] as $index_name => $index) {
+                            if (strtolower($index_name) === 'primary') {
+                                $index_name = $table_name . '_pKey';
+                            }
+                            $buffer .= "$eol   <index>$eol    <name>$index_name</name>$eol";
+                            if (!empty($index['unique'])) {
+                                $buffer .= "    <unique>".$this->_dumpBoolean($index['unique'])."</unique>$eol";
+                            }
+
+                            if (!empty($index['primary'])) {
+                                $buffer .= "    <primary>".$this->_dumpBoolean($index['primary'])."</primary>$eol";
+                            }
+
+                            foreach ($index['fields'] as $field_name => $field) {
+                                $buffer .= "    <field>$eol     <name>$field_name</name>$eol";
+                                if (!empty($field) && is_array($field)) {
+                                    $buffer .= '     <sorting>'.$field['sorting']."</sorting>$eol";
+                                }
+                                $buffer .= "    </field>$eol";
+                            }
+                            $buffer .= "   </index>$eol";
+                        }
+                    }
+
+                    if (!empty($table['constraints']) && is_array($table['constraints'])) {
+                        foreach ($table['constraints'] as $constraint_name => $constraint) {
+                            $buffer .= "$eol   <foreign>$eol    <name>$constraint_name</name>$eol";
+                            if (empty($constraint['fields']) || !is_array($constraint['fields'])) {
+                                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null,
+                                    'it was not specified a field for the foreign key "'.
+                                    $constraint_name.'" of the table "'.$table_name.'"');
+                            }
+                            if (!is_array($constraint['references']) || empty($constraint['references']['table'])) {
+                                return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE, null, null,
+                                    'it was not specified the referenced table of the foreign key "'.
+                                    $constraint_name.'" of the table "'.$table_name.'"');
+                            }
+                            if (!empty($constraint['match'])) {
+                                $buffer .= "    <match>".$constraint['match']."</match>$eol";
+                            }
+                            if (!empty($constraint['ondelete'])) {
+                                $buffer .= "    <ondelete>".$constraint['ondelete']."</ondelete>$eol";
+                            }
+                            if (!empty($constraint['onupdate'])) {
+                                $buffer .= "    <onupdate>".$constraint['onupdate']."</onupdate>$eol";
+                            }
+                            if (!empty($constraint['deferrable'])) {
+                                $buffer .= "    <deferrable>".$constraint['deferrable']."</deferrable>$eol";
+                            }
+                            if (!empty($constraint['initiallydeferred'])) {
+                                $buffer .= "    <initiallydeferred>".$constraint['initiallydeferred']."</initiallydeferred>$eol";
+                            }
+                            foreach ($constraint['fields'] as $field_name => $field) {
+                                $buffer .= "    <field>$field_name</field>$eol";
+                            }
+                            $buffer .= "    <references>$eol     <table>".$constraint['references']['table']."</table>$eol";
+                            foreach ($constraint['references']['fields'] as $field_name => $field) {
+                                $buffer .= "     <field>$field_name</field>$eol";
+                            }
+                            $buffer .= "    </references>$eol";
+
+                            $buffer .= "   </foreign>$eol";
+                        }
+                    }
+
+                    $buffer .= "$eol  </declaration>$eol";
+                }
+
+                if ($output) {
+                    call_user_func($output, $buffer);
+                } else {
+                    fwrite($fp, $buffer);
+                }
+
+                $buffer = '';
+                if ($dump == MDB2_SCHEMA_DUMP_ALL || $dump == MDB2_SCHEMA_DUMP_CONTENT) {
+                    if (!empty($table['initialization']) && is_array($table['initialization'])) {
+                        $buffer = "$eol  <initialization>$eol";
+                        foreach ($table['initialization'] as $instruction) {
+                            switch ($instruction['type']) {
+                            case 'insert':
+                                $buffer .= "$eol   <insert>$eol";
+                                foreach ($instruction['data']['field'] as $field) {
+                                    $field_name = $field['name'];
+
+                                    $buffer .= "$eol    <field>$eol     <name>$field_name</name>$eol";
+                                    $buffer .= $this->writeExpression($field['group'], 5, $arguments);
+                                    $buffer .= "    </field>$eol";
+                                }
+                                $buffer .= "$eol   </insert>$eol";
+                                break;
+                            case 'update':
+                                $buffer .= "$eol   <update>$eol";
+                                foreach ($instruction['data']['field'] as $field) {
+                                    $field_name = $field['name'];
+
+                                    $buffer .= "$eol    <field>$eol     <name>$field_name</name>$eol";
+                                    $buffer .= $this->writeExpression($field['group'], 5, $arguments);
+                                    $buffer .= "    </field>$eol";
+                                }
+
+                                if (!empty($instruction['data']['where'])
+                                    && is_array($instruction['data']['where'])
+                                ) {
+                                    $buffer .= "    <where>$eol";
+                                    $buffer .= $this->writeExpression($instruction['data']['where'], 5, $arguments);
+                                    $buffer .= "    </where>$eol";
+                                }
+
+                                $buffer .= "$eol   </update>$eol";
+                                break;
+                            case 'delete':
+                                $buffer .= "$eol   <delete>$eol$eol";
+                                if (!empty($instruction['data']['where'])
+                                    && is_array($instruction['data']['where'])
+                                ) {
+                                    $buffer .= "    <where>$eol";
+                                    $buffer .= $this->writeExpression($instruction['data']['where'], 5, $arguments);
+                                    $buffer .= "    </where>$eol";
+                                }
+                                $buffer .= "$eol   </delete>$eol";
+                                break;
+                            }
+                        }
+                        $buffer .= "$eol  </initialization>$eol";
+                    }
+                }
+                $buffer .= "$eol </table>$eol";
+                if ($output) {
+                    call_user_func($output, $buffer);
+                } else {
+                    fwrite($fp, $buffer);
+                }
+
+                if (isset($sequences[$table_name])) {
+                    foreach ($sequences[$table_name] as $sequence) {
+                        $result = $this->dumpSequence($database_definition['sequences'][$sequence],
+                                                      $sequence, $eol, $dump);
+                        if (PEAR::isError($result)) {
+                            return $result;
+                        }
+
+                        if ($output) {
+                            call_user_func($output, $result);
+                        } else {
+                            fwrite($fp, $result);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (isset($sequences[''])) {
+            foreach ($sequences[''] as $sequence) {
+                $result = $this->dumpSequence($database_definition['sequences'][$sequence],
+                                              $sequence, $eol, $dump);
+                if (PEAR::isError($result)) {
+                    return $result;
+                }
+
+                if ($output) {
+                    call_user_func($output, $result);
+                } else {
+                    fwrite($fp, $result);
+                }
+            }
+        }
+
+        $buffer = "$eol</database>$eol";
+        if ($output) {
+            call_user_func($output, $buffer);
+        } else {
+            fwrite($fp, $buffer);
+            fclose($fp);
+        }
+
+        return MDB2_OK;
+    }
+
+    // }}}
+    // {{{ writeExpression()
+
+    /**
+     * Dumps the structure of an element. Elements can be value, column,
+     * function or expression.
+     *
+     * @param array   $element   multi dimensional array that represents the parsed element
+     *                           of a DML instruction.
+     * @param integer $offset    base indentation width
+     * @param array   $arguments associative array that takes pairs of tag
+     *                           names and values that define dump options.
+     *
+     * @return string
+     *
+     * @access public
+     * @see MDB2_Schema_Writer::dumpDatabase()
+     */
+    function writeExpression($element, $offset = 0, $arguments = null)
+    {
+        $eol = isset($arguments['end_of_line']) ? $arguments['end_of_line'] : "\n";
+        $str = '';
+
+        $indent  = str_repeat(' ', $offset);
+        $noffset = $offset + 1;
+
+        switch ($element['type']) {
+        case 'value':
+            $str .= "$indent<value>".$this->_escapeSpecialChars($element['data'])."</value>$eol";
+            break;
+        case 'column':
+            $str .= "$indent<column>".$this->_escapeSpecialChars($element['data'])."</column>$eol";
+            break;
+        case 'function':
+            $str .= "$indent<function>$eol$indent <name>".$this->_escapeSpecialChars($element['data']['name'])."</name>$eol";
+
+            if (!empty($element['data']['arguments'])
+                && is_array($element['data']['arguments'])
+            ) {
+                foreach ($element['data']['arguments'] as $v) {
+                    $str .= $this->writeExpression($v, $noffset, $arguments);
+                }
+            }
+
+            $str .= "$indent</function>$eol";
+            break;
+        case 'expression':
+            $str .= "$indent<expression>$eol";
+            $str .= $this->writeExpression($element['data']['operants'][0], $noffset, $arguments);
+            $str .= "$indent <operator>".$element['data']['operator']."</operator>$eol";
+            $str .= $this->writeExpression($element['data']['operants'][1], $noffset, $arguments);
+            $str .= "$indent</expression>$eol";
+            break;
+        }
+        return $str;
+    }
+
+    // }}}
+}
+?>
diff --git a/3rdparty/PEAR.php b/3rdparty/PEAR.php
new file mode 100644
index 0000000000000000000000000000000000000000..f832c0d49162c40c64ecc9b43762a58561f2c15b
--- /dev/null
+++ b/3rdparty/PEAR.php
@@ -0,0 +1,1055 @@
+<?php
+//
+// +--------------------------------------------------------------------+
+// | PEAR, the PHP Extension and Application Repository                 |
+// +--------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                              |
+// +--------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,     |
+// | that is bundled with this package in the file LICENSE, and is      |
+// | available through the world-wide-web at the following url:         |
+// | http://www.php.net/license/3_0.txt.                                |
+// | If you did not receive a copy of the PHP license and are unable to |
+// | obtain it through the world-wide-web, please send a note to        |
+// | license@php.net so we can mail you a copy immediately.             |
+// +--------------------------------------------------------------------+
+// | Authors: Sterling Hughes <sterling@php.net>                        |
+// |          Stig Bakken <ssb@php.net>                                 |
+// |          Tomas V.V.Cox <cox@idecnet.com>                           |
+// +--------------------------------------------------------------------+
+//
+// $Id: PEAR.php,v 1.82.2.6 2005/01/01 05:24:51 cellog Exp $
+//
+
+define('PEAR_ERROR_RETURN',     1);
+define('PEAR_ERROR_PRINT',      2);
+define('PEAR_ERROR_TRIGGER',    4);
+define('PEAR_ERROR_DIE',        8);
+define('PEAR_ERROR_CALLBACK',  16);
+/**
+ * WARNING: obsolete
+ * @deprecated
+ */
+define('PEAR_ERROR_EXCEPTION', 32);
+define('PEAR_ZE2', (function_exists('version_compare') &&
+                    version_compare(zend_version(), "2-dev", "ge")));
+
+if (substr(PHP_OS, 0, 3) == 'WIN') {
+    define('OS_WINDOWS', true);
+    define('OS_UNIX',    false);
+    define('PEAR_OS',    'Windows');
+} else {
+    define('OS_WINDOWS', false);
+    define('OS_UNIX',    true);
+    define('PEAR_OS',    'Unix'); // blatant assumption
+}
+
+// instant backwards compatibility
+if (!defined('PATH_SEPARATOR')) {
+    if (OS_WINDOWS) {
+        define('PATH_SEPARATOR', ';');
+    } else {
+        define('PATH_SEPARATOR', ':');
+    }
+}
+
+$GLOBALS['_PEAR_default_error_mode']     = PEAR_ERROR_RETURN;
+$GLOBALS['_PEAR_default_error_options']  = E_USER_NOTICE;
+$GLOBALS['_PEAR_destructor_object_list'] = array();
+$GLOBALS['_PEAR_shutdown_funcs']         = array();
+$GLOBALS['_PEAR_error_handler_stack']    = array();
+
+@ini_set('track_errors', true);
+
+/**
+ * Base class for other PEAR classes.  Provides rudimentary
+ * emulation of destructors.
+ *
+ * If you want a destructor in your class, inherit PEAR and make a
+ * destructor method called _yourclassname (same name as the
+ * constructor, but with a "_" prefix).  Also, in your constructor you
+ * have to call the PEAR constructor: $this->PEAR();.
+ * The destructor method will be called without parameters.  Note that
+ * at in some SAPI implementations (such as Apache), any output during
+ * the request shutdown (in which destructors are called) seems to be
+ * discarded.  If you need to get any debug information from your
+ * destructor, use error_log(), syslog() or something similar.
+ *
+ * IMPORTANT! To use the emulated destructors you need to create the
+ * objects by reference: $obj =& new PEAR_child;
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <ssb@php.net>
+ * @see http://pear.php.net/manual/
+ */
+class PEAR
+{
+    // {{{ properties
+
+    /**
+     * Whether to enable internal debug messages.
+     *
+     * @var     bool
+     * @access  private
+     */
+    var $_debug = false;
+
+    /**
+     * Default error mode for this object.
+     *
+     * @var     int
+     * @access  private
+     */
+    var $_default_error_mode = null;
+
+    /**
+     * Default error options used for this object when error mode
+     * is PEAR_ERROR_TRIGGER.
+     *
+     * @var     int
+     * @access  private
+     */
+    var $_default_error_options = null;
+
+    /**
+     * Default error handler (callback) for this object, if error mode is
+     * PEAR_ERROR_CALLBACK.
+     *
+     * @var     string
+     * @access  private
+     */
+    var $_default_error_handler = '';
+
+    /**
+     * Which class to use for error objects.
+     *
+     * @var     string
+     * @access  private
+     */
+    var $_error_class = 'PEAR_Error';
+
+    /**
+     * An array of expected errors.
+     *
+     * @var     array
+     * @access  private
+     */
+    var $_expected_errors = array();
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * Constructor.  Registers this object in
+     * $_PEAR_destructor_object_list for destructor emulation if a
+     * destructor object exists.
+     *
+     * @param string $error_class  (optional) which class to use for
+     *        error objects, defaults to PEAR_Error.
+     * @access public
+     * @return void
+     */
+    function PEAR($error_class = null)
+    {
+        $classname = strtolower(get_class($this));
+        if ($this->_debug) {
+            print "PEAR constructor called, class=$classname\n";
+        }
+        if ($error_class !== null) {
+            $this->_error_class = $error_class;
+        }
+        while ($classname && strcasecmp($classname, "pear")) {
+            $destructor = "_$classname";
+            if (method_exists($this, $destructor)) {
+                global $_PEAR_destructor_object_list;
+                $_PEAR_destructor_object_list[] = &$this;
+                if (!isset($GLOBALS['_PEAR_SHUTDOWN_REGISTERED'])) {
+                    register_shutdown_function("_PEAR_call_destructors");
+                    $GLOBALS['_PEAR_SHUTDOWN_REGISTERED'] = true;
+                }
+                break;
+            } else {
+                $classname = get_parent_class($classname);
+            }
+        }
+    }
+
+    // }}}
+    // {{{ destructor
+
+    /**
+     * Destructor (the emulated type of...).  Does nothing right now,
+     * but is included for forward compatibility, so subclass
+     * destructors should always call it.
+     *
+     * See the note in the class desciption about output from
+     * destructors.
+     *
+     * @access public
+     * @return void
+     */
+    function _PEAR() {
+        if ($this->_debug) {
+            printf("PEAR destructor called, class=%s\n", strtolower(get_class($this)));
+        }
+    }
+
+    // }}}
+    // {{{ getStaticProperty()
+
+    /**
+    * If you have a class that's mostly/entirely static, and you need static
+    * properties, you can use this method to simulate them. Eg. in your method(s)
+    * do this: $myVar = &PEAR::getStaticProperty('myclass', 'myVar');
+    * You MUST use a reference, or they will not persist!
+    *
+    * @access public
+    * @param  string $class  The calling classname, to prevent clashes
+    * @param  string $var    The variable to retrieve.
+    * @return mixed   A reference to the variable. If not set it will be
+    *                 auto initialised to NULL.
+    */
+    function &getStaticProperty($class, $var)
+    {
+        static $properties;
+        return $properties[$class][$var];
+    }
+
+    // }}}
+    // {{{ registerShutdownFunc()
+
+    /**
+    * Use this function to register a shutdown method for static
+    * classes.
+    *
+    * @access public
+    * @param  mixed $func  The function name (or array of class/method) to call
+    * @param  mixed $args  The arguments to pass to the function
+    * @return void
+    */
+    function registerShutdownFunc($func, $args = array())
+    {
+        $GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
+    }
+
+    // }}}
+    // {{{ isError()
+
+    /**
+     * Tell whether a value is a PEAR error.
+     *
+     * @param   mixed $data   the value to test
+     * @param   int   $code   if $data is an error object, return true
+     *                        only if $code is a string and
+     *                        $obj->getMessage() == $code or
+     *                        $code is an integer and $obj->getCode() == $code
+     * @access  public
+     * @return  bool    true if parameter is an error
+     */
+    static function isError($data, $code = null)
+    {
+        if ($data instanceof PEAR_Error) {
+            if (is_null($code)) {
+                return true;
+            } elseif (is_string($code)) {
+                return $data->getMessage() == $code;
+            } else {
+                return $data->getCode() == $code;
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ setErrorHandling()
+
+    /**
+     * Sets how errors generated by this object should be handled.
+     * Can be invoked both in objects and statically.  If called
+     * statically, setErrorHandling sets the default behaviour for all
+     * PEAR objects.  If called in an object, setErrorHandling sets
+     * the default behaviour for that object.
+     *
+     * @param int $mode
+     *        One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+     *        PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+     *        PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
+     *
+     * @param mixed $options
+     *        When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
+     *        of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+     *
+     *        When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
+     *        to be the callback function or method.  A callback
+     *        function is a string with the name of the function, a
+     *        callback method is an array of two elements: the element
+     *        at index 0 is the object, and the element at index 1 is
+     *        the name of the method to call in the object.
+     *
+     *        When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
+     *        a printf format string used when printing the error
+     *        message.
+     *
+     * @access public
+     * @return void
+     * @see PEAR_ERROR_RETURN
+     * @see PEAR_ERROR_PRINT
+     * @see PEAR_ERROR_TRIGGER
+     * @see PEAR_ERROR_DIE
+     * @see PEAR_ERROR_CALLBACK
+     * @see PEAR_ERROR_EXCEPTION
+     *
+     * @since PHP 4.0.5
+     */
+
+    function setErrorHandling($mode = null, $options = null)
+    {
+        if (isset($this) && $this instanceof PEAR) {
+            $setmode     = &$this->_default_error_mode;
+            $setoptions  = &$this->_default_error_options;
+        } else {
+            $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
+            $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
+        }
+
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $setmode = $mode;
+                $setoptions = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $setmode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $setoptions = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+    }
+
+    // }}}
+    // {{{ expectError()
+
+    /**
+     * This method is used to tell which errors you expect to get.
+     * Expected errors are always returned with error mode
+     * PEAR_ERROR_RETURN.  Expected error codes are stored in a stack,
+     * and this method pushes a new element onto it.  The list of
+     * expected errors are in effect until they are popped off the
+     * stack with the popExpect() method.
+     *
+     * Note that this method can not be called statically
+     *
+     * @param mixed $code a single error code or an array of error codes to expect
+     *
+     * @return int     the new depth of the "expected errors" stack
+     * @access public
+     */
+    function expectError($code = '*')
+    {
+        if (is_array($code)) {
+            array_push($this->_expected_errors, $code);
+        } else {
+            array_push($this->_expected_errors, array($code));
+        }
+        return sizeof($this->_expected_errors);
+    }
+
+    // }}}
+    // {{{ popExpect()
+
+    /**
+     * This method pops one element off the expected error codes
+     * stack.
+     *
+     * @return array   the list of error codes that were popped
+     */
+    function popExpect()
+    {
+        return array_pop($this->_expected_errors);
+    }
+
+    // }}}
+    // {{{ _checkDelExpect()
+
+    /**
+     * This method checks unsets an error code if available
+     *
+     * @param mixed error code
+     * @return bool true if the error code was unset, false otherwise
+     * @access private
+     * @since PHP 4.3.0
+     */
+    function _checkDelExpect($error_code)
+    {
+        $deleted = false;
+
+        foreach ($this->_expected_errors AS $key => $error_array) {
+            if (in_array($error_code, $error_array)) {
+                unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
+                $deleted = true;
+            }
+
+            // clean up empty arrays
+            if (0 == count($this->_expected_errors[$key])) {
+                unset($this->_expected_errors[$key]);
+            }
+        }
+        return $deleted;
+    }
+
+    // }}}
+    // {{{ delExpect()
+
+    /**
+     * This method deletes all occurences of the specified element from
+     * the expected error codes stack.
+     *
+     * @param  mixed $error_code error code that should be deleted
+     * @return mixed list of error codes that were deleted or error
+     * @access public
+     * @since PHP 4.3.0
+     */
+    function delExpect($error_code)
+    {
+        $deleted = false;
+
+        if ((is_array($error_code) && (0 != count($error_code)))) {
+            // $error_code is a non-empty array here;
+            // we walk through it trying to unset all
+            // values
+            foreach($error_code as $key => $error) {
+                if ($this->_checkDelExpect($error)) {
+                    $deleted =  true;
+                } else {
+                    $deleted = false;
+                }
+            }
+            return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+        } elseif (!empty($error_code)) {
+            // $error_code comes alone, trying to unset it
+            if ($this->_checkDelExpect($error_code)) {
+                return true;
+            } else {
+                return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
+            }
+        } else {
+            // $error_code is empty
+            return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
+        }
+    }
+
+    // }}}
+    // {{{ raiseError()
+
+    /**
+     * This method is a wrapper that returns an instance of the
+     * configured error class with this object's default error
+     * handling applied.  If the $mode and $options parameters are not
+     * specified, the object's defaults are used.
+     *
+     * @param mixed $message a text error message or a PEAR error object
+     *
+     * @param int $code      a numeric error code (it is up to your class
+     *                  to define these if you want to use codes)
+     *
+     * @param int $mode      One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
+     *                  PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
+     *                  PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
+     *
+     * @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
+     *                  specifies the PHP-internal error level (one of
+     *                  E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
+     *                  If $mode is PEAR_ERROR_CALLBACK, this
+     *                  parameter specifies the callback function or
+     *                  method.  In other error modes this parameter
+     *                  is ignored.
+     *
+     * @param string $userinfo If you need to pass along for example debug
+     *                  information, this parameter is meant for that.
+     *
+     * @param string $error_class The returned error object will be
+     *                  instantiated from this class, if specified.
+     *
+     * @param bool $skipmsg If true, raiseError will only pass error codes,
+     *                  the error message parameter will be dropped.
+     *
+     * @access public
+     * @return object   a PEAR error object
+     * @see PEAR::setErrorHandling
+     * @since PHP 4.0.5
+     */
+    function raiseError($message = null,
+                         $code = null,
+                         $mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $error_class = null,
+                         $skipmsg = false)
+    {
+        // The error is yet a PEAR error object
+        if (is_object($message)) {
+            $code        = $message->getCode();
+            $userinfo    = $message->getUserInfo();
+            $error_class = $message->getType();
+            $message->error_message_prefix = '';
+            $message     = $message->getMessage();
+        }
+
+        if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
+            if ($exp[0] == "*" ||
+                (is_int(reset($exp)) && in_array($code, $exp)) ||
+                (is_string(reset($exp)) && in_array($message, $exp))) {
+                $mode = PEAR_ERROR_RETURN;
+            }
+        }
+        // No mode given, try global ones
+        if ($mode === null) {
+            // Class error handler
+            if (isset($this) && isset($this->_default_error_mode)) {
+                $mode    = $this->_default_error_mode;
+                $options = $this->_default_error_options;
+            // Global error handler
+            } elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
+                $mode    = $GLOBALS['_PEAR_default_error_mode'];
+                $options = $GLOBALS['_PEAR_default_error_options'];
+            }
+        }
+
+        if ($error_class !== null) {
+            $ec = $error_class;
+        } elseif (isset($this) && isset($this->_error_class)) {
+            $ec = $this->_error_class;
+        } else {
+            $ec = 'PEAR_Error';
+        }
+        if ($skipmsg) {
+            return new $ec($code, $mode, $options, $userinfo);
+        } else {
+            return new $ec($message, $code, $mode, $options, $userinfo);
+        }
+    }
+
+    // }}}
+    // {{{ throwError()
+
+    /**
+     * Simpler form of raiseError with fewer options.  In most cases
+     * message, code and userinfo are enough.
+     *
+     * @param string $message
+     *
+     */
+    function throwError($message = null,
+                         $code = null,
+                         $userinfo = null)
+    {
+        if (isset($this) && $this instanceof PEAR) {
+            return $this->raiseError($message, $code, null, null, $userinfo);
+        } else {
+            return PEAR::raiseError($message, $code, null, null, $userinfo);
+        }
+    }
+
+    // }}}
+    function staticPushErrorHandling($mode, $options = null)
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
+        $def_options = &$GLOBALS['_PEAR_default_error_options'];
+        $stack[] = array($def_mode, $def_options);
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $def_mode = $mode;
+                $def_options = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $def_mode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $def_options = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+        $stack[] = array($mode, $options);
+        return true;
+    }
+
+    function staticPopErrorHandling()
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        $setmode     = &$GLOBALS['_PEAR_default_error_mode'];
+        $setoptions  = &$GLOBALS['_PEAR_default_error_options'];
+        array_pop($stack);
+        list($mode, $options) = $stack[sizeof($stack) - 1];
+        array_pop($stack);
+        switch ($mode) {
+            case PEAR_ERROR_EXCEPTION:
+            case PEAR_ERROR_RETURN:
+            case PEAR_ERROR_PRINT:
+            case PEAR_ERROR_TRIGGER:
+            case PEAR_ERROR_DIE:
+            case null:
+                $setmode = $mode;
+                $setoptions = $options;
+                break;
+
+            case PEAR_ERROR_CALLBACK:
+                $setmode = $mode;
+                // class/object method callback
+                if (is_callable($options)) {
+                    $setoptions = $options;
+                } else {
+                    trigger_error("invalid error callback", E_USER_WARNING);
+                }
+                break;
+
+            default:
+                trigger_error("invalid error mode", E_USER_WARNING);
+                break;
+        }
+        return true;
+    }
+
+    // {{{ pushErrorHandling()
+
+    /**
+     * Push a new error handler on top of the error handler options stack. With this
+     * you can easily override the actual error handler for some code and restore
+     * it later with popErrorHandling.
+     *
+     * @param mixed $mode (same as setErrorHandling)
+     * @param mixed $options (same as setErrorHandling)
+     *
+     * @return bool Always true
+     *
+     * @see PEAR::setErrorHandling
+     */
+    function pushErrorHandling($mode, $options = null)
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        if (isset($this) && $this instanceof PEAR) {
+            $def_mode    = &$this->_default_error_mode;
+            $def_options = &$this->_default_error_options;
+        } else {
+            $def_mode    = &$GLOBALS['_PEAR_default_error_mode'];
+            $def_options = &$GLOBALS['_PEAR_default_error_options'];
+        }
+        $stack[] = array($def_mode, $def_options);
+
+        if (isset($this) && $this instanceof PEAR) {
+            $this->setErrorHandling($mode, $options);
+        } else {
+            PEAR::setErrorHandling($mode, $options);
+        }
+        $stack[] = array($mode, $options);
+        return true;
+    }
+
+    // }}}
+    // {{{ popErrorHandling()
+
+    /**
+    * Pop the last error handler used
+    *
+    * @return bool Always true
+    *
+    * @see PEAR::pushErrorHandling
+    */
+    function popErrorHandling()
+    {
+        $stack = &$GLOBALS['_PEAR_error_handler_stack'];
+        array_pop($stack);
+        list($mode, $options) = $stack[sizeof($stack) - 1];
+        array_pop($stack);
+        if (isset($this) && $this instanceof PEAR) {
+            $this->setErrorHandling($mode, $options);
+        } else {
+            PEAR::setErrorHandling($mode, $options);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ loadExtension()
+
+    /**
+    * OS independant PHP extension load. Remember to take care
+    * on the correct extension name for case sensitive OSes.
+    *
+    * @param string $ext The extension name
+    * @return bool Success or not on the dl() call
+    */
+    function loadExtension($ext)
+    {
+        if (!extension_loaded($ext)) {
+            // if either returns true dl() will produce a FATAL error, stop that
+            if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
+                return false;
+            }
+            if (OS_WINDOWS) {
+                $suffix = '.dll';
+            } elseif (PHP_OS == 'HP-UX') {
+                $suffix = '.sl';
+            } elseif (PHP_OS == 'AIX') {
+                $suffix = '.a';
+            } elseif (PHP_OS == 'OSX') {
+                $suffix = '.bundle';
+            } else {
+                $suffix = '.so';
+            }
+            return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
+        }
+        return true;
+    }
+
+    // }}}
+}
+
+// {{{ _PEAR_call_destructors()
+
+function _PEAR_call_destructors()
+{
+    global $_PEAR_destructor_object_list;
+    if (is_array($_PEAR_destructor_object_list) &&
+        sizeof($_PEAR_destructor_object_list))
+    {
+        reset($_PEAR_destructor_object_list);
+        if (@PEAR::getStaticProperty('PEAR', 'destructlifo')) {
+            $_PEAR_destructor_object_list = array_reverse($_PEAR_destructor_object_list);
+        }
+        while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
+            $classname = get_class($objref);
+            while ($classname) {
+                $destructor = "_$classname";
+                if (method_exists($objref, $destructor)) {
+                    $objref->$destructor();
+                    break;
+                } else {
+                    $classname = get_parent_class($classname);
+                }
+            }
+        }
+        // Empty the object list to ensure that destructors are
+        // not called more than once.
+        $_PEAR_destructor_object_list = array();
+    }
+
+    // Now call the shutdown functions
+    if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
+        foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
+            call_user_func_array($value[0], $value[1]);
+        }
+    }
+}
+
+// }}}
+
+class PEAR_Error
+{
+    // {{{ properties
+
+    var $error_message_prefix = '';
+    var $mode                 = PEAR_ERROR_RETURN;
+    var $level                = E_USER_NOTICE;
+    var $code                 = -1;
+    var $message              = '';
+    var $userinfo             = '';
+    var $backtrace            = null;
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Error constructor
+     *
+     * @param string $message  message
+     *
+     * @param int $code     (optional) error code
+     *
+     * @param int $mode     (optional) error mode, one of: PEAR_ERROR_RETURN,
+     * PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
+     * PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
+     *
+     * @param mixed $options   (optional) error level, _OR_ in the case of
+     * PEAR_ERROR_CALLBACK, the callback function or object/method
+     * tuple.
+     *
+     * @param string $userinfo (optional) additional user/debug info
+     *
+     * @access public
+     *
+     */
+    function PEAR_Error($message = 'unknown error', $code = null,
+                        $mode = null, $options = null, $userinfo = null)
+    {
+        if ($mode === null) {
+            $mode = PEAR_ERROR_RETURN;
+        }
+        $this->message   = $message;
+        $this->code      = $code;
+        $this->mode      = $mode;
+        $this->userinfo  = $userinfo;
+        if (function_exists("debug_backtrace")) {
+            if (@!PEAR::getStaticProperty('PEAR_Error', 'skiptrace')) {
+                $this->backtrace = debug_backtrace();
+            }
+        }
+        if ($mode & PEAR_ERROR_CALLBACK) {
+            $this->level = E_USER_NOTICE;
+            $this->callback = $options;
+        } else {
+            if ($options === null) {
+                $options = E_USER_NOTICE;
+            }
+            $this->level = $options;
+            $this->callback = null;
+        }
+        if ($this->mode & PEAR_ERROR_PRINT) {
+            if (is_null($options) || is_int($options)) {
+                $format = "%s";
+            } else {
+                $format = $options;
+            }
+            printf($format, $this->getMessage());
+        }
+        if ($this->mode & PEAR_ERROR_TRIGGER) {
+            trigger_error($this->getMessage(), $this->level);
+        }
+        if ($this->mode & PEAR_ERROR_DIE) {
+            $msg = $this->getMessage();
+            if (is_null($options) || is_int($options)) {
+                $format = "%s";
+                if (substr($msg, -1) != "\n") {
+                    $msg .= "\n";
+                }
+            } else {
+                $format = $options;
+            }
+            die(sprintf($format, $msg));
+        }
+        if ($this->mode & PEAR_ERROR_CALLBACK) {
+            if (is_callable($this->callback)) {
+                call_user_func($this->callback, $this);
+            }
+        }
+        if ($this->mode & PEAR_ERROR_EXCEPTION) {
+            trigger_error("PEAR_ERROR_EXCEPTION is obsolete, use class PEAR_ErrorStack for exceptions", E_USER_WARNING);
+            eval('$e = new Exception($this->message, $this->code);$e->PEAR_Error = $this;throw($e);');
+        }
+    }
+
+    // }}}
+    // {{{ getMode()
+
+    /**
+     * Get the error mode from an error object.
+     *
+     * @return int error mode
+     * @access public
+     */
+    function getMode() {
+        return $this->mode;
+    }
+
+    // }}}
+    // {{{ getCallback()
+
+    /**
+     * Get the callback function/method from an error object.
+     *
+     * @return mixed callback function or object/method array
+     * @access public
+     */
+    function getCallback() {
+        return $this->callback;
+    }
+
+    // }}}
+    // {{{ getMessage()
+
+
+    /**
+     * Get the error message from an error object.
+     *
+     * @return  string  full error message
+     * @access public
+     */
+    function getMessage()
+    {
+        return ($this->error_message_prefix . $this->message);
+    }
+
+
+    // }}}
+    // {{{ getCode()
+
+    /**
+     * Get error code from an error object
+     *
+     * @return int error code
+     * @access public
+     */
+     function getCode()
+     {
+        return $this->code;
+     }
+
+    // }}}
+    // {{{ getType()
+
+    /**
+     * Get the name of this error/exception.
+     *
+     * @return string error/exception name (type)
+     * @access public
+     */
+    function getType()
+    {
+        return get_class($this);
+    }
+
+    // }}}
+    // {{{ getUserInfo()
+
+    /**
+     * Get additional user-supplied information.
+     *
+     * @return string user-supplied information
+     * @access public
+     */
+    function getUserInfo()
+    {
+        return $this->userinfo;
+    }
+
+    // }}}
+    // {{{ getDebugInfo()
+
+    /**
+     * Get additional debug information supplied by the application.
+     *
+     * @return string debug information
+     * @access public
+     */
+    function getDebugInfo()
+    {
+        return $this->getUserInfo();
+    }
+
+    // }}}
+    // {{{ getBacktrace()
+
+    /**
+     * Get the call backtrace from where the error was generated.
+     * Supported with PHP 4.3.0 or newer.
+     *
+     * @param int $frame (optional) what frame to fetch
+     * @return array Backtrace, or NULL if not available.
+     * @access public
+     */
+    function getBacktrace($frame = null)
+    {
+        if ($frame === null) {
+            return $this->backtrace;
+        }
+        return $this->backtrace[$frame];
+    }
+
+    // }}}
+    // {{{ addUserInfo()
+
+    function addUserInfo($info)
+    {
+        if (empty($this->userinfo)) {
+            $this->userinfo = $info;
+        } else {
+            $this->userinfo .= " ** $info";
+        }
+    }
+
+    // }}}
+    // {{{ toString()
+
+    /**
+     * Make a string representation of this object.
+     *
+     * @return string a string with an object summary
+     * @access public
+     */
+    function toString() {
+        $modes = array();
+        $levels = array(E_USER_NOTICE  => 'notice',
+                        E_USER_WARNING => 'warning',
+                        E_USER_ERROR   => 'error');
+        if ($this->mode & PEAR_ERROR_CALLBACK) {
+            if (is_array($this->callback)) {
+                $callback = (is_object($this->callback[0]) ?
+                    strtolower(get_class($this->callback[0])) :
+                    $this->callback[0]) . '::' .
+                    $this->callback[1];
+            } else {
+                $callback = $this->callback;
+            }
+            return sprintf('[%s: message="%s" code=%d mode=callback '.
+                           'callback=%s prefix="%s" info="%s"]',
+                           strtolower(get_class($this)), $this->message, $this->code,
+                           $callback, $this->error_message_prefix,
+                           $this->userinfo);
+        }
+        if ($this->mode & PEAR_ERROR_PRINT) {
+            $modes[] = 'print';
+        }
+        if ($this->mode & PEAR_ERROR_TRIGGER) {
+            $modes[] = 'trigger';
+        }
+        if ($this->mode & PEAR_ERROR_DIE) {
+            $modes[] = 'die';
+        }
+        if ($this->mode & PEAR_ERROR_RETURN) {
+            $modes[] = 'return';
+        }
+        return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
+                       'prefix="%s" info="%s"]',
+                       strtolower(get_class($this)), $this->message, $this->code,
+                       implode("|", $modes), $levels[$this->level],
+                       $this->error_message_prefix,
+                       $this->userinfo);
+    }
+
+    // }}}
+}
+
+/*
+ * Local Variables:
+ * mode: php
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+?>
diff --git a/3rdparty/PEAR/Autoloader.php b/3rdparty/PEAR/Autoloader.php
new file mode 100644
index 0000000000000000000000000000000000000000..de0278d61913c6e62fe8982f3efefc04591de6ed
--- /dev/null
+++ b/3rdparty/PEAR/Autoloader.php
@@ -0,0 +1,208 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Autoloader.php,v 1.11 2004/02/27 02:21:29 cellog Exp $
+
+if (!extension_loaded("overload")) {
+    // die hard without ext/overload
+    die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader");
+}
+
+require_once "PEAR.php";
+
+/**
+ * This class is for objects where you want to separate the code for
+ * some methods into separate classes.  This is useful if you have a
+ * class with not-frequently-used methods that contain lots of code
+ * that you would like to avoid always parsing.
+ *
+ * The PEAR_Autoloader class provides autoloading and aggregation.
+ * The autoloading lets you set up in which classes the separated
+ * methods are found.  Aggregation is the technique used to import new
+ * methods, an instance of each class providing separated methods is
+ * stored and called every time the aggregated method is called.
+ *
+ * @author Stig Sæther Bakken <ssb@php.net>
+ */
+class PEAR_Autoloader extends PEAR
+{
+    // {{{ properties
+
+    /**
+     * Map of methods and classes where they are defined
+     *
+     * @var array
+     *
+     * @access private
+     */
+    var $_autoload_map = array();
+
+    /**
+     * Map of methods and aggregate objects
+     *
+     * @var array
+     *
+     * @access private
+     */
+    var $_method_map = array();
+
+    // }}}
+    // {{{ addAutoload()
+
+    /**
+     * Add one or more autoload entries.
+     *
+     * @param string $method     which method to autoload
+     *
+     * @param string $classname  (optional) which class to find the method in.
+     *                           If the $method parameter is an array, this
+     *                           parameter may be omitted (and will be ignored
+     *                           if not), and the $method parameter will be
+     *                           treated as an associative array with method
+     *                           names as keys and class names as values.
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addAutoload($method, $classname = null)
+    {
+        if (is_array($method)) {
+            array_walk($method, create_function('$a,&$b', '$b = strtolower($b);'));
+            $this->_autoload_map = array_merge($this->_autoload_map, $method);
+        } else {
+            $this->_autoload_map[strtolower($method)] = $classname;
+        }
+    }
+
+    // }}}
+    // {{{ removeAutoload()
+
+    /**
+     * Remove an autoload entry.
+     *
+     * @param string $method  which method to remove the autoload entry for
+     *
+     * @return bool TRUE if an entry was removed, FALSE if not
+     *
+     * @access public
+     */
+    function removeAutoload($method)
+    {
+        $method = strtolower($method);
+        $ok = isset($this->_autoload_map[$method]);
+        unset($this->_autoload_map[$method]);
+        return $ok;
+    }
+
+    // }}}
+    // {{{ addAggregateObject()
+
+    /**
+     * Add an aggregate object to this object.  If the specified class
+     * is not defined, loading it will be attempted following PEAR's
+     * file naming scheme.  All the methods in the class will be
+     * aggregated, except private ones (name starting with an
+     * underscore) and constructors.
+     *
+     * @param string $classname  what class to instantiate for the object.
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addAggregateObject($classname)
+    {
+        $classname = strtolower($classname);
+        if (!class_exists($classname)) {
+            $include_file = preg_replace('/[^a-z0-9]/i', '_', $classname);
+            include_once $include_file;
+        }
+        $obj =& new $classname;
+        $methods = get_class_methods($classname);
+        foreach ($methods as $method) {
+            // don't import priviate methods and constructors
+            if ($method{0} != '_' && $method != $classname) {
+                $this->_method_map[$method] = $obj;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ removeAggregateObject()
+
+    /**
+     * Remove an aggregate object.
+     *
+     * @param string $classname  the class of the object to remove
+     *
+     * @return bool  TRUE if an object was removed, FALSE if not
+     *
+     * @access public
+     */
+    function removeAggregateObject($classname)
+    {
+        $ok = false;
+        $classname = strtolower($classname);
+        reset($this->_method_map);
+        while (list($method, $obj) = each($this->_method_map)) {
+            if (is_a($obj, $classname)) {
+                unset($this->_method_map[$method]);
+                $ok = true;
+            }
+        }
+        return $ok;
+    }
+
+    // }}}
+    // {{{ __call()
+
+    /**
+     * Overloaded object call handler, called each time an
+     * undefined/aggregated method is invoked.  This method repeats
+     * the call in the right aggregate object and passes on the return
+     * value.
+     *
+     * @param string $method  which method that was called
+     *
+     * @param string $args    An array of the parameters passed in the
+     *                        original call
+     *
+     * @return mixed  The return value from the aggregated method, or a PEAR
+     *                error if the called method was unknown.
+     */
+    function __call($method, $args, &$retval)
+    {
+        $method = strtolower($method);
+        if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) {
+            $this->addAggregateObject($this->_autoload_map[$method]);
+        }
+        if (isset($this->_method_map[$method])) {
+            $retval = call_user_func_array(array($this->_method_map[$method], $method), $args);
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+}
+
+overload("PEAR_Autoloader");
+
+?>
diff --git a/3rdparty/PEAR/Builder.php b/3rdparty/PEAR/Builder.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f6cc135d1ef825c92a4b89d683a601fdb09e0eb
--- /dev/null
+++ b/3rdparty/PEAR/Builder.php
@@ -0,0 +1,426 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Sæther Bakken <ssb@php.net>                            |
+// +----------------------------------------------------------------------+
+//
+// $Id: Builder.php,v 1.16.2.3 2005/02/17 17:55:01 cellog Exp $
+
+require_once 'PEAR/Common.php';
+
+/**
+ * Class to handle building (compiling) extensions.
+ *
+ * @author Stig Sæther Bakken <ssb@php.net>
+ */
+class PEAR_Builder extends PEAR_Common
+{
+    // {{{ properties
+
+    var $php_api_version = 0;
+    var $zend_module_api_no = 0;
+    var $zend_extension_api_no = 0;
+
+    var $extensions_built = array();
+
+    var $current_callback = null;
+
+    // used for msdev builds
+    var $_lastline = null;
+    var $_firstline = null;
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Builder constructor.
+     *
+     * @param object $ui user interface object (instance of PEAR_Frontend_*)
+     *
+     * @access public
+     */
+    function PEAR_Builder(&$ui)
+    {
+        parent::PEAR_Common();
+        $this->setFrontendObject($ui);
+    }
+
+    // }}}
+
+    // {{{ _build_win32()
+
+    /**
+     * Build an extension from source on windows.
+     * requires msdev
+     */
+    function _build_win32($descfile, $callback = null)
+    {
+        if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) {
+            return $info;
+        }
+        $dir = dirname($descfile);
+        $old_cwd = getcwd();
+
+        if (!@chdir($dir)) {
+            return $this->raiseError("could not chdir to $dir");
+        }
+        $this->log(2, "building in $dir");
+
+        $dsp = $info['package'].'.dsp';
+        if (!@is_file("$dir/$dsp")) {
+            return $this->raiseError("The DSP $dsp does not exist.");
+        }
+        // XXX TODO: make release build type configurable
+        $command = 'msdev '.$dsp.' /MAKE "'.$info['package']. ' - Release"';
+
+        $this->current_callback = $callback;
+        $err = $this->_runCommand($command, array(&$this, 'msdevCallback'));
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+
+        // figure out the build platform and type
+        $platform = 'Win32';
+        $buildtype = 'Release';
+        if (preg_match('/.*?'.$info['package'].'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) {
+            $platform = $matches[1];
+            $buildtype = $matches[2];
+        }
+
+        if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) {
+            if ($matches[2]) {
+                // there were errors in the build
+                return $this->raiseError("There were errors during compilation.");
+            }
+            $out = $matches[1];
+        } else {
+            return $this->raiseError("Did not understand the completion status returned from msdev.exe.");
+        }
+
+        // msdev doesn't tell us the output directory :/
+        // open the dsp, find /out and use that directory
+        $dsptext = join(file($dsp),'');
+
+        // this regex depends on the build platform and type having been
+        // correctly identified above.
+        $regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'.
+                    $info['package'].'\s-\s'.
+                    $platform.'\s'.
+                    $buildtype.'").*?'.
+                    '\/out:"(.*?)"/is';
+
+        if ($dsptext && preg_match($regex,$dsptext,$matches)) {
+            // what we get back is a relative path to the output file itself.
+            $outfile = realpath($matches[2]);
+        } else {
+            return $this->raiseError("Could not retrieve output information from $dsp.");
+        }
+        if (@copy($outfile, "$dir/$out")) {
+            $outfile = "$dir/$out";
+        }
+
+        $built_files[] = array(
+            'file' => "$outfile",
+            'php_api' => $this->php_api_version,
+            'zend_mod_api' => $this->zend_module_api_no,
+            'zend_ext_api' => $this->zend_extension_api_no,
+            );
+
+        return $built_files;
+    }
+    // }}}
+
+    // {{{ msdevCallback()
+    function msdevCallback($what, $data)
+    {
+        if (!$this->_firstline)
+            $this->_firstline = $data;
+        $this->_lastline = $data;
+    }
+    // }}}
+
+    // {{{ _harventInstDir
+    /**
+     * @param string
+     * @param string
+     * @param array
+     * @access private
+     */
+    function _harvestInstDir($dest_prefix, $dirname, &$built_files)
+    {
+        $d = opendir($dirname);
+        if (!$d)
+            return false;
+
+        $ret = true;
+        while (($ent = readdir($d)) !== false) {
+            if ($ent{0} == '.')
+                continue;
+
+            $full = $dirname . DIRECTORY_SEPARATOR . $ent;
+            if (is_dir($full)) {
+                if (!$this->_harvestInstDir(
+                        $dest_prefix . DIRECTORY_SEPARATOR . $ent,
+                        $full, $built_files)) {
+                    $ret = false;
+                    break;
+                }
+            } else {
+                $dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent;
+                $built_files[] = array(
+                        'file' => $full,
+                        'dest' => $dest,
+                        'php_api' => $this->php_api_version,
+                        'zend_mod_api' => $this->zend_module_api_no,
+                        'zend_ext_api' => $this->zend_extension_api_no,
+                        );
+            }
+        }
+        closedir($d);
+        return $ret;
+    }
+
+    // }}}
+
+    // {{{ build()
+
+    /**
+     * Build an extension from source.  Runs "phpize" in the source
+     * directory, but compiles in a temporary directory
+     * (/var/tmp/pear-build-USER/PACKAGE-VERSION).
+     *
+     * @param string $descfile path to XML package description file
+     *
+     * @param mixed $callback callback function used to report output,
+     * see PEAR_Builder::_runCommand for details
+     *
+     * @return array an array of associative arrays with built files,
+     * format:
+     * array( array( 'file' => '/path/to/ext.so',
+     *               'php_api' => YYYYMMDD,
+     *               'zend_mod_api' => YYYYMMDD,
+     *               'zend_ext_api' => YYYYMMDD ),
+     *        ... )
+     *
+     * @access public
+     *
+     * @see PEAR_Builder::_runCommand
+     * @see PEAR_Common::infoFromDescriptionFile
+     */
+    function build($descfile, $callback = null)
+    {
+        if (PEAR_OS == "Windows") {
+            return $this->_build_win32($descfile,$callback);
+        }
+        if (PEAR_OS != 'Unix') {
+            return $this->raiseError("building extensions not supported on this platform");
+        }
+        if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) {
+            return $info;
+        }
+        $dir = dirname($descfile);
+        $old_cwd = getcwd();
+        if (!@chdir($dir)) {
+            return $this->raiseError("could not chdir to $dir");
+        }
+        $vdir = "$info[package]-$info[version]";
+        if (is_dir($vdir)) {
+            chdir($vdir);
+        }
+        $dir = getcwd();
+        $this->log(2, "building in $dir");
+        $this->current_callback = $callback;
+        putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH'));
+        $err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback'));
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+        if (!$err) {
+            return $this->raiseError("`phpize' failed");
+        }
+
+        // {{{ start of interactive part
+        $configure_command = "$dir/configure";
+        if (isset($info['configure_options'])) {
+            foreach ($info['configure_options'] as $o) {
+                list($r) = $this->ui->userDialog('build',
+                                                 array($o['prompt']),
+                                                 array('text'),
+                                                 array(@$o['default']));
+                if (substr($o['name'], 0, 5) == 'with-' &&
+                    ($r == 'yes' || $r == 'autodetect')) {
+                    $configure_command .= " --$o[name]";
+                } else {
+                    $configure_command .= " --$o[name]=".trim($r);
+                }
+            }
+        }
+        // }}} end of interactive part
+
+        // FIXME make configurable
+        if(!$user=getenv('USER')){
+            $user='defaultuser';
+        }
+        $build_basedir = "/var/tmp/pear-build-$user";
+        $build_dir = "$build_basedir/$info[package]-$info[version]";
+        $inst_dir = "$build_basedir/install-$info[package]-$info[version]";
+        $this->log(1, "building in $build_dir");
+        if (is_dir($build_dir)) {
+            System::rm('-rf', $build_dir);
+        }
+        if (!System::mkDir(array('-p', $build_dir))) {
+            return $this->raiseError("could not create build dir: $build_dir");
+        }
+        $this->addTempFile($build_dir);
+        if (!System::mkDir(array('-p', $inst_dir))) {
+            return $this->raiseError("could not create temporary install dir: $inst_dir");
+        }
+        $this->addTempFile($inst_dir);
+
+        if (getenv('MAKE')) {
+            $make_command = getenv('MAKE');
+        } else {
+            $make_command = 'make';
+        }
+        $to_run = array(
+            $configure_command,
+            $make_command,
+            "$make_command INSTALL_ROOT=\"$inst_dir\" install",
+            "find \"$inst_dir\" -ls"
+            );
+        if (!@chdir($build_dir)) {
+            return $this->raiseError("could not chdir to $build_dir");
+        }
+        putenv('PHP_PEAR_VERSION=@PEAR-VER@');
+        foreach ($to_run as $cmd) {
+            $err = $this->_runCommand($cmd, $callback);
+            if (PEAR::isError($err)) {
+                chdir($old_cwd);
+                return $err;
+            }
+            if (!$err) {
+                chdir($old_cwd);
+                return $this->raiseError("`$cmd' failed");
+            }
+        }
+        if (!($dp = opendir("modules"))) {
+            chdir($old_cwd);
+            return $this->raiseError("no `modules' directory found");
+        }
+        $built_files = array();
+        $prefix = exec("php-config --prefix");
+        $this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files);
+        chdir($old_cwd);
+        return $built_files;
+    }
+
+    // }}}
+    // {{{ phpizeCallback()
+
+    /**
+     * Message callback function used when running the "phpize"
+     * program.  Extracts the API numbers used.  Ignores other message
+     * types than "cmdoutput".
+     *
+     * @param string $what the type of message
+     * @param mixed $data the message
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function phpizeCallback($what, $data)
+    {
+        if ($what != 'cmdoutput') {
+            return;
+        }
+        $this->log(1, rtrim($data));
+        if (preg_match('/You should update your .aclocal.m4/', $data)) {
+            return;
+        }
+        $matches = array();
+        if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) {
+            $member = preg_replace('/[^a-z]/', '_', strtolower($matches[1]));
+            $apino = (int)$matches[2];
+            if (isset($this->$member)) {
+                $this->$member = $apino;
+                //$msg = sprintf("%-22s : %d", $matches[1], $apino);
+                //$this->log(1, $msg);
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _runCommand()
+
+    /**
+     * Run an external command, using a message callback to report
+     * output.  The command will be run through popen and output is
+     * reported for every line with a "cmdoutput" message with the
+     * line string, including newlines, as payload.
+     *
+     * @param string $command the command to run
+     *
+     * @param mixed $callback (optional) function to use as message
+     * callback
+     *
+     * @return bool whether the command was successful (exit code 0
+     * means success, any other means failure)
+     *
+     * @access private
+     */
+    function _runCommand($command, $callback = null)
+    {
+        $this->log(1, "running: $command");
+        $pp = @popen("$command 2>&1", "r");
+        if (!$pp) {
+            return $this->raiseError("failed to run `$command'");
+        }
+        if ($callback && $callback[0]->debug == 1) {
+            $olddbg = $callback[0]->debug;
+            $callback[0]->debug = 2;
+        }
+
+        while ($line = fgets($pp, 1024)) {
+            if ($callback) {
+                call_user_func($callback, 'cmdoutput', $line);
+            } else {
+                $this->log(2, rtrim($line));
+            }
+        }
+        if ($callback && isset($olddbg)) {
+            $callback[0]->debug = $olddbg;
+        }
+        $exitcode = @pclose($pp);
+        return ($exitcode == 0);
+    }
+
+    // }}}
+    // {{{ log()
+
+    function log($level, $msg)
+    {
+        if ($this->current_callback) {
+            if ($this->debug >= $level) {
+                call_user_func($this->current_callback, 'output', $msg);
+            }
+            return;
+        }
+        return PEAR_Common::log($level, $msg);
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Command.php b/3rdparty/PEAR/Command.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ea68743d2063e01a60611409efd859e8135ce2c
--- /dev/null
+++ b/3rdparty/PEAR/Command.php
@@ -0,0 +1,398 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Command.php,v 1.24 2004/05/16 15:43:30 pajoye Exp $
+
+
+require_once "PEAR.php";
+
+/**
+ * List of commands and what classes they are implemented in.
+ * @var array command => implementing class
+ */
+$GLOBALS['_PEAR_Command_commandlist'] = array();
+
+/**
+ * List of shortcuts to common commands.
+ * @var array shortcut => command
+ */
+$GLOBALS['_PEAR_Command_shortcuts'] = array();
+
+/**
+ * Array of command objects
+ * @var array class => object
+ */
+$GLOBALS['_PEAR_Command_objects'] = array();
+
+/**
+ * Which user interface class is being used.
+ * @var string class name
+ */
+$GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI';
+
+/**
+ * Instance of $_PEAR_Command_uiclass.
+ * @var object
+ */
+$GLOBALS['_PEAR_Command_uiobject'] = null;
+
+/**
+ * PEAR command class, a simple factory class for administrative
+ * commands.
+ *
+ * How to implement command classes:
+ *
+ * - The class must be called PEAR_Command_Nnn, installed in the
+ *   "PEAR/Common" subdir, with a method called getCommands() that
+ *   returns an array of the commands implemented by the class (see
+ *   PEAR/Command/Install.php for an example).
+ *
+ * - The class must implement a run() function that is called with three
+ *   params:
+ *
+ *    (string) command name
+ *    (array)  assoc array with options, freely defined by each
+ *             command, for example:
+ *             array('force' => true)
+ *    (array)  list of the other parameters
+ *
+ *   The run() function returns a PEAR_CommandResponse object.  Use
+ *   these methods to get information:
+ *
+ *    int getStatus()   Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL)
+ *                      *_PARTIAL means that you need to issue at least
+ *                      one more command to complete the operation
+ *                      (used for example for validation steps).
+ *
+ *    string getMessage()  Returns a message for the user.  Remember,
+ *                         no HTML or other interface-specific markup.
+ *
+ *   If something unexpected happens, run() returns a PEAR error.
+ *
+ * - DON'T OUTPUT ANYTHING! Return text for output instead.
+ *
+ * - DON'T USE HTML! The text you return will be used from both Gtk,
+ *   web and command-line interfaces, so for now, keep everything to
+ *   plain text.
+ *
+ * - DON'T USE EXIT OR DIE! Always use pear errors.  From static
+ *   classes do PEAR::raiseError(), from other classes do
+ *   $this->raiseError().
+ */
+class PEAR_Command
+{
+    // {{{ factory()
+
+    /**
+     * Get the right object for executing a command.
+     *
+     * @param string $command The name of the command
+     * @param object $config  Instance of PEAR_Config object
+     *
+     * @return object the command object or a PEAR error
+     *
+     * @access public
+     * @static
+     */
+    function factory($command, &$config)
+    {
+        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+            PEAR_Command::registerCommands();
+        }
+        if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) {
+            $command = $GLOBALS['_PEAR_Command_shortcuts'][$command];
+        }
+        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+            return PEAR::raiseError("unknown command `$command'");
+        }
+        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+        if (!class_exists($class)) {
+            return PEAR::raiseError("unknown command `$command'");
+        }
+        $ui =& PEAR_Command::getFrontendObject();
+        $obj = &new $class($ui, $config);
+        return $obj;
+    }
+
+    // }}}
+    // {{{ & getFrontendObject()
+
+    /**
+     * Get instance of frontend object.
+     *
+     * @return object
+     * @static
+     */
+    function &getFrontendObject()
+    {
+        if (empty($GLOBALS['_PEAR_Command_uiobject'])) {
+            $GLOBALS['_PEAR_Command_uiobject'] = &new $GLOBALS['_PEAR_Command_uiclass'];
+        }
+        return $GLOBALS['_PEAR_Command_uiobject'];
+    }
+
+    // }}}
+    // {{{ & setFrontendClass()
+
+    /**
+     * Load current frontend class.
+     *
+     * @param string $uiclass Name of class implementing the frontend
+     *
+     * @return object the frontend object, or a PEAR error
+     * @static
+     */
+    function &setFrontendClass($uiclass)
+    {
+        if (is_object($GLOBALS['_PEAR_Command_uiobject']) &&
+              is_a($GLOBALS['_PEAR_Command_uiobject'], $uiclass)) {
+            return $GLOBALS['_PEAR_Command_uiobject'];
+        }
+        if (!class_exists($uiclass)) {
+            $file = str_replace('_', '/', $uiclass) . '.php';
+            if (PEAR_Command::isIncludeable($file)) {
+                include_once $file;
+            }
+        }
+        if (class_exists($uiclass)) {
+            $obj = &new $uiclass;
+            // quick test to see if this class implements a few of the most
+            // important frontend methods
+            if (method_exists($obj, 'userConfirm')) {
+                $GLOBALS['_PEAR_Command_uiobject'] = &$obj;
+                $GLOBALS['_PEAR_Command_uiclass'] = $uiclass;
+                return $obj;
+            } else {
+                $err = PEAR::raiseError("not a frontend class: $uiclass");
+                return $err;
+            }
+        }
+        $err = PEAR::raiseError("no such class: $uiclass");
+        return $err;
+    }
+
+    // }}}
+    // {{{ setFrontendType()
+
+    // }}}
+    // {{{ isIncludeable()
+
+    /**
+     * @param string $path relative or absolute include path
+     * @return boolean
+     * @static
+     */
+    function isIncludeable($path)
+    {
+        if (file_exists($path) && is_readable($path)) {
+            return true;
+        }
+        $ipath = explode(PATH_SEPARATOR, ini_get('include_path'));
+        foreach ($ipath as $include) {
+            $test = realpath($include . DIRECTORY_SEPARATOR . $path);
+            if (file_exists($test) && is_readable($test)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Set current frontend.
+     *
+     * @param string $uitype Name of the frontend type (for example "CLI")
+     *
+     * @return object the frontend object, or a PEAR error
+     * @static
+     */
+    function setFrontendType($uitype)
+    {
+        $uiclass = 'PEAR_Frontend_' . $uitype;
+        return PEAR_Command::setFrontendClass($uiclass);
+    }
+
+    // }}}
+    // {{{ registerCommands()
+
+    /**
+     * Scan through the Command directory looking for classes
+     * and see what commands they implement.
+     *
+     * @param bool   (optional) if FALSE (default), the new list of
+     *               commands should replace the current one.  If TRUE,
+     *               new entries will be merged with old.
+     *
+     * @param string (optional) where (what directory) to look for
+     *               classes, defaults to the Command subdirectory of
+     *               the directory from where this file (__FILE__) is
+     *               included.
+     *
+     * @return bool TRUE on success, a PEAR error on failure
+     *
+     * @access public
+     * @static
+     */
+    function registerCommands($merge = false, $dir = null)
+    {
+        if ($dir === null) {
+            $dir = dirname(__FILE__) . '/Command';
+        }
+        $dp = @opendir($dir);
+        if (empty($dp)) {
+            return PEAR::raiseError("registerCommands: opendir($dir) failed");
+        }
+        if (!$merge) {
+            $GLOBALS['_PEAR_Command_commandlist'] = array();
+        }
+        while ($entry = readdir($dp)) {
+            if ($entry{0} == '.' || substr($entry, -4) != '.php' || $entry == 'Common.php') {
+                continue;
+            }
+            $class = "PEAR_Command_".substr($entry, 0, -4);
+            $file = "$dir/$entry";
+            include_once $file;
+            // List of commands
+            if (empty($GLOBALS['_PEAR_Command_objects'][$class])) {
+                $GLOBALS['_PEAR_Command_objects'][$class] = &new $class($ui, $config);
+            }
+            $implements = $GLOBALS['_PEAR_Command_objects'][$class]->getCommands();
+            foreach ($implements as $command => $desc) {
+                $GLOBALS['_PEAR_Command_commandlist'][$command] = $class;
+                $GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc;
+            }
+            $shortcuts = $GLOBALS['_PEAR_Command_objects'][$class]->getShortcuts();
+            foreach ($shortcuts as $shortcut => $command) {
+                $GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command;
+            }
+        }
+        @closedir($dp);
+        return true;
+    }
+
+    // }}}
+    // {{{ getCommands()
+
+    /**
+     * Get the list of currently supported commands, and what
+     * classes implement them.
+     *
+     * @return array command => implementing class
+     *
+     * @access public
+     * @static
+     */
+    function getCommands()
+    {
+        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+            PEAR_Command::registerCommands();
+        }
+        return $GLOBALS['_PEAR_Command_commandlist'];
+    }
+
+    // }}}
+    // {{{ getShortcuts()
+
+    /**
+     * Get the list of command shortcuts.
+     *
+     * @return array shortcut => command
+     *
+     * @access public
+     * @static
+     */
+    function getShortcuts()
+    {
+        if (empty($GLOBALS['_PEAR_Command_shortcuts'])) {
+            PEAR_Command::registerCommands();
+        }
+        return $GLOBALS['_PEAR_Command_shortcuts'];
+    }
+
+    // }}}
+    // {{{ getGetoptArgs()
+
+    /**
+     * Compiles arguments for getopt.
+     *
+     * @param string $command     command to get optstring for
+     * @param string $short_args  (reference) short getopt format
+     * @param array  $long_args   (reference) long getopt format
+     *
+     * @return void
+     *
+     * @access public
+     * @static
+     */
+    function getGetoptArgs($command, &$short_args, &$long_args)
+    {
+        if (empty($GLOBALS['_PEAR_Command_commandlist'])) {
+            PEAR_Command::registerCommands();
+        }
+        if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) {
+            return null;
+        }
+        $class = $GLOBALS['_PEAR_Command_commandlist'][$command];
+        $obj = &$GLOBALS['_PEAR_Command_objects'][$class];
+        return $obj->getGetoptArgs($command, $short_args, $long_args);
+    }
+
+    // }}}
+    // {{{ getDescription()
+
+    /**
+     * Get description for a command.
+     *
+     * @param  string $command Name of the command
+     *
+     * @return string command description
+     *
+     * @access public
+     * @static
+     */
+    function getDescription($command)
+    {
+        if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) {
+            return null;
+        }
+        return $GLOBALS['_PEAR_Command_commanddesc'][$command];
+    }
+
+    // }}}
+    // {{{ getHelp()
+
+    /**
+     * Get help for command.
+     *
+     * @param string $command Name of the command to return help for
+     *
+     * @access public
+     * @static
+     */
+    function getHelp($command)
+    {
+        $cmds = PEAR_Command::getCommands();
+        if (isset($cmds[$command])) {
+            $class = $cmds[$command];
+            return $GLOBALS['_PEAR_Command_objects'][$class]->getHelp($command);
+        }
+        return false;
+    }
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Command/Auth.php b/3rdparty/PEAR/Command/Auth.php
new file mode 100644
index 0000000000000000000000000000000000000000..0b9d3d3965d46ab1c474794bf6f250fed3e7ea6f
--- /dev/null
+++ b/3rdparty/PEAR/Command/Auth.php
@@ -0,0 +1,155 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Auth.php,v 1.15 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Remote.php";
+require_once "PEAR/Config.php";
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ */
+class PEAR_Command_Auth extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'login' => array(
+            'summary' => 'Connects and authenticates to remote server',
+            'shortcut' => 'li',
+            'function' => 'doLogin',
+            'options' => array(),
+            'doc' => '
+Log in to the remote server.  To use remote functions in the installer
+that require any kind of privileges, you need to log in first.  The
+username and password you enter here will be stored in your per-user
+PEAR configuration (~/.pearrc on Unix-like systems).  After logging
+in, your username and password will be sent along in subsequent
+operations on the remote server.',
+            ),
+        'logout' => array(
+            'summary' => 'Logs out from the remote server',
+            'shortcut' => 'lo',
+            'function' => 'doLogout',
+            'options' => array(),
+            'doc' => '
+Logs out from the remote server.  This command does not actually
+connect to the remote server, it only deletes the stored username and
+password from your user configuration.',
+            )
+
+        );
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Auth constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Auth(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doLogin()
+
+    /**
+     * Execute the 'login' command.
+     *
+     * @param string $command command name
+     *
+     * @param array $options option_name => value
+     *
+     * @param array $params list of additional parameters
+     *
+     * @return bool TRUE on success, FALSE for unknown commands, or
+     * a PEAR error on failure
+     *
+     * @access public
+     */
+    function doLogin($command, $options, $params)
+    {
+        $server = $this->config->get('master_server');
+        $remote = new PEAR_Remote($this->config);
+        $username = $this->config->get('username');
+        if (empty($username)) {
+            $username = @$_ENV['USER'];
+        }
+        $this->ui->outputData("Logging in to $server.", $command);
+        
+        list($username, $password) = $this->ui->userDialog(
+            $command,
+            array('Username', 'Password'),
+            array('text',     'password'),
+            array($username,  '')
+            );
+        $username = trim($username);
+        $password = trim($password);
+        
+        $this->config->set('username', $username);
+        $this->config->set('password', $password);
+        
+        $remote->expectError(401);
+        $ok = $remote->call('logintest');
+        $remote->popExpect();
+        if ($ok === true) {
+            $this->ui->outputData("Logged in.", $command);
+            $this->config->store();
+        } else {
+            return $this->raiseError("Login failed!");
+        }
+
+    }
+
+    // }}}
+    // {{{ doLogout()
+
+    /**
+     * Execute the 'logout' command.
+     *
+     * @param string $command command name
+     *
+     * @param array $options option_name => value
+     *
+     * @param array $params list of additional parameters
+     *
+     * @return bool TRUE on success, FALSE for unknown commands, or
+     * a PEAR error on failure
+     *
+     * @access public
+     */
+    function doLogout($command, $options, $params)
+    {
+        $server = $this->config->get('master_server');
+        $this->ui->outputData("Logging out from $server.", $command);
+        $this->config->remove('username');
+        $this->config->remove('password');
+        $this->config->store();
+    }
+
+    // }}}
+}
+
+?>
\ No newline at end of file
diff --git a/3rdparty/PEAR/Command/Build.php b/3rdparty/PEAR/Command/Build.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ecbbc92f5fe38a3f4f48ac33f9ec5232ea1590f
--- /dev/null
+++ b/3rdparty/PEAR/Command/Build.php
@@ -0,0 +1,89 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// |         Tomas V.V.Cox <cox@idecnet.com>                              |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Build.php,v 1.9 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Builder.php";
+
+/**
+ * PEAR commands for building extensions.
+ *
+ */
+class PEAR_Command_Build extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'build' => array(
+            'summary' => 'Build an Extension From C Source',
+            'function' => 'doBuild',
+            'shortcut' => 'b',
+            'options' => array(),
+            'doc' => '[package.xml]
+Builds one or more extensions contained in a package.'
+            ),
+        );
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Build constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Build(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doBuild()
+
+    function doBuild($command, $options, $params)
+    {
+        if (sizeof($params) < 1) {
+            $params[0] = 'package.xml';
+        }
+        $builder = &new PEAR_Builder($this->ui);
+        $this->debug = $this->config->get('verbose');
+        $err = $builder->build($params[0], array(&$this, 'buildCallback'));
+        if (PEAR::isError($err)) {
+            return $err;
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ buildCallback()
+
+    function buildCallback($what, $data)
+    {
+        if (($what == 'cmdoutput' && $this->debug > 1) ||
+            ($what == 'output' && $this->debug > 0)) {
+            $this->ui->outputData(rtrim($data), 'build');
+        }
+    }
+
+    // }}}
+}
diff --git a/3rdparty/PEAR/Command/Common.php b/3rdparty/PEAR/Command/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6ace694caf92c625b8bd64647f965c5194d3206
--- /dev/null
+++ b/3rdparty/PEAR/Command/Common.php
@@ -0,0 +1,249 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Sæther Bakken <ssb@php.net>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.24 2004/01/08 17:33:13 sniper Exp $
+
+require_once "PEAR.php";
+
+class PEAR_Command_Common extends PEAR
+{
+    // {{{ properties
+
+    /**
+     * PEAR_Config object used to pass user system and configuration
+     * on when executing commands
+     *
+     * @var object
+     */
+    var $config;
+
+    /**
+     * User Interface object, for all interaction with the user.
+     * @var object
+     */
+    var $ui;
+
+    var $_deps_rel_trans = array(
+                                 'lt' => '<',
+                                 'le' => '<=',
+                                 'eq' => '=',
+                                 'ne' => '!=',
+                                 'gt' => '>',
+                                 'ge' => '>=',
+                                 'has' => '=='
+                                 );
+
+    var $_deps_type_trans = array(
+                                  'pkg' => 'package',
+                                  'extension' => 'extension',
+                                  'php' => 'PHP',
+                                  'prog' => 'external program',
+                                  'ldlib' => 'external library for linking',
+                                  'rtlib' => 'external runtime library',
+                                  'os' => 'operating system',
+                                  'websrv' => 'web server',
+                                  'sapi' => 'SAPI backend'
+                                  );
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Common constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Common(&$ui, &$config)
+    {
+        parent::PEAR();
+        $this->config = &$config;
+        $this->ui = &$ui;
+    }
+
+    // }}}
+
+    // {{{ getCommands()
+
+    /**
+     * Return a list of all the commands defined by this class.
+     * @return array list of commands
+     * @access public
+     */
+    function getCommands()
+    {
+        $ret = array();
+        foreach (array_keys($this->commands) as $command) {
+            $ret[$command] = $this->commands[$command]['summary'];
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ getShortcuts()
+
+    /**
+     * Return a list of all the command shortcuts defined by this class.
+     * @return array shortcut => command
+     * @access public
+     */
+    function getShortcuts()
+    {
+        $ret = array();
+        foreach (array_keys($this->commands) as $command) {
+            if (isset($this->commands[$command]['shortcut'])) {
+                $ret[$this->commands[$command]['shortcut']] = $command;
+            }
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ getOptions()
+
+    function getOptions($command)
+    {
+        return @$this->commands[$command]['options'];
+    }
+
+    // }}}
+    // {{{ getGetoptArgs()
+
+    function getGetoptArgs($command, &$short_args, &$long_args)
+    {
+        $short_args = "";
+        $long_args = array();
+        if (empty($this->commands[$command])) {
+            return;
+        }
+        reset($this->commands[$command]);
+        while (list($option, $info) = each($this->commands[$command]['options'])) {
+            $larg = $sarg = '';
+            if (isset($info['arg'])) {
+                if ($info['arg']{0} == '(') {
+                    $larg = '==';
+                    $sarg = '::';
+                    $arg = substr($info['arg'], 1, -1);
+                } else {
+                    $larg = '=';
+                    $sarg = ':';
+                    $arg = $info['arg'];
+                }
+            }
+            if (isset($info['shortopt'])) {
+                $short_args .= $info['shortopt'] . $sarg;
+            }
+            $long_args[] = $option . $larg;
+        }
+    }
+
+    // }}}
+    // {{{ getHelp()
+    /**
+    * Returns the help message for the given command
+    *
+    * @param string $command The command
+    * @return mixed A fail string if the command does not have help or
+    *               a two elements array containing [0]=>help string,
+    *               [1]=> help string for the accepted cmd args
+    */
+    function getHelp($command)
+    {
+        $config = &PEAR_Config::singleton();
+        $help = @$this->commands[$command]['doc'];
+        if (empty($help)) {
+            // XXX (cox) Fallback to summary if there is no doc (show both?)
+            if (!$help = @$this->commands[$command]['summary']) {
+                return "No help for command \"$command\"";
+            }
+        }
+        if (preg_match_all('/{config\s+([^\}]+)}/e', $help, $matches)) {
+            foreach($matches[0] as $k => $v) {
+                $help = preg_replace("/$v/", $config->get($matches[1][$k]), $help);
+            }
+        }
+        return array($help, $this->getHelpArgs($command));
+    }
+
+    // }}}
+    // {{{ getHelpArgs()
+    /**
+    * Returns the help for the accepted arguments of a command
+    *
+    * @param  string $command
+    * @return string The help string
+    */
+    function getHelpArgs($command)
+    {
+        if (isset($this->commands[$command]['options']) &&
+            count($this->commands[$command]['options']))
+        {
+            $help = "Options:\n";
+            foreach ($this->commands[$command]['options'] as $k => $v) {
+                if (isset($v['arg'])) {
+                    if ($v['arg']{0} == '(') {
+                        $arg = substr($v['arg'], 1, -1);
+                        $sapp = " [$arg]";
+                        $lapp = "[=$arg]";
+                    } else {
+                        $sapp = " $v[arg]";
+                        $lapp = "=$v[arg]";
+                    }
+                } else {
+                    $sapp = $lapp = "";
+                }
+                if (isset($v['shortopt'])) {
+                    $s = $v['shortopt'];
+                    @$help .= "  -$s$sapp, --$k$lapp\n";
+                } else {
+                    @$help .= "  --$k$lapp\n";
+                }
+                $p = "        ";
+                $doc = rtrim(str_replace("\n", "\n$p", $v['doc']));
+                $help .= "        $doc\n";
+            }
+            return $help;
+        }
+        return null;
+    }
+
+    // }}}
+    // {{{ run()
+
+    function run($command, $options, $params)
+    {
+        $func = @$this->commands[$command]['function'];
+        if (empty($func)) {
+            // look for shortcuts
+            foreach (array_keys($this->commands) as $cmd) {
+                if (@$this->commands[$cmd]['shortcut'] == $command) {
+                    $command = $cmd;
+                    $func = @$this->commands[$command]['function'];
+                    if (empty($func)) {
+                        return $this->raiseError("unknown command `$command'");
+                    }
+                    break;
+                }
+            }
+        }
+        return $this->$func($command, $options, $params);
+    }
+
+    // }}}
+}
+
+?>
\ No newline at end of file
diff --git a/3rdparty/PEAR/Command/Config.php b/3rdparty/PEAR/Command/Config.php
new file mode 100644
index 0000000000000000000000000000000000000000..474a2345170c6f217da573e45a33c1e11a88d7d8
--- /dev/null
+++ b/3rdparty/PEAR/Command/Config.php
@@ -0,0 +1,225 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// |         Tomas V.V.Cox <cox@idecnet.com>                              |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Config.php,v 1.27 2004/06/15 16:48:49 pajoye Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Config.php";
+
+/**
+ * PEAR commands for managing configuration data.
+ *
+ */
+class PEAR_Command_Config extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'config-show' => array(
+            'summary' => 'Show All Settings',
+            'function' => 'doConfigShow',
+            'shortcut' => 'csh',
+            'options' => array(),
+            'doc' => '
+Displays all configuration values.  An optional argument
+may be used to tell which configuration layer to display.  Valid
+configuration layers are "user", "system" and "default".
+',
+            ),
+        'config-get' => array(
+            'summary' => 'Show One Setting',
+            'function' => 'doConfigGet',
+            'shortcut' => 'cg',
+            'options' => array(),
+            'doc' => '<parameter> [layer]
+Displays the value of one configuration parameter.  The
+first argument is the name of the parameter, an optional second argument
+may be used to tell which configuration layer to look in.  Valid configuration
+layers are "user", "system" and "default".  If no layer is specified, a value
+will be picked from the first layer that defines the parameter, in the order
+just specified.
+',
+            ),
+        'config-set' => array(
+            'summary' => 'Change Setting',
+            'function' => 'doConfigSet',
+            'shortcut' => 'cs',
+            'options' => array(),
+            'doc' => '<parameter> <value> [layer]
+Sets the value of one configuration parameter.  The first argument is
+the name of the parameter, the second argument is the new value.  Some
+parameters are subject to validation, and the command will fail with
+an error message if the new value does not make sense.  An optional
+third argument may be used to specify in which layer to set the
+configuration parameter.  The default layer is "user".
+',
+            ),
+        'config-help' => array(
+            'summary' => 'Show Information About Setting',
+            'function' => 'doConfigHelp',
+            'shortcut' => 'ch',
+            'options' => array(),
+            'doc' => '[parameter]
+Displays help for a configuration parameter.  Without arguments it
+displays help for all configuration parameters.
+',
+           ),
+        );
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Config constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Config(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doConfigShow()
+
+    function doConfigShow($command, $options, $params)
+    {
+        // $params[0] -> the layer
+        if ($error = $this->_checkLayer(@$params[0])) {
+            return $this->raiseError($error);
+        }
+        $keys = $this->config->getKeys();
+        sort($keys);
+        $data = array('caption' => 'Configuration:');
+        foreach ($keys as $key) {
+            $type = $this->config->getType($key);
+            $value = $this->config->get($key, @$params[0]);
+            if ($type == 'password' && $value) {
+                $value = '********';
+            }
+            if ($value === false) {
+                $value = 'false';
+            } elseif ($value === true) {
+                $value = 'true';
+            }
+            $data['data'][$this->config->getGroup($key)][] = array($this->config->getPrompt($key) , $key, $value);
+        }
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doConfigGet()
+
+    function doConfigGet($command, $options, $params)
+    {
+        // $params[0] -> the parameter
+        // $params[1] -> the layer
+        if ($error = $this->_checkLayer(@$params[1])) {
+            return $this->raiseError($error);
+        }
+        if (sizeof($params) < 1 || sizeof($params) > 2) {
+            return $this->raiseError("config-get expects 1 or 2 parameters");
+        } elseif (sizeof($params) == 1) {
+            $this->ui->outputData($this->config->get($params[0]), $command);
+        } else {
+            $data = $this->config->get($params[0], $params[1]);
+            $this->ui->outputData($data, $command);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ doConfigSet()
+
+    function doConfigSet($command, $options, $params)
+    {
+        // $param[0] -> a parameter to set
+        // $param[1] -> the value for the parameter
+        // $param[2] -> the layer
+        $failmsg = '';
+        if (sizeof($params) < 2 || sizeof($params) > 3) {
+            $failmsg .= "config-set expects 2 or 3 parameters";
+            return PEAR::raiseError($failmsg);
+        }
+        if ($error = $this->_checkLayer(@$params[2])) {
+            $failmsg .= $error;
+            return PEAR::raiseError($failmsg);
+        }
+        if (!call_user_func_array(array(&$this->config, 'set'), $params))
+        {
+            $failmsg = "config-set (" . implode(", ", $params) . ") failed";
+        } else {
+            $this->config->store();
+        }
+        if ($failmsg) {
+            return $this->raiseError($failmsg);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ doConfigHelp()
+
+    function doConfigHelp($command, $options, $params)
+    {
+        if (empty($params)) {
+            $params = $this->config->getKeys();
+        }
+        $data['caption']  = "Config help" . ((count($params) == 1) ? " for $params[0]" : '');
+        $data['headline'] = array('Name', 'Type', 'Description');
+        $data['border']   = true;
+        foreach ($params as $name) {
+            $type = $this->config->getType($name);
+            $docs = $this->config->getDocs($name);
+            if ($type == 'set') {
+                $docs = rtrim($docs) . "\nValid set: " .
+                    implode(' ', $this->config->getSetValues($name));
+            }
+            $data['data'][] = array($name, $type, $docs);
+        }
+        $this->ui->outputData($data, $command);
+    }
+
+    // }}}
+    // {{{ _checkLayer()
+
+    /**
+     * Checks if a layer is defined or not
+     *
+     * @param string $layer The layer to search for
+     * @return mixed False on no error or the error message
+     */
+    function _checkLayer($layer = null)
+    {
+        if (!empty($layer) && $layer != 'default') {
+            $layers = $this->config->getLayers();
+            if (!in_array($layer, $layers)) {
+                return " only the layers: \"" . implode('" or "', $layers) . "\" are supported";
+            }
+        }
+        return false;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Command/Install.php b/3rdparty/PEAR/Command/Install.php
new file mode 100644
index 0000000000000000000000000000000000000000..dce52f017e2fe0397c55678db37a0ac14e69d1f2
--- /dev/null
+++ b/3rdparty/PEAR/Command/Install.php
@@ -0,0 +1,470 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Sæther Bakken <ssb@php.net>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Install.php,v 1.53.2.1 2004/10/19 04:08:42 cellog Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Installer.php";
+
+/**
+ * PEAR commands for installation or deinstallation/upgrading of
+ * packages.
+ *
+ */
+class PEAR_Command_Install extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'install' => array(
+            'summary' => 'Install Package',
+            'function' => 'doInstall',
+            'shortcut' => 'i',
+            'options' => array(
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'will overwrite newer installed packages',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, install anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as installed',
+                    ),
+                'soft' => array(
+                    'shortopt' => 's',
+                    'doc' => 'soft install, fail silently, or upgrade if already installed',
+                    ),
+                'nobuild' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'don\'t build C extensions',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                'alldeps' => array(
+                    'shortopt' => 'a',
+                    'doc' => 'install all required and optional dependencies',
+                    ),
+                'onlyreqdeps' => array(
+                    'shortopt' => 'o',
+                    'doc' => 'install all required dependencies',
+                    ),
+                ),
+            'doc' => '<package> ...
+Installs one or more PEAR packages.  You can specify a package to
+install in four ways:
+
+"Package-1.0.tgz" : installs from a local file
+
+"http://example.com/Package-1.0.tgz" : installs from
+anywhere on the net.
+
+"package.xml" : installs the package described in
+package.xml.  Useful for testing, or for wrapping a PEAR package in
+another package manager such as RPM.
+
+"Package" : queries your configured server
+({config master_server}) and downloads the newest package with
+the preferred quality/state ({config preferred_state}).
+
+More than one package may be specified at once.  It is ok to mix these
+four ways of specifying packages.
+'),
+        'upgrade' => array(
+            'summary' => 'Upgrade Package',
+            'function' => 'doInstall',
+            'shortcut' => 'up',
+            'options' => array(
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'overwrite newer installed packages',
+                    ),
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, upgrade anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as upgraded',
+                    ),
+                'nobuild' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'don\'t build C extensions',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                'alldeps' => array(
+                    'shortopt' => 'a',
+                    'doc' => 'install all required and optional dependencies',
+                    ),
+                'onlyreqdeps' => array(
+                    'shortopt' => 'o',
+                    'doc' => 'install all required dependencies',
+                    ),
+                ),
+            'doc' => '<package> ...
+Upgrades one or more PEAR packages.  See documentation for the
+"install" command for ways to specify a package.
+
+When upgrading, your package will be updated if the provided new
+package has a higher version number (use the -f option if you need to
+upgrade anyway).
+
+More than one package may be specified at once.
+'),
+        'upgrade-all' => array(
+            'summary' => 'Upgrade All Packages',
+            'function' => 'doInstall',
+            'shortcut' => 'ua',
+            'options' => array(
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, upgrade anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not install files, only register the package as upgraded',
+                    ),
+                'nobuild' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'don\'t build C extensions',
+                    ),
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'request uncompressed files when downloading',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                ),
+            'doc' => '
+Upgrades all packages that have a newer release available.  Upgrades are
+done only if there is a release available of the state specified in
+"preferred_state" (currently {config preferred_state}), or a state considered
+more stable.
+'),
+        'uninstall' => array(
+            'summary' => 'Un-install Package',
+            'function' => 'doUninstall',
+            'shortcut' => 'un',
+            'options' => array(
+                'nodeps' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'ignore dependencies, uninstall anyway',
+                    ),
+                'register-only' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'do not remove files, only register the packages as not installed',
+                    ),
+                'installroot' => array(
+                    'shortopt' => 'R',
+                    'arg' => 'DIR',
+                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
+                    ),
+                'ignore-errors' => array(
+                    'doc' => 'force install even if there were errors',
+                    ),
+                ),
+            'doc' => '<package> ...
+Uninstalls one or more PEAR packages.  More than one package may be
+specified at once.
+'),
+        'bundle' => array(
+            'summary' => 'Unpacks a Pecl Package',
+            'function' => 'doBundle',
+            'shortcut' => 'bun',
+            'options' => array(
+                'destination' => array(
+                   'shortopt' => 'd',
+                    'arg' => 'DIR',
+                    'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
+                    ),
+                'force' => array(
+                    'shortopt' => 'f',
+                    'doc' => 'Force the unpacking even if there were errors in the package',
+                ),
+            ),
+            'doc' => '<package>
+Unpacks a Pecl Package into the selected location. It will download the
+package if needed.
+'),
+    );
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Install constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Install(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doInstall()
+
+    function doInstall($command, $options, $params)
+    {
+        require_once 'PEAR/Downloader.php';
+        if (empty($this->installer)) {
+            $this->installer = &new PEAR_Installer($this->ui);
+        }
+        if ($command == 'upgrade') {
+            $options['upgrade'] = true;
+        }
+        if ($command == 'upgrade-all') {
+            include_once "PEAR/Remote.php";
+            $options['upgrade'] = true;
+            $remote = &new PEAR_Remote($this->config);
+            $state = $this->config->get('preferred_state');
+            if (empty($state) || $state == 'any') {
+                $latest = $remote->call("package.listLatestReleases");
+            } else {
+                $latest = $remote->call("package.listLatestReleases", $state);
+            }
+            if (PEAR::isError($latest)) {
+                return $latest;
+            }
+            $reg = new PEAR_Registry($this->config->get('php_dir'));
+            $installed = array_flip($reg->listPackages());
+            $params = array();
+            foreach ($latest as $package => $info) {
+                $package = strtolower($package);
+                if (!isset($installed[$package])) {
+                    // skip packages we don't have installed
+                    continue;
+                }
+                $inst_version = $reg->packageInfo($package, 'version');
+                if (version_compare("$info[version]", "$inst_version", "le")) {
+                    // installed version is up-to-date
+                    continue;
+                }
+                $params[] = $package;
+                $this->ui->outputData(array('data' => "Will upgrade $package"), $command);
+            }
+        }
+        $this->downloader = &new PEAR_Downloader($this->ui, $options, $this->config);
+        $errors = array();
+        $downloaded = array();
+        $this->downloader->download($params);
+        $errors = $this->downloader->getErrorMsgs();
+        if (count($errors)) {
+            $err['data'] = array($errors);
+            $err['headline'] = 'Install Errors';
+            $this->ui->outputData($err);
+            return $this->raiseError("$command failed");
+        }
+        $downloaded = $this->downloader->getDownloadedPackages();
+        $this->installer->sortPkgDeps($downloaded);
+        foreach ($downloaded as $pkg) {
+            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
+            $info = $this->installer->install($pkg['file'], $options, $this->config);
+            PEAR::popErrorHandling();
+            if (PEAR::isError($info)) {
+                $this->ui->outputData('ERROR: ' .$info->getMessage());
+                continue;
+            }
+            if (is_array($info)) {
+                if ($this->config->get('verbose') > 0) {
+                    $label = "$info[package] $info[version]";
+                    $out = array('data' => "$command ok: $label");
+                    if (isset($info['release_warnings'])) {
+                        $out['release_warnings'] = $info['release_warnings'];
+                    }
+                    $this->ui->outputData($out, $command);
+                }
+            } else {
+                return $this->raiseError("$command failed");
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ doUninstall()
+
+    function doUninstall($command, $options, $params)
+    {
+        if (empty($this->installer)) {
+            $this->installer = &new PEAR_Installer($this->ui);
+        }
+        if (sizeof($params) < 1) {
+            return $this->raiseError("Please supply the package(s) you want to uninstall");
+        }
+        include_once 'PEAR/Registry.php';
+        $reg = new PEAR_Registry($this->config->get('php_dir'));
+        $newparams = array();
+        $badparams = array();
+        foreach ($params as $pkg) {
+            $info = $reg->packageInfo($pkg);
+            if ($info === null) {
+                $badparams[] = $pkg;
+            } else {
+                $newparams[] = $info;
+            }
+        }
+        $this->installer->sortPkgDeps($newparams, true);
+        $params = array();
+        foreach($newparams as $info) {
+            $params[] = $info['info']['package'];
+        }
+        $params = array_merge($params, $badparams);
+        foreach ($params as $pkg) {
+            if ($this->installer->uninstall($pkg, $options)) {
+                if ($this->config->get('verbose') > 0) {
+                    $this->ui->outputData("uninstall ok: $pkg", $command);
+                }
+            } else {
+                return $this->raiseError("uninstall failed: $pkg");
+            }
+        }
+        return true;
+    }
+
+    // }}}
+
+
+    // }}}
+    // {{{ doBundle()
+    /*
+    (cox) It just downloads and untars the package, does not do
+            any check that the PEAR_Installer::_installFile() does.
+    */
+
+    function doBundle($command, $options, $params)
+    {
+        if (empty($this->installer)) {
+            $this->installer = &new PEAR_Downloader($this->ui);
+        }
+        $installer = &$this->installer;
+        if (sizeof($params) < 1) {
+            return $this->raiseError("Please supply the package you want to bundle");
+        }
+        $pkgfile = $params[0];
+        $need_download = false;
+        if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+            $need_download = true;
+        } elseif (!@is_file($pkgfile)) {
+            if ($installer->validPackageName($pkgfile)) {
+                $pkgfile = $installer->getPackageDownloadUrl($pkgfile);
+                $need_download = true;
+            } else {
+                if (strlen($pkgfile)) {
+                    return $this->raiseError("Could not open the package file: $pkgfile");
+                } else {
+                    return $this->raiseError("No package file given");
+                }
+            }
+        }
+
+        // Download package -----------------------------------------------
+        if ($need_download) {
+            $downloaddir = $installer->config->get('download_dir');
+            if (empty($downloaddir)) {
+                if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+                    return $downloaddir;
+                }
+                $installer->log(2, '+ tmp dir created at ' . $downloaddir);
+            }
+            $callback = $this->ui ? array(&$installer, '_downloadCallback') : null;
+            $file = $installer->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+            if (PEAR::isError($file)) {
+                return $this->raiseError($file);
+            }
+            $pkgfile = $file;
+        }
+
+       // Parse xml file -----------------------------------------------
+        $pkginfo = $installer->infoFromTgzFile($pkgfile);
+        if (PEAR::isError($pkginfo)) {
+            return $this->raiseError($pkginfo);
+        }
+        $installer->validatePackageInfo($pkginfo, $errors, $warnings);
+        // XXX We allow warnings, do we have to do it?
+        if (count($errors)) {
+             if (empty($options['force'])) {
+                return $this->raiseError("The following errors where found:\n".
+                                                 implode("\n", $errors));
+            } else {
+                $this->log(0, "warning : the following errors were found:\n".
+                           implode("\n", $errors));
+            }
+        }
+        $pkgname = $pkginfo['package'];
+
+        // Unpacking -------------------------------------------------
+
+        if (isset($options['destination'])) {
+            if (!is_dir($options['destination'])) {
+                System::mkdir('-p ' . $options['destination']);
+            }
+            $dest = realpath($options['destination']);
+        } else {
+            $pwd = getcwd();
+            if (is_dir($pwd . DIRECTORY_SEPARATOR . 'ext')) {
+                $dest = $pwd . DIRECTORY_SEPARATOR . 'ext';
+            } else {
+                $dest = $pwd;
+            }
+        }
+        $dest .= DIRECTORY_SEPARATOR . $pkgname;
+        $orig = $pkgname . '-' . $pkginfo['version'];
+
+        $tar = new Archive_Tar($pkgfile);
+        if (!@$tar->extractModify($dest, $orig)) {
+            return $this->raiseError("unable to unpack $pkgfile");
+        }
+        $this->ui->outputData("Package ready at '$dest'");
+    // }}}
+    }
+
+    // }}}
+
+}
+?>
diff --git a/3rdparty/PEAR/Command/Mirror.php b/3rdparty/PEAR/Command/Mirror.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf56c3db640b806ea4b223f93c2a7f29514d3bd3
--- /dev/null
+++ b/3rdparty/PEAR/Command/Mirror.php
@@ -0,0 +1,101 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Alexander Merz <alexmerz@php.net>                            |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Mirror.php,v 1.5 2004/03/18 12:23:57 mj Exp $
+
+require_once "PEAR/Command/Common.php";
+require_once "PEAR/Command.php";
+require_once "PEAR/Remote.php";
+require_once "PEAR.php";
+
+/**
+ * PEAR commands for providing file mirrors
+ *
+ */
+class PEAR_Command_Mirror extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'download-all' => array(
+            'summary' => 'Downloads each available package from master_server',
+            'function' => 'doDownloadAll',
+            'shortcut' => 'da',
+            'options' => array(),
+            'doc' => '
+	    Requests a list of available packages from the package server
+	    (master_server) and downloads them to current working directory'
+            ),
+        );
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Mirror constructor.
+     *
+     * @access public
+     * @param object PEAR_Frontend a reference to an frontend
+     * @param object PEAR_Config a reference to the configuration data
+     */
+    function PEAR_Command_Mirror(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doDownloadAll()
+    /**
+    * retrieves a list of avaible Packages from master server
+    * and downloads them
+    *
+    * @access public
+    * @param string $command the command
+    * @param array $options the command options before the command
+    * @param array $params the stuff after the command name
+    * @return bool true if succesful
+    * @throw PEAR_Error 
+    */
+    function doDownloadAll($command, $options, $params)
+    {
+        $this->config->set("php_dir", "."); 
+        $remote = &new PEAR_Remote($this->config);
+        $remoteInfo = $remote->call("package.listAll");
+        if (PEAR::isError($remoteInfo)) {
+            return $remoteInfo;
+        }
+        $cmd = &PEAR_Command::factory("download", $this->config);
+        if (PEAR::isError($cmd)) {
+            return $cmd;
+        }
+        foreach ($remoteInfo as $pkgn => $pkg) {
+            /**
+             * Error handling not neccesary, because already done by 
+             * the download command
+             */
+            $cmd->run("download", array(), array($pkgn));
+        }
+
+        return true;
+    }
+
+    // }}}
+}
diff --git a/3rdparty/PEAR/Command/Package.php b/3rdparty/PEAR/Command/Package.php
new file mode 100644
index 0000000000000000000000000000000000000000..aca8711111802b46eb0aabee901c3596298f8460
--- /dev/null
+++ b/3rdparty/PEAR/Command/Package.php
@@ -0,0 +1,819 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net>                                   |
+// |          Martin Jansen <mj@php.net>                                  |
+// |          Greg Beaver <cellog@php.net>                                |
+// +----------------------------------------------------------------------+
+//
+// $Id: Package.php,v 1.61.2.7 2005/02/17 17:47:55 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Command/Common.php';
+
+class PEAR_Command_Package extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'package' => array(
+            'summary' => 'Build Package',
+            'function' => 'doPackage',
+            'shortcut' => 'p',
+            'options' => array(
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'Do not gzip the package file'
+                    ),
+                'showname' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Print the name of the packaged file.',
+                    ),
+                ),
+            'doc' => '[descfile]
+Creates a PEAR package from its description file (usually called
+package.xml).
+'
+            ),
+        'package-validate' => array(
+            'summary' => 'Validate Package Consistency',
+            'function' => 'doPackageValidate',
+            'shortcut' => 'pv',
+            'options' => array(),
+            'doc' => '
+',
+            ),
+        'cvsdiff' => array(
+            'summary' => 'Run a "cvs diff" for all files in a package',
+            'function' => 'doCvsDiff',
+            'shortcut' => 'cd',
+            'options' => array(
+                'quiet' => array(
+                    'shortopt' => 'q',
+                    'doc' => 'Be quiet',
+                    ),
+                'reallyquiet' => array(
+                    'shortopt' => 'Q',
+                    'doc' => 'Be really quiet',
+                    ),
+                'date' => array(
+                    'shortopt' => 'D',
+                    'doc' => 'Diff against revision of DATE',
+                    'arg' => 'DATE',
+                    ),
+                'release' => array(
+                    'shortopt' => 'R',
+                    'doc' => 'Diff against tag for package release REL',
+                    'arg' => 'REL',
+                    ),
+                'revision' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'Diff against revision REV',
+                    'arg' => 'REV',
+                    ),
+                'context' => array(
+                    'shortopt' => 'c',
+                    'doc' => 'Generate context diff',
+                    ),
+                'unified' => array(
+                    'shortopt' => 'u',
+                    'doc' => 'Generate unified diff',
+                    ),
+                'ignore-case' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'Ignore case, consider upper- and lower-case letters equivalent',
+                    ),
+                'ignore-whitespace' => array(
+                    'shortopt' => 'b',
+                    'doc' => 'Ignore changes in amount of white space',
+                    ),
+                'ignore-blank-lines' => array(
+                    'shortopt' => 'B',
+                    'doc' => 'Ignore changes that insert or delete blank lines',
+                    ),
+                'brief' => array(
+                    'doc' => 'Report only whether the files differ, no details',
+                    ),
+                'dry-run' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Don\'t do anything, just pretend',
+                    ),
+                ),
+            'doc' => '<package.xml>
+Compares all the files in a package.  Without any options, this
+command will compare the current code with the last checked-in code.
+Using the -r or -R option you may compare the current code with that
+of a specific release.
+',
+            ),
+        'cvstag' => array(
+            'summary' => 'Set CVS Release Tag',
+            'function' => 'doCvsTag',
+            'shortcut' => 'ct',
+            'options' => array(
+                'quiet' => array(
+                    'shortopt' => 'q',
+                    'doc' => 'Be quiet',
+                    ),
+                'reallyquiet' => array(
+                    'shortopt' => 'Q',
+                    'doc' => 'Be really quiet',
+                    ),
+                'slide' => array(
+                    'shortopt' => 'F',
+                    'doc' => 'Move (slide) tag if it exists',
+                    ),
+                'delete' => array(
+                    'shortopt' => 'd',
+                    'doc' => 'Remove tag',
+                    ),
+                'dry-run' => array(
+                    'shortopt' => 'n',
+                    'doc' => 'Don\'t do anything, just pretend',
+                    ),
+                ),
+            'doc' => '<package.xml>
+Sets a CVS tag on all files in a package.  Use this command after you have
+packaged a distribution tarball with the "package" command to tag what
+revisions of what files were in that release.  If need to fix something
+after running cvstag once, but before the tarball is released to the public,
+use the "slide" option to move the release tag.
+',
+            ),
+        'run-tests' => array(
+            'summary' => 'Run Regression Tests',
+            'function' => 'doRunTests',
+            'shortcut' => 'rt',
+            'options' => array(
+                'recur' => array(
+                    'shortopt' => 'r',
+                    'doc' => 'Run tests in child directories, recursively.  4 dirs deep maximum',
+                ),
+                'ini' => array(
+                    'shortopt' => 'i',
+                    'doc' => 'actual string of settings to pass to php in format " -d setting=blah"',
+                    'arg' => 'SETTINGS'
+                ),
+                'realtimelog' => array(
+                    'shortopt' => 'l',
+                    'doc' => 'Log test runs/results as they are run',
+                ),
+            ),
+            'doc' => '[testfile|dir ...]
+Run regression tests with PHP\'s regression testing script (run-tests.php).',
+            ),
+        'package-dependencies' => array(
+            'summary' => 'Show package dependencies',
+            'function' => 'doPackageDependencies',
+            'shortcut' => 'pd',
+            'options' => array(),
+            'doc' => '
+List all depencies the package has.'
+            ),
+        'sign' => array(
+            'summary' => 'Sign a package distribution file',
+            'function' => 'doSign',
+            'shortcut' => 'si',
+            'options' => array(),
+            'doc' => '<package-file>
+Signs a package distribution (.tar or .tgz) file with GnuPG.',
+            ),
+        'makerpm' => array(
+            'summary' => 'Builds an RPM spec file from a PEAR package',
+            'function' => 'doMakeRPM',
+            'shortcut' => 'rpm',
+            'options' => array(
+                'spec-template' => array(
+                    'shortopt' => 't',
+                    'arg' => 'FILE',
+                    'doc' => 'Use FILE as RPM spec file template'
+                    ),
+                'rpm-pkgname' => array(
+                    'shortopt' => 'p',
+                    'arg' => 'FORMAT',
+                    'doc' => 'Use FORMAT as format string for RPM package name, %s is replaced
+by the PEAR package name, defaults to "PEAR::%s".',
+                    ),
+                ),
+            'doc' => '<package-file>
+
+Creates an RPM .spec file for wrapping a PEAR package inside an RPM
+package.  Intended to be used from the SPECS directory, with the PEAR
+package tarball in the SOURCES directory:
+
+$ pear makerpm ../SOURCES/Net_Socket-1.0.tgz
+Wrote RPM spec file PEAR::Net_Geo-1.0.spec
+$ rpm -bb PEAR::Net_Socket-1.0.spec
+...
+Wrote: /usr/src/redhat/RPMS/i386/PEAR::Net_Socket-1.0-1.i386.rpm
+',
+            ),
+        );
+
+    var $output;
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Package constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Package(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ _displayValidationResults()
+
+    function _displayValidationResults($err, $warn, $strict = false)
+    {
+        foreach ($err as $e) {
+            $this->output .= "Error: $e\n";
+        }
+        foreach ($warn as $w) {
+            $this->output .= "Warning: $w\n";
+        }
+        $this->output .= sprintf('Validation: %d error(s), %d warning(s)'."\n",
+                                       sizeof($err), sizeof($warn));
+        if ($strict && sizeof($err) > 0) {
+            $this->output .= "Fix these errors and try again.";
+            return false;
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ doPackage()
+
+    function doPackage($command, $options, $params)
+    {
+        $this->output = '';
+        include_once 'PEAR/Packager.php';
+        if (sizeof($params) < 1) {
+            $params[0] = "package.xml";
+        }
+        $pkginfofile = isset($params[0]) ? $params[0] : 'package.xml';
+        $packager =& new PEAR_Packager();
+        $err = $warn = array();
+        $dir = dirname($pkginfofile);
+        $compress = empty($options['nocompress']) ? true : false;
+        $result = $packager->package($pkginfofile, $compress);
+        if (PEAR::isError($result)) {
+            $this->ui->outputData($this->output, $command);
+            return $this->raiseError($result);
+        }
+        // Don't want output, only the package file name just created
+        if (isset($options['showname'])) {
+            $this->output = $result;
+        }
+        if (PEAR::isError($result)) {
+            $this->output .= "Package failed: ".$result->getMessage();
+        }
+        $this->ui->outputData($this->output, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doPackageValidate()
+
+    function doPackageValidate($command, $options, $params)
+    {
+        $this->output = '';
+        if (sizeof($params) < 1) {
+            $params[0] = "package.xml";
+        }
+        $obj = new PEAR_Common;
+        $info = null;
+        if ($fp = @fopen($params[0], "r")) {
+            $test = fread($fp, 5);
+            fclose($fp);
+            if ($test == "<?xml") {
+                $info = $obj->infoFromDescriptionFile($params[0]);
+            }
+        }
+        if (empty($info)) {
+            $info = $obj->infoFromTgzFile($params[0]);
+        }
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+        $obj->validatePackageInfo($info, $err, $warn);
+        $this->_displayValidationResults($err, $warn);
+        $this->ui->outputData($this->output, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doCvsTag()
+
+    function doCvsTag($command, $options, $params)
+    {
+        $this->output = '';
+        $_cmd = $command;
+        if (sizeof($params) < 1) {
+            $help = $this->getHelp($command);
+            return $this->raiseError("$command: missing parameter: $help[0]");
+        }
+        $obj = new PEAR_Common;
+        $info = $obj->infoFromDescriptionFile($params[0]);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+        $err = $warn = array();
+        $obj->validatePackageInfo($info, $err, $warn);
+        if (!$this->_displayValidationResults($err, $warn, true)) {
+            $this->ui->outputData($this->output, $command);
+            break;
+        }
+        $version = $info['version'];
+        $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $version);
+        $cvstag = "RELEASE_$cvsversion";
+        $files = array_keys($info['filelist']);
+        $command = "cvs";
+        if (isset($options['quiet'])) {
+            $command .= ' -q';
+        }
+        if (isset($options['reallyquiet'])) {
+            $command .= ' -Q';
+        }
+        $command .= ' tag';
+        if (isset($options['slide'])) {
+            $command .= ' -F';
+        }
+        if (isset($options['delete'])) {
+            $command .= ' -d';
+        }
+        $command .= ' ' . $cvstag . ' ' . escapeshellarg($params[0]);
+        foreach ($files as $file) {
+            $command .= ' ' . escapeshellarg($file);
+        }
+        if ($this->config->get('verbose') > 1) {
+            $this->output .= "+ $command\n";
+        }
+        $this->output .= "+ $command\n";
+        if (empty($options['dry-run'])) {
+            $fp = popen($command, "r");
+            while ($line = fgets($fp, 1024)) {
+                $this->output .= rtrim($line)."\n";
+            }
+            pclose($fp);
+        }
+        $this->ui->outputData($this->output, $_cmd);
+        return true;
+    }
+
+    // }}}
+    // {{{ doCvsDiff()
+
+    function doCvsDiff($command, $options, $params)
+    {
+        $this->output = '';
+        if (sizeof($params) < 1) {
+            $help = $this->getHelp($command);
+            return $this->raiseError("$command: missing parameter: $help[0]");
+        }
+        $obj = new PEAR_Common;
+        $info = $obj->infoFromDescriptionFile($params[0]);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+        $files = array_keys($info['filelist']);
+        $cmd = "cvs";
+        if (isset($options['quiet'])) {
+            $cmd .= ' -q';
+            unset($options['quiet']);
+        }
+        if (isset($options['reallyquiet'])) {
+            $cmd .= ' -Q';
+            unset($options['reallyquiet']);
+        }
+        if (isset($options['release'])) {
+            $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $options['release']);
+            $cvstag = "RELEASE_$cvsversion";
+            $options['revision'] = $cvstag;
+            unset($options['release']);
+        }
+        $execute = true;
+        if (isset($options['dry-run'])) {
+            $execute = false;
+            unset($options['dry-run']);
+        }
+        $cmd .= ' diff';
+        // the rest of the options are passed right on to "cvs diff"
+        foreach ($options as $option => $optarg) {
+            $arg = @$this->commands[$command]['options'][$option]['arg'];
+            $short = @$this->commands[$command]['options'][$option]['shortopt'];
+            $cmd .= $short ? " -$short" : " --$option";
+            if ($arg && $optarg) {
+                $cmd .= ($short ? '' : '=') . escapeshellarg($optarg);
+            }
+        }
+        foreach ($files as $file) {
+            $cmd .= ' ' . escapeshellarg($file);
+        }
+        if ($this->config->get('verbose') > 1) {
+            $this->output .= "+ $cmd\n";
+        }
+        if ($execute) {
+            $fp = popen($cmd, "r");
+            while ($line = fgets($fp, 1024)) {
+                $this->output .= rtrim($line)."\n";
+            }
+            pclose($fp);
+        }
+        $this->ui->outputData($this->output, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doRunTests()
+
+    function doRunTests($command, $options, $params)
+    {
+        include_once 'PEAR/RunTest.php';
+        $log = new PEAR_Common;
+        $log->ui = &$this->ui; // slightly hacky, but it will work
+        $run = new PEAR_RunTest($log);
+        $tests = array();
+        if (isset($options['recur'])) {
+            $depth = 4;
+        } else {
+            $depth = 1;
+        }
+        if (!count($params)) {
+            $params[] = '.';
+        }
+        foreach ($params as $p) {
+            if (is_dir($p)) {
+                $dir = System::find(array($p, '-type', 'f',
+                                            '-maxdepth', $depth,
+                                            '-name', '*.phpt'));
+                $tests = array_merge($tests, $dir);
+            } else {
+                if (!@file_exists($p)) {
+                    if (!preg_match('/\.phpt$/', $p)) {
+                        $p .= '.phpt';
+                    }
+                    $dir = System::find(array(dirname($p), '-type', 'f',
+                                                '-maxdepth', $depth,
+                                                '-name', $p));
+                    $tests = array_merge($tests, $dir);
+                } else {
+                    $tests[] = $p;
+                }
+            }
+        }
+        $ini_settings = '';
+        if (isset($options['ini'])) {
+            $ini_settings .= $options['ini'];
+        }
+        if (isset($_ENV['TEST_PHP_INCLUDE_PATH'])) {
+            $ini_settings .= " -d include_path={$_ENV['TEST_PHP_INCLUDE_PATH']}";
+        }
+        if ($ini_settings) {
+            $this->ui->outputData('Using INI settings: "' . $ini_settings . '"');
+        }
+        $skipped = $passed = $failed = array();
+        $this->ui->outputData('Running ' . count($tests) . ' tests', $command);
+        $start = time();
+        if (isset($options['realtimelog'])) {
+            @unlink('run-tests.log');
+        }
+        foreach ($tests as $t) {
+            if (isset($options['realtimelog'])) {
+                $fp = @fopen('run-tests.log', 'a');
+                if ($fp) {
+                    fwrite($fp, "Running test $t...");
+                    fclose($fp);
+                }
+            }
+            $result = $run->run($t, $ini_settings);
+            if (OS_WINDOWS) {
+                for($i=0;$i<2000;$i++) {
+                    $i = $i; // delay - race conditions on windows
+                }
+            }
+            if (isset($options['realtimelog'])) {
+                $fp = @fopen('run-tests.log', 'a');
+                if ($fp) {
+                    fwrite($fp, "$result\n");
+                    fclose($fp);
+                }
+            }
+            if ($result == 'FAILED') {
+            	$failed[] = $t;
+            }
+            if ($result == 'PASSED') {
+            	$passed[] = $t;
+            }
+            if ($result == 'SKIPPED') {
+            	$skipped[] = $t;
+            }
+        }
+        $total = date('i:s', time() - $start);
+        if (count($failed)) {
+            $output = "TOTAL TIME: $total\n";
+            $output .= count($passed) . " PASSED TESTS\n";
+            $output .= count($skipped) . " SKIPPED TESTS\n";
+    		$output .= count($failed) . " FAILED TESTS:\n";
+        	foreach ($failed as $failure) {
+        		$output .= $failure . "\n";
+        	}
+            if (isset($options['realtimelog'])) {
+                $fp = @fopen('run-tests.log', 'a');
+            } else {
+                $fp = @fopen('run-tests.log', 'w');
+            }
+            if ($fp) {
+                fwrite($fp, $output, strlen($output));
+                fclose($fp);
+                $this->ui->outputData('wrote log to "' . realpath('run-tests.log') . '"', $command);
+            }
+        } elseif (@file_exists('run-tests.log') && !@is_dir('run-tests.log')) {
+            @unlink('run-tests.log');
+        }
+        $this->ui->outputData('TOTAL TIME: ' . $total);
+        $this->ui->outputData(count($passed) . ' PASSED TESTS', $command);
+        $this->ui->outputData(count($skipped) . ' SKIPPED TESTS', $command);
+        if (count($failed)) {
+    		$this->ui->outputData(count($failed) . ' FAILED TESTS:', $command);
+        	foreach ($failed as $failure) {
+        		$this->ui->outputData($failure, $command);
+        	}
+        }
+
+        return true;
+    }
+
+    // }}}
+    // {{{ doPackageDependencies()
+
+    function doPackageDependencies($command, $options, $params)
+    {
+        // $params[0] -> the PEAR package to list its information
+        if (sizeof($params) != 1) {
+            return $this->raiseError("bad parameter(s), try \"help $command\"");
+        }
+
+        $obj = new PEAR_Common();
+        if (PEAR::isError($info = $obj->infoFromAny($params[0]))) {
+            return $this->raiseError($info);
+        }
+
+        if (is_array($info['release_deps'])) {
+            $data = array(
+                'caption' => 'Dependencies for ' . $info['package'],
+                'border' => true,
+                'headline' => array("Type", "Name", "Relation", "Version"),
+                );
+
+            foreach ($info['release_deps'] as $d) {
+
+                if (isset($this->_deps_rel_trans[$d['rel']])) {
+                    $rel = $this->_deps_rel_trans[$d['rel']];
+                } else {
+                    $rel = $d['rel'];
+                }
+
+                if (isset($this->_deps_type_trans[$d['type']])) {
+                    $type = ucfirst($this->_deps_type_trans[$d['type']]);
+                } else {
+                    $type = $d['type'];
+                }
+
+                if (isset($d['name'])) {
+                    $name = $d['name'];
+                } else {
+                    $name = '';
+                }
+
+                if (isset($d['version'])) {
+                    $version = $d['version'];
+                } else {
+                    $version = '';
+                }
+
+                $data['data'][] = array($type, $name, $rel, $version);
+            }
+
+            $this->ui->outputData($data, $command);
+            return true;
+        }
+
+        // Fallback
+        $this->ui->outputData("This package does not have any dependencies.", $command);
+    }
+
+    // }}}
+    // {{{ doSign()
+
+    function doSign($command, $options, $params)
+    {
+        // should move most of this code into PEAR_Packager
+        // so it'll be easy to implement "pear package --sign"
+        if (sizeof($params) != 1) {
+            return $this->raiseError("bad parameter(s), try \"help $command\"");
+        }
+        if (!file_exists($params[0])) {
+            return $this->raiseError("file does not exist: $params[0]");
+        }
+        $obj = new PEAR_Common;
+        $info = $obj->infoFromTgzFile($params[0]);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+        include_once "Archive/Tar.php";
+        include_once "System.php";
+        $tar = new Archive_Tar($params[0]);
+        $tmpdir = System::mktemp('-d pearsign');
+        if (!$tar->extractList('package.xml package.sig', $tmpdir)) {
+            return $this->raiseError("failed to extract tar file");
+        }
+        if (file_exists("$tmpdir/package.sig")) {
+            return $this->raiseError("package already signed");
+        }
+        @unlink("$tmpdir/package.sig");
+        $input = $this->ui->userDialog($command,
+                                       array('GnuPG Passphrase'),
+                                       array('password'));
+        $gpg = popen("gpg --batch --passphrase-fd 0 --armor --detach-sign --output $tmpdir/package.sig $tmpdir/package.xml 2>/dev/null", "w");
+        if (!$gpg) {
+            return $this->raiseError("gpg command failed");
+        }
+        fwrite($gpg, "$input[0]\r");
+        if (pclose($gpg) || !file_exists("$tmpdir/package.sig")) {
+            return $this->raiseError("gpg sign failed");
+        }
+        $tar->addModify("$tmpdir/package.sig", '', $tmpdir);
+        return true;
+    }
+
+    // }}}
+    // {{{ doMakeRPM()
+
+    /*
+
+    (cox)
+
+    TODO:
+
+        - Fill the rpm dependencies in the template file.
+
+    IDEAS:
+
+        - Instead of mapping the role to rpm vars, perhaps it's better
+
+          to use directly the pear cmd to install the files by itself
+
+          in %postrun so:
+
+          pear -d php_dir=%{_libdir}/php/pear -d test_dir=.. <package>
+
+    */
+
+    function doMakeRPM($command, $options, $params)
+    {
+        if (sizeof($params) != 1) {
+            return $this->raiseError("bad parameter(s), try \"help $command\"");
+        }
+        if (!file_exists($params[0])) {
+            return $this->raiseError("file does not exist: $params[0]");
+        }
+        include_once "Archive/Tar.php";
+        include_once "PEAR/Installer.php";
+        include_once "System.php";
+        $tar = new Archive_Tar($params[0]);
+        $tmpdir = System::mktemp('-d pear2rpm');
+        $instroot = System::mktemp('-d pear2rpm');
+        $tmp = $this->config->get('verbose');
+        $this->config->set('verbose', 0);
+        $installer = new PEAR_Installer($this->ui);
+        $info = $installer->install($params[0],
+                                    array('installroot' => $instroot,
+                                          'nodeps' => true));
+        $pkgdir = "$info[package]-$info[version]";
+        $info['rpm_xml_dir'] = '/var/lib/pear';
+        $this->config->set('verbose', $tmp);
+        if (!$tar->extractList("package.xml", $tmpdir, $pkgdir)) {
+            return $this->raiseError("failed to extract $params[0]");
+        }
+        if (!file_exists("$tmpdir/package.xml")) {
+            return $this->raiseError("no package.xml found in $params[0]");
+        }
+        if (isset($options['spec-template'])) {
+            $spec_template = $options['spec-template'];
+        } else {
+            $spec_template = $this->config->get('data_dir') .
+                '/PEAR/template.spec';
+        }
+        if (isset($options['rpm-pkgname'])) {
+            $rpm_pkgname_format = $options['rpm-pkgname'];
+        } else {
+            $rpm_pkgname_format = "PEAR::%s";
+        }
+
+        $info['extra_headers'] = '';
+        $info['doc_files'] = '';
+        $info['files'] = '';
+        $info['rpm_package'] = sprintf($rpm_pkgname_format, $info['package']);
+        $srcfiles = 0;
+        foreach ($info['filelist'] as $name => $attr) {
+
+            if (!isset($attr['role'])) {
+                continue;
+            }
+            $name = preg_replace('![/:\\\\]!', '/', $name);
+            if ($attr['role'] == 'doc') {
+                $info['doc_files'] .= " $name";
+
+            // Map role to the rpm vars
+            } else {
+
+                $c_prefix = '%{_libdir}/php/pear';
+
+                switch ($attr['role']) {
+
+                    case 'php':
+
+                        $prefix = $c_prefix; break;
+
+                    case 'ext':
+
+                        $prefix = '%{_libdir}/php'; break; // XXX good place?
+
+                    case 'src':
+
+                        $srcfiles++;
+
+                        $prefix = '%{_includedir}/php'; break; // XXX good place?
+
+                    case 'test':
+
+                        $prefix = "$c_prefix/tests/" . $info['package']; break;
+
+                    case 'data':
+
+                        $prefix = "$c_prefix/data/" . $info['package']; break;
+
+                    case 'script':
+
+                        $prefix = '%{_bindir}'; break;
+
+                }
+
+                $name = str_replace('\\', '/', $name);
+                $info['files'] .= "$prefix/$name\n";
+
+            }
+        }
+        if ($srcfiles > 0) {
+            include_once "OS/Guess.php";
+            $os = new OS_Guess;
+            $arch = $os->getCpu();
+        } else {
+            $arch = 'noarch';
+        }
+        $cfg = array('master_server', 'php_dir', 'ext_dir', 'doc_dir',
+                     'bin_dir', 'data_dir', 'test_dir');
+        foreach ($cfg as $k) {
+            $info[$k] = $this->config->get($k);
+        }
+        $info['arch'] = $arch;
+        $fp = @fopen($spec_template, "r");
+        if (!$fp) {
+            return $this->raiseError("could not open RPM spec file template $spec_template: $php_errormsg");
+        }
+        $spec_contents = preg_replace('/@([a-z0-9_-]+)@/e', '$info["\1"]', fread($fp, filesize($spec_template)));
+        fclose($fp);
+        $spec_file = "$info[rpm_package]-$info[version].spec";
+        $wp = fopen($spec_file, "wb");
+        if (!$wp) {
+            return $this->raiseError("could not write RPM spec file $spec_file: $php_errormsg");
+        }
+        fwrite($wp, $spec_contents);
+        fclose($wp);
+        $this->ui->outputData("Wrote RPM spec file $spec_file", $command);
+
+        return true;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Command/Registry.php b/3rdparty/PEAR/Command/Registry.php
new file mode 100644
index 0000000000000000000000000000000000000000..62cb4b00e28947cd0c3f811387dd69bd99a72218
--- /dev/null
+++ b/3rdparty/PEAR/Command/Registry.php
@@ -0,0 +1,351 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Registry.php,v 1.36 2004/01/08 17:33:13 sniper Exp $
+
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Config.php';
+
+class PEAR_Command_Registry extends PEAR_Command_Common
+{
+    // {{{ properties
+
+    var $commands = array(
+        'list' => array(
+            'summary' => 'List Installed Packages',
+            'function' => 'doList',
+            'shortcut' => 'l',
+            'options' => array(),
+            'doc' => '[package]
+If invoked without parameters, this command lists the PEAR packages
+installed in your php_dir ({config php_dir)).  With a parameter, it
+lists the files in that package.
+',
+            ),
+        'shell-test' => array(
+            'summary' => 'Shell Script Test',
+            'function' => 'doShellTest',
+            'shortcut' => 'st',
+            'options' => array(),
+            'doc' => '<package> [[relation] version]
+Tests if a package is installed in the system. Will exit(1) if it is not.
+   <relation>   The version comparison operator. One of:
+                <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne
+   <version>    The version to compare with
+'),
+        'info' => array(
+            'summary'  => 'Display information about a package',
+            'function' => 'doInfo',
+            'shortcut' => 'in',
+            'options'  => array(),
+            'doc'      => '<package>
+Displays information about a package. The package argument may be a
+local package file, an URL to a package file, or the name of an
+installed package.'
+            )
+        );
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Registry constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Registry(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doList()
+
+    function _sortinfo($a, $b)
+    {
+        return strcmp($a['package'], $b['package']);
+    }
+
+    function doList($command, $options, $params)
+    {
+        $reg = new PEAR_Registry($this->config->get('php_dir'));
+        if (sizeof($params) == 0) {
+            $installed = $reg->packageInfo();
+            usort($installed, array(&$this, '_sortinfo'));
+            $i = $j = 0;
+            $data = array(
+                'caption' => 'Installed packages:',
+                'border' => true,
+                'headline' => array('Package', 'Version', 'State')
+                );
+            foreach ($installed as $package) {
+                $data['data'][] = array($package['package'],
+                                          $package['version'],
+                                          @$package['release_state']);
+            }
+            if (count($installed)==0) {
+                $data = '(no packages installed)';
+            }
+            $this->ui->outputData($data, $command);
+        } else {
+            if (file_exists($params[0]) && !is_dir($params[0])) {
+                include_once "PEAR/Common.php";
+                $obj = &new PEAR_Common;
+                $info = $obj->infoFromAny($params[0]);
+                $headings = array('Package File', 'Install Path');
+                $installed = false;
+            } else {
+                $info = $reg->packageInfo($params[0]);
+                $headings = array('Type', 'Install Path');
+                $installed = true;
+            }
+            if (PEAR::isError($info)) {
+                return $this->raiseError($info);
+            }
+            if ($info === null) {
+                return $this->raiseError("`$params[0]' not installed");
+            }
+            $list = $info['filelist'];
+            if ($installed) {
+                $caption = 'Installed Files For ' . $params[0];
+            } else {
+                $caption = 'Contents of ' . basename($params[0]);
+            }
+            $data = array(
+                'caption' => $caption,
+                'border' => true,
+                'headline' => $headings);
+            foreach ($list as $file => $att) {
+                if ($installed) {
+                    if (empty($att['installed_as'])) {
+                        continue;
+                    }
+                    $data['data'][] = array($att['role'], $att['installed_as']);
+                } else {
+                    if (isset($att['baseinstalldir'])) {
+                        $dest = $att['baseinstalldir'] . DIRECTORY_SEPARATOR .
+                            $file;
+                    } else {
+                        $dest = $file;
+                    }
+                    switch ($att['role']) {
+                        case 'test':
+                        case 'data':
+                            if ($installed) {
+                                break 2;
+                            }
+                            $dest = '-- will not be installed --';
+                            break;
+                        case 'doc':
+                            $dest = $this->config->get('doc_dir') . DIRECTORY_SEPARATOR .
+                                $dest;
+                            break;
+                        case 'php':
+                        default:
+                            $dest = $this->config->get('php_dir') . DIRECTORY_SEPARATOR .
+                                $dest;
+                    }
+                    $dest = preg_replace('!/+!', '/', $dest);
+                    $file = preg_replace('!/+!', '/', $file);
+                    $data['data'][] = array($file, $dest);
+                }
+            }
+            $this->ui->outputData($data, $command);
+
+
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ doShellTest()
+
+    function doShellTest($command, $options, $params)
+    {
+        $this->pushErrorHandling(PEAR_ERROR_RETURN);
+        $reg = &new PEAR_Registry($this->config->get('php_dir'));
+        // "pear shell-test Foo"
+        if (sizeof($params) == 1) {
+            if (!$reg->packageExists($params[0])) {
+                exit(1);
+            }
+            // "pear shell-test Foo 1.0"
+        } elseif (sizeof($params) == 2) {
+            $v = $reg->packageInfo($params[0], 'version');
+            if (!$v || !version_compare("$v", "{$params[1]}", "ge")) {
+                exit(1);
+            }
+            // "pear shell-test Foo ge 1.0"
+        } elseif (sizeof($params) == 3) {
+            $v = $reg->packageInfo($params[0], 'version');
+            if (!$v || !version_compare("$v", "{$params[2]}", $params[1])) {
+                exit(1);
+            }
+        } else {
+            $this->popErrorHandling();
+            $this->raiseError("$command: expects 1 to 3 parameters");
+            exit(1);
+        }
+    }
+
+    // }}}
+    // {{{ doInfo
+
+    function doInfo($command, $options, $params)
+    {
+        // $params[0] The package for showing info
+        if (sizeof($params) != 1) {
+            return $this->raiseError("This command only accepts one param: ".
+                                     "the package you want information");
+        }
+        if (@is_file($params[0])) {
+            $obj  = &new PEAR_Common();
+            $info = $obj->infoFromAny($params[0]);
+        } else {
+            $reg = &new PEAR_Registry($this->config->get('php_dir'));
+            $info = $reg->packageInfo($params[0]);
+        }
+        if (PEAR::isError($info)) {
+            return $info;
+        }
+        if (empty($info)) {
+            $this->raiseError("Nothing found for `$params[0]'");
+            return;
+        }
+        unset($info['filelist']);
+        unset($info['changelog']);
+        $keys = array_keys($info);
+        $longtext = array('description', 'summary');
+        foreach ($keys as $key) {
+            if (is_array($info[$key])) {
+                switch ($key) {
+                    case 'maintainers': {
+                        $i = 0;
+                        $mstr = '';
+                        foreach ($info[$key] as $m) {
+                            if ($i++ > 0) {
+                                $mstr .= "\n";
+                            }
+                            $mstr .= $m['name'] . " <";
+                            if (isset($m['email'])) {
+                                $mstr .= $m['email'];
+                            } else {
+                                $mstr .= $m['handle'] . '@php.net';
+                            }
+                            $mstr .= "> ($m[role])";
+                        }
+                        $info[$key] = $mstr;
+                        break;
+                    }
+                    case 'release_deps': {
+                        $i = 0;
+                        $dstr = '';
+                        foreach ($info[$key] as $d) {
+                            if (isset($this->_deps_rel_trans[$d['rel']])) {
+                                $rel = $this->_deps_rel_trans[$d['rel']];
+                            } else {
+                                $rel = $d['rel'];
+                            }
+                            if (isset($this->_deps_type_trans[$d['type']])) {
+                                $type = ucfirst($this->_deps_type_trans[$d['type']]);
+                            } else {
+                                $type = $d['type'];
+                            }
+                            if (isset($d['name'])) {
+                                $name = $d['name'] . ' ';
+                            } else {
+                                $name = '';
+                            }
+                            if (isset($d['version'])) {
+                                $version = $d['version'] . ' ';
+                            } else {
+                                $version = '';
+                            }
+                            $dstr .= "$type $name$rel $version\n";
+                        }
+                        $info[$key] = $dstr;
+                        break;
+                    }
+                    case 'provides' : {
+                        $debug = $this->config->get('verbose');
+                        if ($debug < 2) {
+                            $pstr = 'Classes: ';
+                        } else {
+                            $pstr = '';
+                        }
+                        $i = 0;
+                        foreach ($info[$key] as $p) {
+                            if ($debug < 2 && $p['type'] != "class") {
+                                continue;
+                            }
+                            // Only print classes when verbosity mode is < 2
+                            if ($debug < 2) {
+                                if ($i++ > 0) {
+                                    $pstr .= ", ";
+                                }
+                                $pstr .= $p['name'];
+                            } else {
+                                if ($i++ > 0) {
+                                    $pstr .= "\n";
+                                }
+                                $pstr .= ucfirst($p['type']) . " " . $p['name'];
+                                if (isset($p['explicit']) && $p['explicit'] == 1) {
+                                    $pstr .= " (explicit)";
+                                }
+                            }
+                        }
+                        $info[$key] = $pstr;
+                        break;
+                    }
+                    default: {
+                        $info[$key] = implode(", ", $info[$key]);
+                        break;
+                    }
+                }
+            }
+            if ($key == '_lastmodified') {
+                $hdate = date('Y-m-d', $info[$key]);
+                unset($info[$key]);
+                $info['Last Modified'] = $hdate;
+            } else {
+                $info[$key] = trim($info[$key]);
+                if (in_array($key, $longtext)) {
+                    $info[$key] = preg_replace('/  +/', ' ', $info[$key]);
+                }
+            }
+        }
+        $caption = 'About ' . $info['package'] . '-' . $info['version'];
+        $data = array(
+            'caption' => $caption,
+            'border' => true);
+        foreach ($info as $key => $value) {
+            $key = ucwords(trim(str_replace('_', ' ', $key)));
+            $data['data'][] = array($key, $value);
+        }
+        $data['raw'] = $info;
+
+        $this->ui->outputData($data, 'package-info');
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Command/Remote.php b/3rdparty/PEAR/Command/Remote.php
new file mode 100644
index 0000000000000000000000000000000000000000..bbd16093f5d83876aba5686e4b74ab54d01e515f
--- /dev/null
+++ b/3rdparty/PEAR/Command/Remote.php
@@ -0,0 +1,435 @@
+<?php
+// /* vim: set expandtab tabstop=4 shiftwidth=4: */
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Remote.php,v 1.39 2004/04/03 15:56:00 cellog Exp $
+
+require_once 'PEAR/Command/Common.php';
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Remote.php';
+require_once 'PEAR/Registry.php';
+
+class PEAR_Command_Remote extends PEAR_Command_Common
+{
+    // {{{ command definitions
+
+    var $commands = array(
+        'remote-info' => array(
+            'summary' => 'Information About Remote Packages',
+            'function' => 'doRemoteInfo',
+            'shortcut' => 'ri',
+            'options' => array(),
+            'doc' => '<package>
+Get details on a package from the server.',
+            ),
+        'list-upgrades' => array(
+            'summary' => 'List Available Upgrades',
+            'function' => 'doListUpgrades',
+            'shortcut' => 'lu',
+            'options' => array(),
+            'doc' => '
+List releases on the server of packages you have installed where
+a newer version is available with the same release state (stable etc.).'
+            ),
+        'remote-list' => array(
+            'summary' => 'List Remote Packages',
+            'function' => 'doRemoteList',
+            'shortcut' => 'rl',
+            'options' => array(),
+            'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+            ),
+        'search' => array(
+            'summary' => 'Search remote package database',
+            'function' => 'doSearch',
+            'shortcut' => 'sp',
+            'options' => array(),
+            'doc' => '
+Lists all packages which match the search parameters (first param
+is package name, second package info)',
+            ),
+        'list-all' => array(
+            'summary' => 'List All Packages',
+            'function' => 'doListAll',
+            'shortcut' => 'la',
+            'options' => array(),
+            'doc' => '
+Lists the packages available on the configured server along with the
+latest stable release of each package.',
+            ),
+        'download' => array(
+            'summary' => 'Download Package',
+            'function' => 'doDownload',
+            'shortcut' => 'd',
+            'options' => array(
+                'nocompress' => array(
+                    'shortopt' => 'Z',
+                    'doc' => 'download an uncompressed (.tar) file',
+                    ),
+                ),
+            'doc' => '{package|package-version}
+Download a package tarball.  The file will be named as suggested by the
+server, for example if you download the DB package and the latest stable
+version of DB is 1.2, the downloaded file will be DB-1.2.tgz.',
+            ),
+        'clear-cache' => array(
+            'summary' => 'Clear XML-RPC Cache',
+            'function' => 'doClearCache',
+            'shortcut' => 'cc',
+            'options' => array(),
+            'doc' => '
+Clear the XML-RPC cache.  See also the cache_ttl configuration
+parameter.
+',
+            ),
+        );
+
+    // }}}
+    // {{{ constructor
+
+    /**
+     * PEAR_Command_Remote constructor.
+     *
+     * @access public
+     */
+    function PEAR_Command_Remote(&$ui, &$config)
+    {
+        parent::PEAR_Command_Common($ui, $config);
+    }
+
+    // }}}
+
+    // {{{ doRemoteInfo()
+
+    function doRemoteInfo($command, $options, $params)
+    {
+        if (sizeof($params) != 1) {
+            return $this->raiseError("$command expects one param: the remote package name");
+        }
+        $r = new PEAR_Remote($this->config);
+        $info = $r->call('package.info', $params[0]);
+        if (PEAR::isError($info)) {
+            return $this->raiseError($info);
+        }
+
+        $reg = new PEAR_Registry($this->config->get('php_dir'));
+        $installed = $reg->packageInfo($info['name']);
+        $info['installed'] = $installed['version'] ? $installed['version'] : '- no -';
+
+        $this->ui->outputData($info, $command);
+
+        return true;
+    }
+
+    // }}}
+    // {{{ doRemoteList()
+
+    function doRemoteList($command, $options, $params)
+    {
+        $r = new PEAR_Remote($this->config);
+        $list_options = false;
+        if ($this->config->get('preferred_state') == 'stable')
+            $list_options = true;
+        $available = $r->call('package.listAll', $list_options);
+        if (PEAR::isError($available)) {
+            return $this->raiseError($available);
+        }
+        $i = $j = 0;
+        $data = array(
+            'caption' => 'Available packages:',
+            'border' => true,
+            'headline' => array('Package', 'Version'),
+            );
+        foreach ($available as $name => $info) {
+            $data['data'][] = array($name, isset($info['stable']) ? $info['stable'] : '-n/a-');
+        }
+        if (count($available)==0) {
+            $data = '(no packages installed yet)';
+        }
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doListAll()
+
+    function doListAll($command, $options, $params)
+    {
+        $r = new PEAR_Remote($this->config);
+        $reg = new PEAR_Registry($this->config->get('php_dir'));
+        $list_options = false;
+        if ($this->config->get('preferred_state') == 'stable')
+            $list_options = true;
+        $available = $r->call('package.listAll', $list_options);
+        if (PEAR::isError($available)) {
+            return $this->raiseError($available);
+        }
+        if (!is_array($available)) {
+            return $this->raiseError('The package list could not be fetched from the remote server. Please try again. (Debug info: "'.$available.'")');
+        }
+        $data = array(
+            'caption' => 'All packages:',
+            'border' => true,
+            'headline' => array('Package', 'Latest', 'Local'),
+            );
+        $local_pkgs = $reg->listPackages();
+        
+        foreach ($available as $name => $info) {
+            $installed = $reg->packageInfo($name);
+            $desc = $info['summary'];
+            if (isset($params[$name]))
+                $desc .= "\n\n".$info['description'];
+
+            if (isset($options['mode']))
+            {
+                if ($options['mode'] == 'installed' && !isset($installed['version']))
+                    continue;
+                if ($options['mode'] == 'notinstalled' && isset($installed['version']))
+                    continue;
+                if ($options['mode'] == 'upgrades'
+                    && (!isset($installed['version']) || $installed['version'] == $info['stable']))
+                {
+                    continue;
+                }
+            }
+            $pos = array_search(strtolower($name), $local_pkgs);
+            if ($pos !== false) {
+                unset($local_pkgs[$pos]);
+            }
+
+            $data['data'][$info['category']][] = array(
+                $name,
+                @$info['stable'],
+                @$installed['version'],
+                @$desc,
+                @$info['deps'],
+                );
+        }
+        
+        foreach ($local_pkgs as $name) {
+            $info = $reg->packageInfo($name);
+            $data['data']['Local'][] = array(
+                $info['package'], 
+                '',
+                $info['version'],
+                $info['summary'],
+                @$info['release_deps']
+                );
+        }
+
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doSearch()
+
+    function doSearch($command, $options, $params)
+    {
+        if ((!isset($params[0]) || empty($params[0]))
+            && (!isset($params[1]) || empty($params[1])))
+        {
+            return $this->raiseError('no valid search string supplied');
+        };
+
+        $r = new PEAR_Remote($this->config);
+        $reg = new PEAR_Registry($this->config->get('php_dir'));
+        $available = $r->call('package.listAll', true, false);
+        if (PEAR::isError($available)) {
+            return $this->raiseError($available);
+        }
+        $data = array(
+            'caption' => 'Matched packages:',
+            'border' => true,
+            'headline' => array('Package', 'Stable/(Latest)', 'Local'),
+            );
+
+        foreach ($available as $name => $info) {
+            $found = (!empty($params[0]) && stristr($name, $params[0]) !== false);
+            if (!$found && !(isset($params[1]) && !empty($params[1])
+                && (stristr($info['summary'], $params[1]) !== false
+                    || stristr($info['description'], $params[1]) !== false)))
+            {
+                continue;
+            };
+
+            $installed = $reg->packageInfo($name);
+            $desc = $info['summary'];
+            if (isset($params[$name]))
+                $desc .= "\n\n".$info['description'];
+
+            $unstable = '';
+            if ($info['unstable']) {
+                $unstable = '/(' . $info['unstable'] . $info['state'] . ')';
+            }
+            if (!isset($info['stable']) || !$info['stable']) {
+                $info['stable'] = 'none';
+            }
+            $data['data'][$info['category']][] = array(
+                $name,
+                $info['stable'] . $unstable,
+                $installed['version'],
+                $desc,
+                );
+        }
+        if (!isset($data['data'])) {
+            return $this->raiseError('no packages found');
+        }
+        $this->ui->outputData($data, $command);
+        return true;
+    }
+
+    // }}}
+    // {{{ doDownload()
+
+    function doDownload($command, $options, $params)
+    {
+        //$params[0] -> The package to download
+        if (count($params) != 1) {
+            return PEAR::raiseError("download expects one argument: the package to download");
+        }
+        $server = $this->config->get('master_server');
+        if (!ereg('^http://', $params[0])) {
+            $getoption = isset($options['nocompress'])&&$options['nocompress']==1?'?uncompress=on':'';
+            $pkgfile = "http://$server/get/$params[0]".$getoption;
+        } else {
+            $pkgfile = $params[0];
+        }
+        $this->bytes_downloaded = 0;
+        $saved = PEAR_Common::downloadHttp($pkgfile, $this->ui, '.',
+                                           array(&$this, 'downloadCallback'));
+        if (PEAR::isError($saved)) {
+            return $this->raiseError($saved);
+        }
+        $fname = basename($saved);
+        $this->ui->outputData("File $fname downloaded ($this->bytes_downloaded bytes)", $command);
+        return true;
+    }
+
+    function downloadCallback($msg, $params = null)
+    {
+        if ($msg == 'done') {
+            $this->bytes_downloaded = $params;
+        }
+    }
+
+    // }}}
+    // {{{ doListUpgrades()
+
+    function doListUpgrades($command, $options, $params)
+    {
+        include_once "PEAR/Registry.php";
+        $remote = new PEAR_Remote($this->config);
+        if (empty($params[0])) {
+            $state = $this->config->get('preferred_state');
+        } else {
+            $state = $params[0];
+        }
+        $caption = 'Available Upgrades';
+        if (empty($state) || $state == 'any') {
+            $latest = $remote->call("package.listLatestReleases");
+        } else {
+            $latest = $remote->call("package.listLatestReleases", $state);
+            $caption .= ' (' . implode(', ', PEAR_Common::betterStates($state, true)) . ')';
+        }
+        $caption .= ':';
+        if (PEAR::isError($latest)) {
+            return $latest;
+        }
+        $reg = new PEAR_Registry($this->config->get('php_dir'));
+        $inst = array_flip($reg->listPackages());
+        $data = array(
+            'caption' => $caption,
+            'border' => 1,
+            'headline' => array('Package', 'Local', 'Remote', 'Size'),
+            );
+        foreach ((array)$latest as $pkg => $info) {
+            $package = strtolower($pkg);
+            if (!isset($inst[$package])) {
+                // skip packages we don't have installed
+                continue;
+            }
+            extract($info);
+            $pkginfo = $reg->packageInfo($package);
+            $inst_version = $pkginfo['version'];
+            $inst_state   = $pkginfo['release_state'];
+            if (version_compare("$version", "$inst_version", "le")) {
+                // installed version is up-to-date
+                continue;
+            }
+            if ($filesize >= 20480) {
+                $filesize += 1024 - ($filesize % 1024);
+                $fs = sprintf("%dkB", $filesize / 1024);
+            } elseif ($filesize > 0) {
+                $filesize += 103 - ($filesize % 103);
+                $fs = sprintf("%.1fkB", $filesize / 1024.0);
+            } else {
+                $fs = "  -"; // XXX center instead
+            }
+            $data['data'][] = array($pkg, "$inst_version ($inst_state)", "$version ($state)", $fs);
+        }
+        if (empty($data['data'])) {
+            $this->ui->outputData('No upgrades available');
+        } else {
+            $this->ui->outputData($data, $command);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ doClearCache()
+
+    function doClearCache($command, $options, $params)
+    {
+        $cache_dir = $this->config->get('cache_dir');
+        $verbose = $this->config->get('verbose');
+        $output = '';
+        if (!($dp = @opendir($cache_dir))) {
+            return $this->raiseError("opendir($cache_dir) failed: $php_errormsg");
+        }
+        if ($verbose >= 1) {
+            $output .= "reading directory $cache_dir\n";
+        }
+        $num = 0;
+        while ($ent = readdir($dp)) {
+            if (preg_match('/^xmlrpc_cache_[a-z0-9]{32}$/', $ent)) {
+                $path = $cache_dir . DIRECTORY_SEPARATOR . $ent;
+                $ok = @unlink($path);
+                if ($ok) {
+                    if ($verbose >= 2) {
+                        $output .= "deleted $path\n";
+                    }
+                    $num++;
+                } elseif ($verbose >= 1) {
+                    $output .= "failed to delete $path\n";
+                }
+            }
+        }
+        closedir($dp);
+        if ($verbose >= 1) {
+            $output .= "$num cache entries cleared\n";
+        }
+        $this->ui->outputData(rtrim($output), $command);
+        return $num;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Common.php b/3rdparty/PEAR/Common.php
new file mode 100644
index 0000000000000000000000000000000000000000..fba53a377997ab5d70e596cc26d24554ff4e564e
--- /dev/null
+++ b/3rdparty/PEAR/Common.php
@@ -0,0 +1,2094 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2003 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net>                                   |
+// |          Tomas V.V.Cox <cox@idecnet.com>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Common.php,v 1.126.2.2 2004/12/27 07:04:19 cellog Exp $
+
+require_once 'PEAR.php';
+require_once 'Archive/Tar.php';
+require_once 'System.php';
+require_once 'PEAR/Config.php';
+
+// {{{ constants and globals
+
+/**
+ * PEAR_Common error when an invalid PHP file is passed to PEAR_Common::analyzeSourceCode()
+ */
+define('PEAR_COMMON_ERROR_INVALIDPHP', 1);
+define('_PEAR_COMMON_PACKAGE_NAME_PREG', '[A-Za-z][a-zA-Z0-9_]+');
+define('PEAR_COMMON_PACKAGE_NAME_PREG', '/^' . _PEAR_COMMON_PACKAGE_NAME_PREG . '$/');
+
+// this should allow: 1, 1.0, 1.0RC1, 1.0dev, 1.0dev123234234234, 1.0a1, 1.0b1, 1.0pl1
+define('_PEAR_COMMON_PACKAGE_VERSION_PREG', '\d+(?:\.\d+)*(?:[a-zA-Z]+\d*)?');
+define('PEAR_COMMON_PACKAGE_VERSION_PREG', '/^' . _PEAR_COMMON_PACKAGE_VERSION_PREG . '$/i');
+
+// XXX far from perfect :-)
+define('PEAR_COMMON_PACKAGE_DOWNLOAD_PREG', '/^(' . _PEAR_COMMON_PACKAGE_NAME_PREG . ')(-([.0-9a-zA-Z]+))?$/');
+
+/**
+ * List of temporary files and directories registered by
+ * PEAR_Common::addTempFile().
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_tempfiles'] = array();
+
+/**
+ * Valid maintainer roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_maintainer_roles'] = array('lead','developer','contributor','helper');
+
+/**
+ * Valid release states
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_release_states'] = array('alpha','beta','stable','snapshot','devel');
+
+/**
+ * Valid dependency types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_types'] = array('pkg','ext','php','prog','ldlib','rtlib','os','websrv','sapi');
+
+/**
+ * Valid dependency relations
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_dependency_relations'] = array('has','eq','lt','le','gt','ge','not', 'ne');
+
+/**
+ * Valid file roles
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_file_roles'] = array('php','ext','test','doc','data','src','script');
+
+/**
+ * Valid replacement types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_replacement_types'] = array('php-const', 'pear-config', 'package-info');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_provide_types'] = array('ext', 'prog', 'class', 'function', 'feature', 'api');
+
+/**
+ * Valid "provide" types
+ * @var array
+ */
+$GLOBALS['_PEAR_Common_script_phases'] = array('pre-install', 'post-install', 'pre-uninstall', 'post-uninstall', 'pre-build', 'post-build', 'pre-configure', 'post-configure', 'pre-setup', 'post-setup');
+
+// }}}
+
+/**
+ * Class providing common functionality for PEAR administration classes.
+ * @deprecated This class will disappear, and its components will be spread
+ *             into smaller classes, like the AT&T breakup
+ */
+class PEAR_Common extends PEAR
+{
+    // {{{ properties
+
+    /** stack of elements, gives some sort of XML context */
+    var $element_stack = array();
+
+    /** name of currently parsed XML element */
+    var $current_element;
+
+    /** array of attributes of the currently parsed XML element */
+    var $current_attributes = array();
+
+    /** assoc with information about a package */
+    var $pkginfo = array();
+
+    /**
+     * User Interface object (PEAR_Frontend_* class).  If null,
+     * the log() method uses print.
+     * @var object
+     */
+    var $ui = null;
+
+    /**
+     * Configuration object (PEAR_Config).
+     * @var object
+     */
+    var $config = null;
+
+    var $current_path = null;
+
+    /**
+     * PEAR_SourceAnalyzer instance
+     * @var object
+     */
+    var $source_analyzer = null;
+    /**
+     * Flag variable used to mark a valid package file
+     * @var boolean
+     * @access private
+     */
+    var $_validPackageFile;
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Common constructor
+     *
+     * @access public
+     */
+    function PEAR_Common()
+    {
+        parent::PEAR();
+        $this->config = &PEAR_Config::singleton();
+        $this->debug = $this->config->get('verbose');
+    }
+
+    // }}}
+    // {{{ destructor
+
+    /**
+     * PEAR_Common destructor
+     *
+     * @access private
+     */
+    function _PEAR_Common()
+    {
+        // doesn't work due to bug #14744
+        //$tempfiles = $this->_tempfiles;
+        $tempfiles =& $GLOBALS['_PEAR_Common_tempfiles'];
+        while ($file = array_shift($tempfiles)) {
+            if (@is_dir($file)) {
+                System::rm(array('-rf', $file));
+            } elseif (file_exists($file)) {
+                unlink($file);
+            }
+        }
+    }
+
+    // }}}
+    // {{{ addTempFile()
+
+    /**
+     * Register a temporary file or directory.  When the destructor is
+     * executed, all registered temporary files and directories are
+     * removed.
+     *
+     * @param string  $file  name of file or directory
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function addTempFile($file)
+    {
+        $GLOBALS['_PEAR_Common_tempfiles'][] = $file;
+    }
+
+    // }}}
+    // {{{ mkDirHier()
+
+    /**
+     * Wrapper to System::mkDir(), creates a directory as well as
+     * any necessary parent directories.
+     *
+     * @param string  $dir  directory name
+     *
+     * @return bool TRUE on success, or a PEAR error
+     *
+     * @access public
+     */
+    function mkDirHier($dir)
+    {
+        $this->log(2, "+ create dir $dir");
+        return System::mkDir(array('-p', $dir));
+    }
+
+    // }}}
+    // {{{ log()
+
+    /**
+     * Logging method.
+     *
+     * @param int    $level  log level (0 is quiet, higher is noisier)
+     * @param string $msg    message to write to the log
+     *
+     * @return void
+     *
+     * @access public
+     */
+    function log($level, $msg, $append_crlf = true)
+    {
+        if ($this->debug >= $level) {
+            if (is_object($this->ui)) {
+                $this->ui->log($msg, $append_crlf);
+            } else {
+                print "$msg\n";
+            }
+        }
+    }
+
+    // }}}
+    // {{{ mkTempDir()
+
+    /**
+     * Create and register a temporary directory.
+     *
+     * @param string $tmpdir (optional) Directory to use as tmpdir.
+     *                       Will use system defaults (for example
+     *                       /tmp or c:\windows\temp) if not specified
+     *
+     * @return string name of created directory
+     *
+     * @access public
+     */
+    function mkTempDir($tmpdir = '')
+    {
+        if ($tmpdir) {
+            $topt = array('-t', $tmpdir);
+        } else {
+            $topt = array();
+        }
+        $topt = array_merge($topt, array('-d', 'pear'));
+        if (!$tmpdir = System::mktemp($topt)) {
+            return false;
+        }
+        $this->addTempFile($tmpdir);
+        return $tmpdir;
+    }
+
+    // }}}
+    // {{{ setFrontendObject()
+
+    /**
+     * Set object that represents the frontend to be used.
+     *
+     * @param  object Reference of the frontend object
+     * @return void
+     * @access public
+     */
+    function setFrontendObject(&$ui)
+    {
+        $this->ui = &$ui;
+    }
+
+    // }}}
+
+    // {{{ _unIndent()
+
+    /**
+     * Unindent given string (?)
+     *
+     * @param string $str The string that has to be unindented.
+     * @return string
+     * @access private
+     */
+    function _unIndent($str)
+    {
+        // remove leading newlines
+        $str = preg_replace('/^[\r\n]+/', '', $str);
+        // find whitespace at the beginning of the first line
+        $indent_len = strspn($str, " \t");
+        $indent = substr($str, 0, $indent_len);
+        $data = '';
+        // remove the same amount of whitespace from following lines
+        foreach (explode("\n", $str) as $line) {
+            if (substr($line, 0, $indent_len) == $indent) {
+                $data .= substr($line, $indent_len) . "\n";
+            }
+        }
+        return $data;
+    }
+
+    // }}}
+    // {{{ _element_start()
+
+    /**
+     * XML parser callback for starting elements.  Used while package
+     * format version is not yet known.
+     *
+     * @param resource  $xp       XML parser resource
+     * @param string    $name     name of starting element
+     * @param array     $attribs  element attributes, name => value
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _element_start($xp, $name, $attribs)
+    {
+        array_push($this->element_stack, $name);
+        $this->current_element = $name;
+        $spos = sizeof($this->element_stack) - 2;
+        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+        $this->current_attributes = $attribs;
+        switch ($name) {
+            case 'package': {
+                $this->_validPackageFile = true;
+                if (isset($attribs['version'])) {
+                    $vs = preg_replace('/[^0-9a-z]/', '_', $attribs['version']);
+                } else {
+                    $vs = '1_0';
+                }
+                $elem_start = '_element_start_'. $vs;
+                $elem_end = '_element_end_'. $vs;
+                $cdata = '_pkginfo_cdata_'. $vs;
+                if (!method_exists($this, $elem_start) ||
+                      !method_exists($this, $elem_end) ||
+                      !method_exists($this, $cdata)) {
+                    $this->raiseError("No handlers for package.xml version $attribs[version]");
+                    return;
+                }
+                xml_set_element_handler($xp, $elem_start, $elem_end);
+                xml_set_character_data_handler($xp, $cdata);
+                break;
+            }
+        }
+    }
+
+    // }}}
+    // {{{ _element_end()
+
+    /**
+     * XML parser callback for ending elements.  Used while package
+     * format version is not yet known.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  name of ending element
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _element_end($xp, $name)
+    {
+    }
+
+    // }}}
+
+    // Support for package DTD v1.0:
+    // {{{ _element_start_1_0()
+
+    /**
+     * XML parser callback for ending elements.  Used for version 1.0
+     * packages.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  name of ending element
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _element_start_1_0($xp, $name, $attribs)
+    {
+        array_push($this->element_stack, $name);
+        $this->current_element = $name;
+        $spos = sizeof($this->element_stack) - 2;
+        $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
+        $this->current_attributes = $attribs;
+        $this->cdata = '';
+        switch ($name) {
+            case 'dir':
+                if ($this->in_changelog) {
+                    break;
+                }
+                if ($attribs['name'] != '/') {
+                    $this->dir_names[] = $attribs['name'];
+                }
+                if (isset($attribs['baseinstalldir'])) {
+                    $this->dir_install = $attribs['baseinstalldir'];
+                }
+                if (isset($attribs['role'])) {
+                    $this->dir_role = $attribs['role'];
+                }
+                break;
+            case 'file':
+                if ($this->in_changelog) {
+                    break;
+                }
+                if (isset($attribs['name'])) {
+                    $path = '';
+                    if (count($this->dir_names)) {
+                        foreach ($this->dir_names as $dir) {
+                            $path .= $dir . DIRECTORY_SEPARATOR;
+                        }
+                    }
+                    $path .= $attribs['name'];
+                    unset($attribs['name']);
+                    $this->current_path = $path;
+                    $this->filelist[$path] = $attribs;
+                    // Set the baseinstalldir only if the file don't have this attrib
+                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
+                        isset($this->dir_install))
+                    {
+                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+                    }
+                    // Set the Role
+                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+                        $this->filelist[$path]['role'] = $this->dir_role;
+                    }
+                }
+                break;
+            case 'replace':
+                if (!$this->in_changelog) {
+                    $this->filelist[$this->current_path]['replacements'][] = $attribs;
+                }
+                break;
+            case 'maintainers':
+                $this->pkginfo['maintainers'] = array();
+                $this->m_i = 0; // maintainers array index
+                break;
+            case 'maintainer':
+                // compatibility check
+                if (!isset($this->pkginfo['maintainers'])) {
+                    $this->pkginfo['maintainers'] = array();
+                    $this->m_i = 0;
+                }
+                $this->pkginfo['maintainers'][$this->m_i] = array();
+                $this->current_maintainer =& $this->pkginfo['maintainers'][$this->m_i];
+                break;
+            case 'changelog':
+                $this->pkginfo['changelog'] = array();
+                $this->c_i = 0; // changelog array index
+                $this->in_changelog = true;
+                break;
+            case 'release':
+                if ($this->in_changelog) {
+                    $this->pkginfo['changelog'][$this->c_i] = array();
+                    $this->current_release = &$this->pkginfo['changelog'][$this->c_i];
+                } else {
+                    $this->current_release = &$this->pkginfo;
+                }
+                break;
+            case 'deps':
+                if (!$this->in_changelog) {
+                    $this->pkginfo['release_deps'] = array();
+                }
+                break;
+            case 'dep':
+                // dependencies array index
+                if (!$this->in_changelog) {
+                    $this->d_i++;
+                    $this->pkginfo['release_deps'][$this->d_i] = $attribs;
+                }
+                break;
+            case 'configureoptions':
+                if (!$this->in_changelog) {
+                    $this->pkginfo['configure_options'] = array();
+                }
+                break;
+            case 'configureoption':
+                if (!$this->in_changelog) {
+                    $this->pkginfo['configure_options'][] = $attribs;
+                }
+                break;
+            case 'provides':
+                if (empty($attribs['type']) || empty($attribs['name'])) {
+                    break;
+                }
+                $attribs['explicit'] = true;
+                $this->pkginfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
+                break;
+        }
+    }
+
+    // }}}
+    // {{{ _element_end_1_0()
+
+    /**
+     * XML parser callback for ending elements.  Used for version 1.0
+     * packages.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  name of ending element
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _element_end_1_0($xp, $name)
+    {
+        $data = trim($this->cdata);
+        switch ($name) {
+            case 'name':
+                switch ($this->prev_element) {
+                    case 'package':
+                        // XXX should we check the package name here?
+                        $this->pkginfo['package'] = ereg_replace('[^a-zA-Z0-9._]', '_', $data);
+                        break;
+                    case 'maintainer':
+                        $this->current_maintainer['name'] = $data;
+                        break;
+                }
+                break;
+            case 'summary':
+                $this->pkginfo['summary'] = $data;
+                break;
+            case 'description':
+                $data = $this->_unIndent($this->cdata);
+                $this->pkginfo['description'] = $data;
+                break;
+            case 'user':
+                $this->current_maintainer['handle'] = $data;
+                break;
+            case 'email':
+                $this->current_maintainer['email'] = $data;
+                break;
+            case 'role':
+                $this->current_maintainer['role'] = $data;
+                break;
+            case 'version':
+                $data = ereg_replace ('[^a-zA-Z0-9._\-]', '_', $data);
+                if ($this->in_changelog) {
+                    $this->current_release['version'] = $data;
+                } else {
+                    $this->pkginfo['version'] = $data;
+                }
+                break;
+            case 'date':
+                if ($this->in_changelog) {
+                    $this->current_release['release_date'] = $data;
+                } else {
+                    $this->pkginfo['release_date'] = $data;
+                }
+                break;
+            case 'notes':
+                // try to "de-indent" release notes in case someone
+                // has been over-indenting their xml ;-)
+                $data = $this->_unIndent($this->cdata);
+                if ($this->in_changelog) {
+                    $this->current_release['release_notes'] = $data;
+                } else {
+                    $this->pkginfo['release_notes'] = $data;
+                }
+                break;
+            case 'warnings':
+                if ($this->in_changelog) {
+                    $this->current_release['release_warnings'] = $data;
+                } else {
+                    $this->pkginfo['release_warnings'] = $data;
+                }
+                break;
+            case 'state':
+                if ($this->in_changelog) {
+                    $this->current_release['release_state'] = $data;
+                } else {
+                    $this->pkginfo['release_state'] = $data;
+                }
+                break;
+            case 'license':
+                if ($this->in_changelog) {
+                    $this->current_release['release_license'] = $data;
+                } else {
+                    $this->pkginfo['release_license'] = $data;
+                }
+                break;
+            case 'dep':
+                if ($data && !$this->in_changelog) {
+                    $this->pkginfo['release_deps'][$this->d_i]['name'] = $data;
+                }
+                break;
+            case 'dir':
+                if ($this->in_changelog) {
+                    break;
+                }
+                array_pop($this->dir_names);
+                break;
+            case 'file':
+                if ($this->in_changelog) {
+                    break;
+                }
+                if ($data) {
+                    $path = '';
+                    if (count($this->dir_names)) {
+                        foreach ($this->dir_names as $dir) {
+                            $path .= $dir . DIRECTORY_SEPARATOR;
+                        }
+                    }
+                    $path .= $data;
+                    $this->filelist[$path] = $this->current_attributes;
+                    // Set the baseinstalldir only if the file don't have this attrib
+                    if (!isset($this->filelist[$path]['baseinstalldir']) &&
+                        isset($this->dir_install))
+                    {
+                        $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
+                    }
+                    // Set the Role
+                    if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
+                        $this->filelist[$path]['role'] = $this->dir_role;
+                    }
+                }
+                break;
+            case 'maintainer':
+                if (empty($this->pkginfo['maintainers'][$this->m_i]['role'])) {
+                    $this->pkginfo['maintainers'][$this->m_i]['role'] = 'lead';
+                }
+                $this->m_i++;
+                break;
+            case 'release':
+                if ($this->in_changelog) {
+                    $this->c_i++;
+                }
+                break;
+            case 'changelog':
+                $this->in_changelog = false;
+                break;
+        }
+        array_pop($this->element_stack);
+        $spos = sizeof($this->element_stack) - 1;
+        $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
+        $this->cdata = '';
+    }
+
+    // }}}
+    // {{{ _pkginfo_cdata_1_0()
+
+    /**
+     * XML parser callback for character data.  Used for version 1.0
+     * packages.
+     *
+     * @param resource  $xp    XML parser resource
+     * @param string    $name  character data
+     *
+     * @return void
+     *
+     * @access private
+     */
+    function _pkginfo_cdata_1_0($xp, $data)
+    {
+        if (isset($this->cdata)) {
+            $this->cdata .= $data;
+        }
+    }
+
+    // }}}
+
+    // {{{ infoFromTgzFile()
+
+    /**
+     * Returns information about a package file.  Expects the name of
+     * a gzipped tar file as input.
+     *
+     * @param string  $file  name of .tgz file
+     *
+     * @return array  array with package information
+     *
+     * @access public
+     *
+     */
+    function infoFromTgzFile($file)
+    {
+        if (!@is_file($file)) {
+            return $this->raiseError("could not open file \"$file\"");
+        }
+        $tar = new Archive_Tar($file);
+        if ($this->debug <= 1) {
+            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
+        }
+        $content = $tar->listContent();
+        if ($this->debug <= 1) {
+            $tar->popErrorHandling();
+        }
+        if (!is_array($content)) {
+            $file = realpath($file);
+            return $this->raiseError("Could not get contents of package \"$file\"".
+                                     '. Invalid tgz file.');
+        }
+        $xml = null;
+        foreach ($content as $file) {
+            $name = $file['filename'];
+            if ($name == 'package.xml') {
+                $xml = $name;
+                break;
+            } elseif (ereg('package.xml$', $name, $match)) {
+                $xml = $match[0];
+                break;
+            }
+        }
+        $tmpdir = System::mkTemp(array('-d', 'pear'));
+        $this->addTempFile($tmpdir);
+        if (!$xml || !$tar->extractList(array($xml), $tmpdir)) {
+            return $this->raiseError('could not extract the package.xml file');
+        }
+        return $this->infoFromDescriptionFile("$tmpdir/$xml");
+    }
+
+    // }}}
+    // {{{ infoFromDescriptionFile()
+
+    /**
+     * Returns information about a package file.  Expects the name of
+     * a package xml file as input.
+     *
+     * @param string  $descfile  name of package xml file
+     *
+     * @return array  array with package information
+     *
+     * @access public
+     *
+     */
+    function infoFromDescriptionFile($descfile)
+    {
+        if (!@is_file($descfile) || !is_readable($descfile) ||
+             (!$fp = @fopen($descfile, 'r'))) {
+            return $this->raiseError("Unable to open $descfile");
+        }
+
+        // read the whole thing so we only get one cdata callback
+        // for each block of cdata
+        $data = fread($fp, filesize($descfile));
+        return $this->infoFromString($data);
+    }
+
+    // }}}
+    // {{{ infoFromString()
+
+    /**
+     * Returns information about a package file.  Expects the contents
+     * of a package xml file as input.
+     *
+     * @param string  $data  name of package xml file
+     *
+     * @return array   array with package information
+     *
+     * @access public
+     *
+     */
+    function infoFromString($data)
+    {
+        require_once('PEAR/Dependency.php');
+        if (PEAR_Dependency::checkExtension($error, 'xml')) {
+            return $this->raiseError($error);
+        }
+        $xp = @xml_parser_create();
+        if (!$xp) {
+            return $this->raiseError('Unable to create XML parser');
+        }
+        xml_set_object($xp, $this);
+        xml_set_element_handler($xp, '_element_start', '_element_end');
+        xml_set_character_data_handler($xp, '_pkginfo_cdata');
+        xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
+
+        $this->element_stack = array();
+        $this->pkginfo = array('provides' => array());
+        $this->current_element = false;
+        unset($this->dir_install);
+        $this->pkginfo['filelist'] = array();
+        $this->filelist =& $this->pkginfo['filelist'];
+        $this->dir_names = array();
+        $this->in_changelog = false;
+        $this->d_i = 0;
+        $this->cdata = '';
+        $this->_validPackageFile = false;
+
+        if (!xml_parse($xp, $data, 1)) {
+            $code = xml_get_error_code($xp);
+            $msg = sprintf("XML error: %s at line %d",
+                           xml_error_string($code),
+                           xml_get_current_line_number($xp));
+            xml_parser_free($xp);
+            return $this->raiseError($msg, $code);
+        }
+
+        xml_parser_free($xp);
+
+        if (!$this->_validPackageFile) {
+            return $this->raiseError('Invalid Package File, no <package> tag');
+        }
+        foreach ($this->pkginfo as $k => $v) {
+            if (!is_array($v)) {
+                $this->pkginfo[$k] = trim($v);
+            }
+        }
+        return $this->pkginfo;
+    }
+    // }}}
+    // {{{ infoFromAny()
+
+    /**
+     * Returns package information from different sources
+     *
+     * This method is able to extract information about a package
+     * from a .tgz archive or from a XML package definition file.
+     *
+     * @access public
+     * @param  string Filename of the source ('package.xml', '<package>.tgz')
+     * @return string
+     */
+    function infoFromAny($info)
+    {
+        if (is_string($info) && file_exists($info)) {
+            $tmp = substr($info, -4);
+            if ($tmp == '.xml') {
+                $info = $this->infoFromDescriptionFile($info);
+            } elseif ($tmp == '.tar' || $tmp == '.tgz') {
+                $info = $this->infoFromTgzFile($info);
+            } else {
+                $fp = fopen($info, "r");
+                $test = fread($fp, 5);
+                fclose($fp);
+                if ($test == "<?xml") {
+                    $info = $this->infoFromDescriptionFile($info);
+                } else {
+                    $info = $this->infoFromTgzFile($info);
+                }
+            }
+            if (PEAR::isError($info)) {
+                return $this->raiseError($info);
+            }
+        }
+        return $info;
+    }
+
+    // }}}
+    // {{{ xmlFromInfo()
+
+    /**
+     * Return an XML document based on the package info (as returned
+     * by the PEAR_Common::infoFrom* methods).
+     *
+     * @param array  $pkginfo  package info
+     *
+     * @return string XML data
+     *
+     * @access public
+     */
+    function xmlFromInfo($pkginfo)
+    {
+        static $maint_map = array(
+            "handle" => "user",
+            "name" => "name",
+            "email" => "email",
+            "role" => "role",
+            );
+        $ret = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\" ?>\n";
+        $ret .= "<!DOCTYPE package SYSTEM \"http://pear.php.net/dtd/package-1.0\">\n";
+        $ret .= "<package version=\"1.0\">
+  <name>$pkginfo[package]</name>
+  <summary>".htmlspecialchars($pkginfo['summary'])."</summary>
+  <description>".htmlspecialchars($pkginfo['description'])."</description>
+  <maintainers>
+";
+        foreach ($pkginfo['maintainers'] as $maint) {
+            $ret .= "    <maintainer>\n";
+            foreach ($maint_map as $idx => $elm) {
+                $ret .= "      <$elm>";
+                $ret .= htmlspecialchars($maint[$idx]);
+                $ret .= "</$elm>\n";
+            }
+            $ret .= "    </maintainer>\n";
+        }
+        $ret .= "  </maintainers>\n";
+        $ret .= $this->_makeReleaseXml($pkginfo);
+        if (@sizeof($pkginfo['changelog']) > 0) {
+            $ret .= "  <changelog>\n";
+            foreach ($pkginfo['changelog'] as $oldrelease) {
+                $ret .= $this->_makeReleaseXml($oldrelease, true);
+            }
+            $ret .= "  </changelog>\n";
+        }
+        $ret .= "</package>\n";
+        return $ret;
+    }
+
+    // }}}
+    // {{{ _makeReleaseXml()
+
+    /**
+     * Generate part of an XML description with release information.
+     *
+     * @param array  $pkginfo    array with release information
+     * @param bool   $changelog  whether the result will be in a changelog element
+     *
+     * @return string XML data
+     *
+     * @access private
+     */
+    function _makeReleaseXml($pkginfo, $changelog = false)
+    {
+        // XXX QUOTE ENTITIES IN PCDATA, OR EMBED IN CDATA BLOCKS!!
+        $indent = $changelog ? "  " : "";
+        $ret = "$indent  <release>\n";
+        if (!empty($pkginfo['version'])) {
+            $ret .= "$indent    <version>$pkginfo[version]</version>\n";
+        }
+        if (!empty($pkginfo['release_date'])) {
+            $ret .= "$indent    <date>$pkginfo[release_date]</date>\n";
+        }
+        if (!empty($pkginfo['release_license'])) {
+            $ret .= "$indent    <license>$pkginfo[release_license]</license>\n";
+        }
+        if (!empty($pkginfo['release_state'])) {
+            $ret .= "$indent    <state>$pkginfo[release_state]</state>\n";
+        }
+        if (!empty($pkginfo['release_notes'])) {
+            $ret .= "$indent    <notes>".htmlspecialchars($pkginfo['release_notes'])."</notes>\n";
+        }
+        if (!empty($pkginfo['release_warnings'])) {
+            $ret .= "$indent    <warnings>".htmlspecialchars($pkginfo['release_warnings'])."</warnings>\n";
+        }
+        if (isset($pkginfo['release_deps']) && sizeof($pkginfo['release_deps']) > 0) {
+            $ret .= "$indent    <deps>\n";
+            foreach ($pkginfo['release_deps'] as $dep) {
+                $ret .= "$indent      <dep type=\"$dep[type]\" rel=\"$dep[rel]\"";
+                if (isset($dep['version'])) {
+                    $ret .= " version=\"$dep[version]\"";
+                }
+                if (isset($dep['optional'])) {
+                    $ret .= " optional=\"$dep[optional]\"";
+                }
+                if (isset($dep['name'])) {
+                    $ret .= ">$dep[name]</dep>\n";
+                } else {
+                    $ret .= "/>\n";
+                }
+            }
+            $ret .= "$indent    </deps>\n";
+        }
+        if (isset($pkginfo['configure_options'])) {
+            $ret .= "$indent    <configureoptions>\n";
+            foreach ($pkginfo['configure_options'] as $c) {
+                $ret .= "$indent      <configureoption name=\"".
+                    htmlspecialchars($c['name']) . "\"";
+                if (isset($c['default'])) {
+                    $ret .= " default=\"" . htmlspecialchars($c['default']) . "\"";
+                }
+                $ret .= " prompt=\"" . htmlspecialchars($c['prompt']) . "\"";
+                $ret .= "/>\n";
+            }
+            $ret .= "$indent    </configureoptions>\n";
+        }
+        if (isset($pkginfo['provides'])) {
+            foreach ($pkginfo['provides'] as $key => $what) {
+                $ret .= "$indent    <provides type=\"$what[type]\" ";
+                $ret .= "name=\"$what[name]\" ";
+                if (isset($what['extends'])) {
+                    $ret .= "extends=\"$what[extends]\" ";
+                }
+                $ret .= "/>\n";
+            }
+        }
+        if (isset($pkginfo['filelist'])) {
+            $ret .= "$indent    <filelist>\n";
+            foreach ($pkginfo['filelist'] as $file => $fa) {
+                @$ret .= "$indent      <file role=\"$fa[role]\"";
+                if (isset($fa['baseinstalldir'])) {
+                    $ret .= ' baseinstalldir="' .
+                        htmlspecialchars($fa['baseinstalldir']) . '"';
+                }
+                if (isset($fa['md5sum'])) {
+                    $ret .= " md5sum=\"$fa[md5sum]\"";
+                }
+                if (isset($fa['platform'])) {
+                    $ret .= " platform=\"$fa[platform]\"";
+                }
+                if (!empty($fa['install-as'])) {
+                    $ret .= ' install-as="' .
+                        htmlspecialchars($fa['install-as']) . '"';
+                }
+                $ret .= ' name="' . htmlspecialchars($file) . '"';
+                if (empty($fa['replacements'])) {
+                    $ret .= "/>\n";
+                } else {
+                    $ret .= ">\n";
+                    foreach ($fa['replacements'] as $r) {
+                        $ret .= "$indent        <replace";
+                        foreach ($r as $k => $v) {
+                            $ret .= " $k=\"" . htmlspecialchars($v) .'"';
+                        }
+                        $ret .= "/>\n";
+                    }
+                    @$ret .= "$indent      </file>\n";
+                }
+            }
+            $ret .= "$indent    </filelist>\n";
+        }
+        $ret .= "$indent  </release>\n";
+        return $ret;
+    }
+
+    // }}}
+    // {{{ validatePackageInfo()
+
+    /**
+     * Validate XML package definition file.
+     *
+     * @param  string $info Filename of the package archive or of the
+     *                package definition file
+     * @param  array $errors Array that will contain the errors
+     * @param  array $warnings Array that will contain the warnings
+     * @param  string $dir_prefix (optional) directory where source files
+     *                may be found, or empty if they are not available
+     * @access public
+     * @return boolean
+     */
+    function validatePackageInfo($info, &$errors, &$warnings, $dir_prefix = '')
+    {
+        if (PEAR::isError($info = $this->infoFromAny($info))) {
+            return $this->raiseError($info);
+        }
+        if (!is_array($info)) {
+            return false;
+        }
+
+        $errors = array();
+        $warnings = array();
+        if (!isset($info['package'])) {
+            $errors[] = 'missing package name';
+        } elseif (!$this->validPackageName($info['package'])) {
+            $errors[] = 'invalid package name';
+        }
+        $this->_packageName = $pn = $info['package'];
+
+        if (empty($info['summary'])) {
+            $errors[] = 'missing summary';
+        } elseif (strpos(trim($info['summary']), "\n") !== false) {
+            $warnings[] = 'summary should be on a single line';
+        }
+        if (empty($info['description'])) {
+            $errors[] = 'missing description';
+        }
+        if (empty($info['release_license'])) {
+            $errors[] = 'missing license';
+        }
+        if (!isset($info['version'])) {
+            $errors[] = 'missing version';
+        } elseif (!$this->validPackageVersion($info['version'])) {
+            $errors[] = 'invalid package release version';
+        }
+        if (empty($info['release_state'])) {
+            $errors[] = 'missing release state';
+        } elseif (!in_array($info['release_state'], PEAR_Common::getReleaseStates())) {
+            $errors[] = "invalid release state `$info[release_state]', should be one of: "
+                . implode(' ', PEAR_Common::getReleaseStates());
+        }
+        if (empty($info['release_date'])) {
+            $errors[] = 'missing release date';
+        } elseif (!preg_match('/^\d{4}-\d\d-\d\d$/', $info['release_date'])) {
+            $errors[] = "invalid release date `$info[release_date]', format is YYYY-MM-DD";
+        }
+        if (empty($info['release_notes'])) {
+            $errors[] = "missing release notes";
+        }
+        if (empty($info['maintainers'])) {
+            $errors[] = 'no maintainer(s)';
+        } else {
+            $i = 1;
+            foreach ($info['maintainers'] as $m) {
+                if (empty($m['handle'])) {
+                    $errors[] = "maintainer $i: missing handle";
+                }
+                if (empty($m['role'])) {
+                    $errors[] = "maintainer $i: missing role";
+                } elseif (!in_array($m['role'], PEAR_Common::getUserRoles())) {
+                    $errors[] = "maintainer $i: invalid role `$m[role]', should be one of: "
+                        . implode(' ', PEAR_Common::getUserRoles());
+                }
+                if (empty($m['name'])) {
+                    $errors[] = "maintainer $i: missing name";
+                }
+                if (empty($m['email'])) {
+                    $errors[] = "maintainer $i: missing email";
+                }
+                $i++;
+            }
+        }
+        if (!empty($info['release_deps'])) {
+            $i = 1;
+            foreach ($info['release_deps'] as $d) {
+                if (empty($d['type'])) {
+                    $errors[] = "dependency $i: missing type";
+                } elseif (!in_array($d['type'], PEAR_Common::getDependencyTypes())) {
+                    $errors[] = "dependency $i: invalid type '$d[type]', should be one of: " .
+                        implode(' ', PEAR_Common::getDependencyTypes());
+                }
+                if (empty($d['rel'])) {
+                    $errors[] = "dependency $i: missing relation";
+                } elseif (!in_array($d['rel'], PEAR_Common::getDependencyRelations())) {
+                    $errors[] = "dependency $i: invalid relation '$d[rel]', should be one of: "
+                        . implode(' ', PEAR_Common::getDependencyRelations());
+                }
+                if (!empty($d['optional'])) {
+                    if (!in_array($d['optional'], array('yes', 'no'))) {
+                        $errors[] = "dependency $i: invalid relation optional attribute '$d[optional]', should be one of: yes no";
+                    } else {
+                        if (($d['rel'] == 'not' || $d['rel'] == 'ne') && $d['optional'] == 'yes') {
+                            $errors[] = "dependency $i: 'not' and 'ne' dependencies cannot be " .
+                                "optional";
+                        }
+                    }
+                }
+                if ($d['rel'] != 'not' && $d['rel'] != 'has' && empty($d['version'])) {
+                    $warnings[] = "dependency $i: missing version";
+                } elseif (($d['rel'] == 'not' || $d['rel'] == 'has') && !empty($d['version'])) {
+                    $warnings[] = "dependency $i: version ignored for `$d[rel]' dependencies";
+                }
+                if ($d['rel'] == 'not' && !empty($d['version'])) {
+                    $warnings[] = "dependency $i: 'not' defines a total conflict, to exclude " .
+                        "specific versions, use 'ne'";
+                }
+                if ($d['type'] == 'php' && !empty($d['name'])) {
+                    $warnings[] = "dependency $i: name ignored for php type dependencies";
+                } elseif ($d['type'] != 'php' && empty($d['name'])) {
+                    $errors[] = "dependency $i: missing name";
+                }
+                if ($d['type'] == 'php' && $d['rel'] == 'not') {
+                    $errors[] = "dependency $i: PHP dependencies cannot use 'not' " .
+                        "rel, use 'ne' to exclude versions";
+                }
+                $i++;
+            }
+        }
+        if (!empty($info['configure_options'])) {
+            $i = 1;
+            foreach ($info['configure_options'] as $c) {
+                if (empty($c['name'])) {
+                    $errors[] = "configure option $i: missing name";
+                }
+                if (empty($c['prompt'])) {
+                    $errors[] = "configure option $i: missing prompt";
+                }
+                $i++;
+            }
+        }
+        if (empty($info['filelist'])) {
+            $errors[] = 'no files';
+        } else {
+            foreach ($info['filelist'] as $file => $fa) {
+                if (empty($fa['role'])) {
+                    $errors[] = "file $file: missing role";
+                    continue;
+                } elseif (!in_array($fa['role'], PEAR_Common::getFileRoles())) {
+                    $errors[] = "file $file: invalid role, should be one of: "
+                        . implode(' ', PEAR_Common::getFileRoles());
+                }
+                if ($fa['role'] == 'php' && $dir_prefix) {
+                    $this->log(1, "Analyzing $file");
+                    $srcinfo = $this->analyzeSourceCode($dir_prefix . DIRECTORY_SEPARATOR . $file);
+                    if ($srcinfo) {
+                        $this->buildProvidesArray($srcinfo);
+                    }
+                }
+
+                // (ssb) Any checks we can do for baseinstalldir?
+                // (cox) Perhaps checks that either the target dir and
+                //       baseInstall doesn't cointain "../../"
+            }
+        }
+        $this->_packageName = $pn = $info['package'];
+        $pnl = strlen($pn);
+        foreach ((array)$this->pkginfo['provides'] as $key => $what) {
+            if (isset($what['explicit'])) {
+                // skip conformance checks if the provides entry is
+                // specified in the package.xml file
+                continue;
+            }
+            extract($what);
+            if ($type == 'class') {
+                if (!strncasecmp($name, $pn, $pnl)) {
+                    continue;
+                }
+                $warnings[] = "in $file: class \"$name\" not prefixed with package name \"$pn\"";
+            } elseif ($type == 'function') {
+                if (strstr($name, '::') || !strncasecmp($name, $pn, $pnl)) {
+                    continue;
+                }
+                $warnings[] = "in $file: function \"$name\" not prefixed with package name \"$pn\"";
+            }
+        }
+
+
+        return true;
+    }
+
+    // }}}
+    // {{{ buildProvidesArray()
+
+    /**
+     * Build a "provides" array from data returned by
+     * analyzeSourceCode().  The format of the built array is like
+     * this:
+     *
+     *  array(
+     *    'class;MyClass' => 'array('type' => 'class', 'name' => 'MyClass'),
+     *    ...
+     *  )
+     *
+     *
+     * @param array $srcinfo array with information about a source file
+     * as returned by the analyzeSourceCode() method.
+     *
+     * @return void
+     *
+     * @access public
+     *
+     */
+    function buildProvidesArray($srcinfo)
+    {
+        $file = basename($srcinfo['source_file']);
+        $pn = '';
+        if (isset($this->_packageName)) {
+            $pn = $this->_packageName;
+        }
+        $pnl = strlen($pn);
+        foreach ($srcinfo['declared_classes'] as $class) {
+            $key = "class;$class";
+            if (isset($this->pkginfo['provides'][$key])) {
+                continue;
+            }
+            $this->pkginfo['provides'][$key] =
+                array('file'=> $file, 'type' => 'class', 'name' => $class);
+            if (isset($srcinfo['inheritance'][$class])) {
+                $this->pkginfo['provides'][$key]['extends'] =
+                    $srcinfo['inheritance'][$class];
+            }
+        }
+        foreach ($srcinfo['declared_methods'] as $class => $methods) {
+            foreach ($methods as $method) {
+                $function = "$class::$method";
+                $key = "function;$function";
+                if ($method{0} == '_' || !strcasecmp($method, $class) ||
+                    isset($this->pkginfo['provides'][$key])) {
+                    continue;
+                }
+                $this->pkginfo['provides'][$key] =
+                    array('file'=> $file, 'type' => 'function', 'name' => $function);
+            }
+        }
+
+        foreach ($srcinfo['declared_functions'] as $function) {
+            $key = "function;$function";
+            if ($function{0} == '_' || isset($this->pkginfo['provides'][$key])) {
+                continue;
+            }
+            if (!strstr($function, '::') && strncasecmp($function, $pn, $pnl)) {
+                $warnings[] = "in1 " . $file . ": function \"$function\" not prefixed with package name \"$pn\"";
+            }
+            $this->pkginfo['provides'][$key] =
+                array('file'=> $file, 'type' => 'function', 'name' => $function);
+        }
+    }
+
+    // }}}
+    // {{{ analyzeSourceCode()
+
+    /**
+     * Analyze the source code of the given PHP file
+     *
+     * @param  string Filename of the PHP file
+     * @return mixed
+     * @access public
+     */
+    function analyzeSourceCode($file)
+    {
+        if (!function_exists("token_get_all")) {
+            return false;
+        }
+        if (!defined('T_DOC_COMMENT')) {
+            define('T_DOC_COMMENT', T_COMMENT);
+        }
+        if (!defined('T_INTERFACE')) {
+            define('T_INTERFACE', -1);
+        }
+        if (!defined('T_IMPLEMENTS')) {
+            define('T_IMPLEMENTS', -1);
+        }
+        if (!$fp = @fopen($file, "r")) {
+            return false;
+        }
+        $contents = fread($fp, filesize($file));
+        $tokens = token_get_all($contents);
+/*
+        for ($i = 0; $i < sizeof($tokens); $i++) {
+            @list($token, $data) = $tokens[$i];
+            if (is_string($token)) {
+                var_dump($token);
+            } else {
+                print token_name($token) . ' ';
+                var_dump(rtrim($data));
+            }
+        }
+*/
+        $look_for = 0;
+        $paren_level = 0;
+        $bracket_level = 0;
+        $brace_level = 0;
+        $lastphpdoc = '';
+        $current_class = '';
+        $current_interface = '';
+        $current_class_level = -1;
+        $current_function = '';
+        $current_function_level = -1;
+        $declared_classes = array();
+        $declared_interfaces = array();
+        $declared_functions = array();
+        $declared_methods = array();
+        $used_classes = array();
+        $used_functions = array();
+        $extends = array();
+        $implements = array();
+        $nodeps = array();
+        $inquote = false;
+        $interface = false;
+        for ($i = 0; $i < sizeof($tokens); $i++) {
+            if (is_array($tokens[$i])) {
+                list($token, $data) = $tokens[$i];
+            } else {
+                $token = $tokens[$i];
+                $data = '';
+            }
+            if ($inquote) {
+                if ($token != '"') {
+                    continue;
+                } else {
+                    $inquote = false;
+                }
+            }
+            switch ($token) {
+                case T_WHITESPACE:
+                    continue;
+                case ';':
+                    if ($interface) {
+                        $current_function = '';
+                        $current_function_level = -1;
+                    }
+                    break;
+                case '"':
+                    $inquote = true;
+                    break;
+                case T_CURLY_OPEN:
+                case T_DOLLAR_OPEN_CURLY_BRACES:
+                case '{': $brace_level++; continue 2;
+                case '}':
+                    $brace_level--;
+                    if ($current_class_level == $brace_level) {
+                        $current_class = '';
+                        $current_class_level = -1;
+                    }
+                    if ($current_function_level == $brace_level) {
+                        $current_function = '';
+                        $current_function_level = -1;
+                    }
+                    continue 2;
+                case '[': $bracket_level++; continue 2;
+                case ']': $bracket_level--; continue 2;
+                case '(': $paren_level++;   continue 2;
+                case ')': $paren_level--;   continue 2;
+                case T_INTERFACE:
+                    $interface = true;
+                case T_CLASS:
+                    if (($current_class_level != -1) || ($current_function_level != -1)) {
+                        PEAR::raiseError("Parser error: Invalid PHP file $file",
+                            PEAR_COMMON_ERROR_INVALIDPHP);
+                        return false;
+                    }
+                case T_FUNCTION:
+                case T_NEW:
+                case T_EXTENDS:
+                case T_IMPLEMENTS:
+                    $look_for = $token;
+                    continue 2;
+                case T_STRING:
+                    if (version_compare(zend_version(), '2.0', '<')) {
+                        if (in_array(strtolower($data),
+                            array('public', 'private', 'protected', 'abstract',
+                                  'interface', 'implements', 'clone', 'throw') 
+                                 )) {
+                            PEAR::raiseError('Error: PHP5 packages must be packaged by php 5 PEAR');
+                            return false;
+                        }
+                    }
+                    if ($look_for == T_CLASS) {
+                        $current_class = $data;
+                        $current_class_level = $brace_level;
+                        $declared_classes[] = $current_class;
+                    } elseif ($look_for == T_INTERFACE) {
+                        $current_interface = $data;
+                        $current_class_level = $brace_level;
+                        $declared_interfaces[] = $current_interface;
+                    } elseif ($look_for == T_IMPLEMENTS) {
+                        $implements[$current_class] = $data;
+                    } elseif ($look_for == T_EXTENDS) {
+                        $extends[$current_class] = $data;
+                    } elseif ($look_for == T_FUNCTION) {
+                        if ($current_class) {
+                            $current_function = "$current_class::$data";
+                            $declared_methods[$current_class][] = $data;
+                        } elseif ($current_interface) {
+                            $current_function = "$current_interface::$data";
+                            $declared_methods[$current_interface][] = $data;
+                        } else {
+                            $current_function = $data;
+                            $declared_functions[] = $current_function;
+                        }
+                        $current_function_level = $brace_level;
+                        $m = array();
+                    } elseif ($look_for == T_NEW) {
+                        $used_classes[$data] = true;
+                    }
+                    $look_for = 0;
+                    continue 2;
+                case T_VARIABLE:
+                    $look_for = 0;
+                    continue 2;
+                case T_DOC_COMMENT:
+                case T_COMMENT:
+                    if (preg_match('!^/\*\*\s!', $data)) {
+                        $lastphpdoc = $data;
+                        if (preg_match_all('/@nodep\s+(\S+)/', $lastphpdoc, $m)) {
+                            $nodeps = array_merge($nodeps, $m[1]);
+                        }
+                    }
+                    continue 2;
+                case T_DOUBLE_COLON:
+                    if (!($tokens[$i - 1][0] == T_WHITESPACE || $tokens[$i - 1][0] == T_STRING)) {
+                        PEAR::raiseError("Parser error: Invalid PHP file $file",
+                            PEAR_COMMON_ERROR_INVALIDPHP);
+                        return false;
+                    }
+                    $class = $tokens[$i - 1][1];
+                    if (strtolower($class) != 'parent') {
+                        $used_classes[$class] = true;
+                    }
+                    continue 2;
+            }
+        }
+        return array(
+            "source_file" => $file,
+            "declared_classes" => $declared_classes,
+            "declared_interfaces" => $declared_interfaces,
+            "declared_methods" => $declared_methods,
+            "declared_functions" => $declared_functions,
+            "used_classes" => array_diff(array_keys($used_classes), $nodeps),
+            "inheritance" => $extends,
+            "implements" => $implements,
+            );
+    }
+
+    // }}}
+    // {{{  betterStates()
+
+    /**
+     * Return an array containing all of the states that are more stable than
+     * or equal to the passed in state
+     *
+     * @param string Release state
+     * @param boolean Determines whether to include $state in the list
+     * @return false|array False if $state is not a valid release state
+     */
+    function betterStates($state, $include = false)
+    {
+        static $states = array('snapshot', 'devel', 'alpha', 'beta', 'stable');
+        $i = array_search($state, $states);
+        if ($i === false) {
+            return false;
+        }
+        if ($include) {
+            $i--;
+        }
+        return array_slice($states, $i + 1);
+    }
+
+    // }}}
+    // {{{ detectDependencies()
+
+    function detectDependencies($any, $status_callback = null)
+    {
+        if (!function_exists("token_get_all")) {
+            return false;
+        }
+        if (PEAR::isError($info = $this->infoFromAny($any))) {
+            return $this->raiseError($info);
+        }
+        if (!is_array($info)) {
+            return false;
+        }
+        $deps = array();
+        $used_c = $decl_c = $decl_f = $decl_m = array();
+        foreach ($info['filelist'] as $file => $fa) {
+            $tmp = $this->analyzeSourceCode($file);
+            $used_c = @array_merge($used_c, $tmp['used_classes']);
+            $decl_c = @array_merge($decl_c, $tmp['declared_classes']);
+            $decl_f = @array_merge($decl_f, $tmp['declared_functions']);
+            $decl_m = @array_merge($decl_m, $tmp['declared_methods']);
+            $inheri = @array_merge($inheri, $tmp['inheritance']);
+        }
+        $used_c = array_unique($used_c);
+        $decl_c = array_unique($decl_c);
+        $undecl_c = array_diff($used_c, $decl_c);
+        return array('used_classes' => $used_c,
+                     'declared_classes' => $decl_c,
+                     'declared_methods' => $decl_m,
+                     'declared_functions' => $decl_f,
+                     'undeclared_classes' => $undecl_c,
+                     'inheritance' => $inheri,
+                     );
+    }
+
+    // }}}
+    // {{{ getUserRoles()
+
+    /**
+     * Get the valid roles for a PEAR package maintainer
+     *
+     * @return array
+     * @static
+     */
+    function getUserRoles()
+    {
+        return $GLOBALS['_PEAR_Common_maintainer_roles'];
+    }
+
+    // }}}
+    // {{{ getReleaseStates()
+
+    /**
+     * Get the valid package release states of packages
+     *
+     * @return array
+     * @static
+     */
+    function getReleaseStates()
+    {
+        return $GLOBALS['_PEAR_Common_release_states'];
+    }
+
+    // }}}
+    // {{{ getDependencyTypes()
+
+    /**
+     * Get the implemented dependency types (php, ext, pkg etc.)
+     *
+     * @return array
+     * @static
+     */
+    function getDependencyTypes()
+    {
+        return $GLOBALS['_PEAR_Common_dependency_types'];
+    }
+
+    // }}}
+    // {{{ getDependencyRelations()
+
+    /**
+     * Get the implemented dependency relations (has, lt, ge etc.)
+     *
+     * @return array
+     * @static
+     */
+    function getDependencyRelations()
+    {
+        return $GLOBALS['_PEAR_Common_dependency_relations'];
+    }
+
+    // }}}
+    // {{{ getFileRoles()
+
+    /**
+     * Get the implemented file roles
+     *
+     * @return array
+     * @static
+     */
+    function getFileRoles()
+    {
+        return $GLOBALS['_PEAR_Common_file_roles'];
+    }
+
+    // }}}
+    // {{{ getReplacementTypes()
+
+    /**
+     * Get the implemented file replacement types in
+     *
+     * @return array
+     * @static
+     */
+    function getReplacementTypes()
+    {
+        return $GLOBALS['_PEAR_Common_replacement_types'];
+    }
+
+    // }}}
+    // {{{ getProvideTypes()
+
+    /**
+     * Get the implemented file replacement types in
+     *
+     * @return array
+     * @static
+     */
+    function getProvideTypes()
+    {
+        return $GLOBALS['_PEAR_Common_provide_types'];
+    }
+
+    // }}}
+    // {{{ getScriptPhases()
+
+    /**
+     * Get the implemented file replacement types in
+     *
+     * @return array
+     * @static
+     */
+    function getScriptPhases()
+    {
+        return $GLOBALS['_PEAR_Common_script_phases'];
+    }
+
+    // }}}
+    // {{{ validPackageName()
+
+    /**
+     * Test whether a string contains a valid package name.
+     *
+     * @param string $name the package name to test
+     *
+     * @return bool
+     *
+     * @access public
+     */
+    function validPackageName($name)
+    {
+        return (bool)preg_match(PEAR_COMMON_PACKAGE_NAME_PREG, $name);
+    }
+
+
+    // }}}
+    // {{{ validPackageVersion()
+
+    /**
+     * Test whether a string contains a valid package version.
+     *
+     * @param string $ver the package version to test
+     *
+     * @return bool
+     *
+     * @access public
+     */
+    function validPackageVersion($ver)
+    {
+        return (bool)preg_match(PEAR_COMMON_PACKAGE_VERSION_PREG, $ver);
+    }
+
+
+    // }}}
+
+    // {{{ downloadHttp()
+
+    /**
+     * Download a file through HTTP.  Considers suggested file name in
+     * Content-disposition: header and can run a callback function for
+     * different events.  The callback will be called with two
+     * parameters: the callback type, and parameters.  The implemented
+     * callback types are:
+     *
+     *  'setup'       called at the very beginning, parameter is a UI object
+     *                that should be used for all output
+     *  'message'     the parameter is a string with an informational message
+     *  'saveas'      may be used to save with a different file name, the
+     *                parameter is the filename that is about to be used.
+     *                If a 'saveas' callback returns a non-empty string,
+     *                that file name will be used as the filename instead.
+     *                Note that $save_dir will not be affected by this, only
+     *                the basename of the file.
+     *  'start'       download is starting, parameter is number of bytes
+     *                that are expected, or -1 if unknown
+     *  'bytesread'   parameter is the number of bytes read so far
+     *  'done'        download is complete, parameter is the total number
+     *                of bytes read
+     *  'connfailed'  if the TCP connection fails, this callback is called
+     *                with array(host,port,errno,errmsg)
+     *  'writefailed' if writing to disk fails, this callback is called
+     *                with array(destfile,errmsg)
+     *
+     * If an HTTP proxy has been configured (http_proxy PEAR_Config
+     * setting), the proxy will be used.
+     *
+     * @param string  $url       the URL to download
+     * @param object  $ui        PEAR_Frontend_* instance
+     * @param object  $config    PEAR_Config instance
+     * @param string  $save_dir  (optional) directory to save file in
+     * @param mixed   $callback  (optional) function/method to call for status
+     *                           updates
+     *
+     * @return string  Returns the full path of the downloaded file or a PEAR
+     *                 error on failure.  If the error is caused by
+     *                 socket-related errors, the error object will
+     *                 have the fsockopen error code available through
+     *                 getCode().
+     *
+     * @access public
+     */
+    function downloadHttp($url, &$ui, $save_dir = '.', $callback = null)
+    {
+        if ($callback) {
+            call_user_func($callback, 'setup', array(&$ui));
+        }
+        if (preg_match('!^http://([^/:?#]*)(:(\d+))?(/.*)!', $url, $matches)) {
+            list(,$host,,$port,$path) = $matches;
+        }
+        if (isset($this)) {
+            $config = &$this->config;
+        } else {
+            $config = &PEAR_Config::singleton();
+        }
+        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+        if ($proxy = parse_url($config->get('http_proxy'))) {
+            $proxy_host = @$proxy['host'];
+            $proxy_port = @$proxy['port'];
+            $proxy_user = @$proxy['user'];
+            $proxy_pass = @$proxy['pass'];
+
+            if ($proxy_port == '') {
+                $proxy_port = 8080;
+            }
+            if ($callback) {
+                call_user_func($callback, 'message', "Using HTTP proxy $host:$port");
+            }
+        }
+        if (empty($port)) {
+            $port = 80;
+        }
+        if ($proxy_host != '') {
+            $fp = @fsockopen($proxy_host, $proxy_port, $errno, $errstr);
+            if (!$fp) {
+                if ($callback) {
+                    call_user_func($callback, 'connfailed', array($proxy_host, $proxy_port,
+                                                                  $errno, $errstr));
+                }
+                return PEAR::raiseError("Connection to `$proxy_host:$proxy_port' failed: $errstr", $errno);
+            }
+            $request = "GET $url HTTP/1.0\r\n";
+        } else {
+            $fp = @fsockopen($host, $port, $errno, $errstr);
+            if (!$fp) {
+                if ($callback) {
+                    call_user_func($callback, 'connfailed', array($host, $port,
+                                                                  $errno, $errstr));
+                }
+                return PEAR::raiseError("Connection to `$host:$port' failed: $errstr", $errno);
+            }
+            $request = "GET $path HTTP/1.0\r\n";
+        }
+        $request .= "Host: $host:$port\r\n".
+            "User-Agent: PHP/".PHP_VERSION."\r\n";
+        if ($proxy_host != '' && $proxy_user != '') {
+            $request .= 'Proxy-Authorization: Basic ' .
+                base64_encode($proxy_user . ':' . $proxy_pass) . "\r\n";
+        }
+        $request .= "\r\n";
+        fwrite($fp, $request);
+        $headers = array();
+        while (trim($line = fgets($fp, 1024))) {
+            if (preg_match('/^([^:]+):\s+(.*)\s*$/', $line, $matches)) {
+                $headers[strtolower($matches[1])] = trim($matches[2]);
+            } elseif (preg_match('|^HTTP/1.[01] ([0-9]{3}) |', $line, $matches)) {
+                if ($matches[1] != 200) {
+                    return PEAR::raiseError("File http://$host:$port$path not valid (received: $line)");
+                }
+            }
+        }
+        if (isset($headers['content-disposition']) &&
+            preg_match('/\sfilename=\"([^;]*\S)\"\s*(;|$)/', $headers['content-disposition'], $matches)) {
+            $save_as = basename($matches[1]);
+        } else {
+            $save_as = basename($url);
+        }
+        if ($callback) {
+            $tmp = call_user_func($callback, 'saveas', $save_as);
+            if ($tmp) {
+                $save_as = $tmp;
+            }
+        }
+        $dest_file = $save_dir . DIRECTORY_SEPARATOR . $save_as;
+        if (!$wp = @fopen($dest_file, 'wb')) {
+            fclose($fp);
+            if ($callback) {
+                call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+            }
+            return PEAR::raiseError("could not open $dest_file for writing");
+        }
+        if (isset($headers['content-length'])) {
+            $length = $headers['content-length'];
+        } else {
+            $length = -1;
+        }
+        $bytes = 0;
+        if ($callback) {
+            call_user_func($callback, 'start', array(basename($dest_file), $length));
+        }
+        while ($data = @fread($fp, 1024)) {
+            $bytes += strlen($data);
+            if ($callback) {
+                call_user_func($callback, 'bytesread', $bytes);
+            }
+            if (!@fwrite($wp, $data)) {
+                fclose($fp);
+                if ($callback) {
+                    call_user_func($callback, 'writefailed', array($dest_file, $php_errormsg));
+                }
+                return PEAR::raiseError("$dest_file: write failed ($php_errormsg)");
+            }
+        }
+        fclose($fp);
+        fclose($wp);
+        if ($callback) {
+            call_user_func($callback, 'done', $bytes);
+        }
+        return $dest_file;
+    }
+
+    // }}}
+    // {{{ sortPkgDeps()
+
+    /**
+     * Sort a list of arrays of array(downloaded packagefilename) by dependency.
+     *
+     * It also removes duplicate dependencies
+     * @param array
+     * @param boolean Sort packages in reverse order if true
+     * @return array array of array(packagefilename, package.xml contents)
+     */
+    function sortPkgDeps(&$packages, $uninstall = false)
+    {
+        $ret = array();
+        if ($uninstall) {
+            foreach($packages as $packageinfo) {
+                $ret[] = array('info' => $packageinfo);
+            }
+        } else {
+            foreach($packages as $packagefile) {
+                if (!is_array($packagefile)) {
+                    $ret[] = array('file' => $packagefile,
+                                   'info' => $a = $this->infoFromAny($packagefile),
+                                   'pkg' => $a['package']);
+                } else {
+                    $ret[] = $packagefile;
+                }
+            }
+        }
+        $checkdupes = array();
+        $newret = array();
+        foreach($ret as $i => $p) {
+            if (!isset($checkdupes[$p['info']['package']])) {
+                $checkdupes[$p['info']['package']][] = $i;
+                $newret[] = $p;
+            }
+        }
+        $this->_packageSortTree = $this->_getPkgDepTree($newret);
+
+        $func = $uninstall ? '_sortPkgDepsRev' : '_sortPkgDeps';
+        usort($newret, array(&$this, $func));
+        $this->_packageSortTree = null;
+        $packages = $newret;
+    }
+
+    // }}}
+    // {{{ _sortPkgDeps()
+
+    /**
+     * Compare two package's package.xml, and sort
+     * so that dependencies are installed first
+     *
+     * This is a crude compare, real dependency checking is done on install.
+     * The only purpose this serves is to make the command-line
+     * order-independent (you can list a dependent package first, and
+     * installation occurs in the order required)
+     * @access private
+     */
+    function _sortPkgDeps($p1, $p2)
+    {
+        $p1name = $p1['info']['package'];
+        $p2name = $p2['info']['package'];
+        $p1deps = $this->_getPkgDeps($p1);
+        $p2deps = $this->_getPkgDeps($p2);
+        if (!count($p1deps) && !count($p2deps)) {
+            return 0; // order makes no difference
+        }
+        if (!count($p1deps)) {
+            return -1; // package 2 has dependencies, package 1 doesn't
+        }
+        if (!count($p2deps)) {
+            return 1; // package 1 has dependencies, package 2 doesn't
+        }
+        // both have dependencies
+        if (in_array($p1name, $p2deps)) {
+            return -1; // put package 1 first: package 2 depends on package 1
+        }
+        if (in_array($p2name, $p1deps)) {
+            return 1; // put package 2 first: package 1 depends on package 2
+        }
+        if ($this->_removedDependency($p1name, $p2name)) {
+            return -1; // put package 1 first: package 2 depends on packages that depend on package 1
+        }
+        if ($this->_removedDependency($p2name, $p1name)) {
+            return 1; // put package 2 first: package 1 depends on packages that depend on package 2
+        }
+        // doesn't really matter if neither depends on the other
+        return 0;
+    }
+
+    // }}}
+    // {{{ _sortPkgDepsRev()
+
+    /**
+     * Compare two package's package.xml, and sort
+     * so that dependencies are uninstalled last
+     *
+     * This is a crude compare, real dependency checking is done on uninstall.
+     * The only purpose this serves is to make the command-line
+     * order-independent (you can list a dependency first, and
+     * uninstallation occurs in the order required)
+     * @access private
+     */
+    function _sortPkgDepsRev($p1, $p2)
+    {
+        $p1name = $p1['info']['package'];
+        $p2name = $p2['info']['package'];
+        $p1deps = $this->_getRevPkgDeps($p1);
+        $p2deps = $this->_getRevPkgDeps($p2);
+        if (!count($p1deps) && !count($p2deps)) {
+            return 0; // order makes no difference
+        }
+        if (!count($p1deps)) {
+            return 1; // package 2 has dependencies, package 1 doesn't
+        }
+        if (!count($p2deps)) {
+            return -1; // package 2 has dependencies, package 1 doesn't
+        }
+        // both have dependencies
+        if (in_array($p1name, $p2deps)) {
+            return 1; // put package 1 last
+        }
+        if (in_array($p2name, $p1deps)) {
+            return -1; // put package 2 last
+        }
+        if ($this->_removedDependency($p1name, $p2name)) {
+            return 1; // put package 1 last: package 2 depends on packages that depend on package 1
+        }
+        if ($this->_removedDependency($p2name, $p1name)) {
+            return -1; // put package 2 last: package 1 depends on packages that depend on package 2
+        }
+        // doesn't really matter if neither depends on the other
+        return 0;
+    }
+
+    // }}}
+    // {{{ _getPkgDeps()
+
+    /**
+     * get an array of package dependency names
+     * @param array
+     * @return array
+     * @access private
+     */
+    function _getPkgDeps($p)
+    {
+        if (!isset($p['info']['releases'])) {
+            return $this->_getRevPkgDeps($p);
+        }
+        $rel = array_shift($p['info']['releases']);
+        if (!isset($rel['deps'])) {
+            return array();
+        }
+        $ret = array();
+        foreach($rel['deps'] as $dep) {
+            if ($dep['type'] == 'pkg') {
+                $ret[] = $dep['name'];
+            }
+        }
+        return $ret;
+    }
+
+    // }}}
+    // {{{ _getPkgDeps()
+
+    /**
+     * get an array representation of the package dependency tree
+     * @return array
+     * @access private
+     */
+    function _getPkgDepTree($packages)
+    {
+        $tree = array();
+        foreach ($packages as $p) {
+            $package = $p['info']['package'];
+            $deps = $this->_getPkgDeps($p);
+            $tree[$package] = $deps;
+        }
+        return $tree;
+    }
+
+    // }}}
+    // {{{ _removedDependency($p1, $p2)
+
+    /**
+     * get an array of package dependency names for uninstall
+     * @param string package 1 name
+     * @param string package 2 name
+     * @return bool
+     * @access private
+     */
+    function _removedDependency($p1, $p2)
+    {
+        if (empty($this->_packageSortTree[$p2])) {
+            return false;
+        }
+        if (!in_array($p1, $this->_packageSortTree[$p2])) {
+            foreach ($this->_packageSortTree[$p2] as $potential) {
+                if ($this->_removedDependency($p1, $potential)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _getRevPkgDeps()
+
+    /**
+     * get an array of package dependency names for uninstall
+     * @param array
+     * @return array
+     * @access private
+     */
+    function _getRevPkgDeps($p)
+    {
+        if (!isset($p['info']['release_deps'])) {
+            return array();
+        }
+        $ret = array();
+        foreach($p['info']['release_deps'] as $dep) {
+            if ($dep['type'] == 'pkg') {
+                $ret[] = $dep['name'];
+            }
+        }
+        return $ret;
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Config.php b/3rdparty/PEAR/Config.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba641735250ddf68878f66ef40178707d5d30a60
--- /dev/null
+++ b/3rdparty/PEAR/Config.php
@@ -0,0 +1,1169 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Config.php,v 1.52 2004/01/08 17:33:12 sniper Exp $
+
+require_once 'PEAR.php';
+require_once 'System.php';
+
+/**
+ * Last created PEAR_Config instance.
+ * @var object
+ */
+$GLOBALS['_PEAR_Config_instance'] = null;
+if (!defined('PEAR_INSTALL_DIR') || !PEAR_INSTALL_DIR) {
+    $PEAR_INSTALL_DIR = PHP_LIBDIR . DIRECTORY_SEPARATOR . 'pear';
+} else {
+    $PEAR_INSTALL_DIR = PEAR_INSTALL_DIR;
+}
+
+// Below we define constants with default values for all configuration
+// parameters except username/password.  All of them can have their
+// defaults set through environment variables.  The reason we use the
+// PHP_ prefix is for some security, PHP protects environment
+// variables starting with PHP_*.
+
+if (getenv('PHP_PEAR_SYSCONF_DIR')) {
+    define('PEAR_CONFIG_SYSCONFDIR', getenv('PHP_PEAR_SYSCONF_DIR'));
+} elseif (getenv('SystemRoot')) {
+    define('PEAR_CONFIG_SYSCONFDIR', getenv('SystemRoot'));
+} else {
+    define('PEAR_CONFIG_SYSCONFDIR', PHP_SYSCONFDIR);
+}
+
+// Default for master_server
+if (getenv('PHP_PEAR_MASTER_SERVER')) {
+    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', getenv('PHP_PEAR_MASTER_SERVER'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_MASTER_SERVER', 'pear.php.net');
+}
+
+// Default for http_proxy
+if (getenv('PHP_PEAR_HTTP_PROXY')) {
+    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('PHP_PEAR_HTTP_PROXY'));
+} elseif (getenv('http_proxy')) {
+    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', getenv('http_proxy'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_HTTP_PROXY', '');
+}
+
+// Default for php_dir
+if (getenv('PHP_PEAR_INSTALL_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_PHP_DIR', getenv('PHP_PEAR_INSTALL_DIR'));
+} else {
+    if (@is_dir($PEAR_INSTALL_DIR)) {
+        define('PEAR_CONFIG_DEFAULT_PHP_DIR',
+               $PEAR_INSTALL_DIR);
+    } else {
+        define('PEAR_CONFIG_DEFAULT_PHP_DIR', $PEAR_INSTALL_DIR);
+    }
+}
+
+// Default for ext_dir
+if (getenv('PHP_PEAR_EXTENSION_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_EXT_DIR', getenv('PHP_PEAR_EXTENSION_DIR'));
+} else {
+    if (ini_get('extension_dir')) {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', ini_get('extension_dir'));
+    } elseif (defined('PEAR_EXTENSION_DIR') && @is_dir(PEAR_EXTENSION_DIR)) {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PEAR_EXTENSION_DIR);
+    } elseif (defined('PHP_EXTENSION_DIR')) {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', PHP_EXTENSION_DIR);
+    } else {
+        define('PEAR_CONFIG_DEFAULT_EXT_DIR', '.');
+    }
+}
+
+// Default for doc_dir
+if (getenv('PHP_PEAR_DOC_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_DOC_DIR', getenv('PHP_PEAR_DOC_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_DOC_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'docs');
+}
+
+// Default for bin_dir
+if (getenv('PHP_PEAR_BIN_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_BIN_DIR', getenv('PHP_PEAR_BIN_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_BIN_DIR', PHP_BINDIR);
+}
+
+// Default for data_dir
+if (getenv('PHP_PEAR_DATA_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_DATA_DIR', getenv('PHP_PEAR_DATA_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_DATA_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'data');
+}
+
+// Default for test_dir
+if (getenv('PHP_PEAR_TEST_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_TEST_DIR', getenv('PHP_PEAR_TEST_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_TEST_DIR',
+           $PEAR_INSTALL_DIR.DIRECTORY_SEPARATOR.'tests');
+}
+
+// Default for cache_dir
+if (getenv('PHP_PEAR_CACHE_DIR')) {
+    define('PEAR_CONFIG_DEFAULT_CACHE_DIR', getenv('PHP_PEAR_CACHE_DIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_CACHE_DIR',
+           System::tmpdir() . DIRECTORY_SEPARATOR . 'pear' .
+           DIRECTORY_SEPARATOR . 'cache');
+}
+
+// Default for php_bin
+if (getenv('PHP_PEAR_PHP_BIN')) {
+    define('PEAR_CONFIG_DEFAULT_PHP_BIN', getenv('PHP_PEAR_PHP_BIN'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_PHP_BIN', PEAR_CONFIG_DEFAULT_BIN_DIR.
+           DIRECTORY_SEPARATOR.'php'.(OS_WINDOWS ? '.exe' : ''));
+}
+
+// Default for verbose
+if (getenv('PHP_PEAR_VERBOSE')) {
+    define('PEAR_CONFIG_DEFAULT_VERBOSE', getenv('PHP_PEAR_VERBOSE'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_VERBOSE', 1);
+}
+
+// Default for preferred_state
+if (getenv('PHP_PEAR_PREFERRED_STATE')) {
+    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', getenv('PHP_PEAR_PREFERRED_STATE'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_PREFERRED_STATE', 'stable');
+}
+
+// Default for umask
+if (getenv('PHP_PEAR_UMASK')) {
+    define('PEAR_CONFIG_DEFAULT_UMASK', getenv('PHP_PEAR_UMASK'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_UMASK', decoct(umask()));
+}
+
+// Default for cache_ttl
+if (getenv('PHP_PEAR_CACHE_TTL')) {
+    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', getenv('PHP_PEAR_CACHE_TTL'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_CACHE_TTL', 3600);
+}
+
+// Default for sig_type
+if (getenv('PHP_PEAR_SIG_TYPE')) {
+    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', getenv('PHP_PEAR_SIG_TYPE'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_SIG_TYPE', 'gpg');
+}
+
+// Default for sig_bin
+if (getenv('PHP_PEAR_SIG_BIN')) {
+    define('PEAR_CONFIG_DEFAULT_SIG_BIN', getenv('PHP_PEAR_SIG_BIN'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_SIG_BIN',
+           System::which(
+               'gpg', OS_WINDOWS ? 'c:\gnupg\gpg.exe' : '/usr/local/bin/gpg'));
+}
+
+// Default for sig_keydir
+if (getenv('PHP_PEAR_SIG_KEYDIR')) {
+    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR', getenv('PHP_PEAR_SIG_KEYDIR'));
+} else {
+    define('PEAR_CONFIG_DEFAULT_SIG_KEYDIR',
+           PEAR_CONFIG_SYSCONFDIR . DIRECTORY_SEPARATOR . 'pearkeys');
+}
+
+/**
+ * This is a class for storing configuration data, keeping track of
+ * which are system-defined, user-defined or defaulted.
+ */
+class PEAR_Config extends PEAR
+{
+    // {{{ properties
+
+    /**
+     * Array of config files used.
+     *
+     * @var array layer => config file
+     */
+    var $files = array(
+        'system' => '',
+        'user' => '',
+        );
+
+    var $layers = array();
+
+    /**
+     * Configuration data, two-dimensional array where the first
+     * dimension is the config layer ('user', 'system' and 'default'),
+     * and the second dimension is keyname => value.
+     *
+     * The order in the first dimension is important!  Earlier
+     * layers will shadow later ones when a config value is
+     * requested (if a 'user' value exists, it will be returned first,
+     * then 'system' and finally 'default').
+     *
+     * @var array layer => array(keyname => value, ...)
+     */
+    var $configuration = array(
+        'user' => array(),
+        'system' => array(),
+        'default' => array(),
+        );
+
+    /**
+     * Information about the configuration data.  Stores the type,
+     * default value and a documentation string for each configuration
+     * value.
+     *
+     * @var array layer => array(infotype => value, ...)
+     */
+    var $configuration_info = array(
+        // Internet Access
+        'master_server' => array(
+            'type' => 'string',
+            'default' => 'pear.php.net',
+            'doc' => 'name of the main PEAR server',
+            'prompt' => 'PEAR server',
+            'group' => 'Internet Access',
+            ),
+        'http_proxy' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_HTTP_PROXY,
+            'doc' => 'HTTP proxy (host:port) to use when downloading packages',
+            'prompt' => 'HTTP Proxy Server Address',
+            'group' => 'Internet Access',
+            ),
+        // File Locations
+        'php_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_PHP_DIR,
+            'doc' => 'directory where .php files are installed',
+            'prompt' => 'PEAR directory',
+            'group' => 'File Locations',
+            ),
+        'ext_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_EXT_DIR,
+            'doc' => 'directory where loadable extensions are installed',
+            'prompt' => 'PHP extension directory',
+            'group' => 'File Locations',
+            ),
+        'doc_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_DOC_DIR,
+            'doc' => 'directory where documentation is installed',
+            'prompt' => 'PEAR documentation directory',
+            'group' => 'File Locations',
+            ),
+        'bin_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_BIN_DIR,
+            'doc' => 'directory where executables are installed',
+            'prompt' => 'PEAR executables directory',
+            'group' => 'File Locations',
+            ),
+        'data_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_DATA_DIR,
+            'doc' => 'directory where data files are installed',
+            'prompt' => 'PEAR data directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'test_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_TEST_DIR,
+            'doc' => 'directory where regression tests are installed',
+            'prompt' => 'PEAR test directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'cache_dir' => array(
+            'type' => 'directory',
+            'default' => PEAR_CONFIG_DEFAULT_CACHE_DIR,
+            'doc' => 'directory which is used for XMLRPC cache',
+            'prompt' => 'PEAR Installer cache directory',
+            'group' => 'File Locations (Advanced)',
+            ),
+        'php_bin' => array(
+            'type' => 'file',
+            'default' => PEAR_CONFIG_DEFAULT_PHP_BIN,
+            'doc' => 'PHP CLI/CGI binary for executing scripts',
+            'prompt' => 'PHP CLI/CGI binary',
+            'group' => 'File Locations (Advanced)',
+            ),
+        // Maintainers
+        'username' => array(
+            'type' => 'string',
+            'default' => '',
+            'doc' => '(maintainers) your PEAR account name',
+            'prompt' => 'PEAR username (for maintainers)',
+            'group' => 'Maintainers',
+            ),
+        'password' => array(
+            'type' => 'password',
+            'default' => '',
+            'doc' => '(maintainers) your PEAR account password',
+            'prompt' => 'PEAR password (for maintainers)',
+            'group' => 'Maintainers',
+            ),
+        // Advanced
+        'verbose' => array(
+            'type' => 'integer',
+            'default' => PEAR_CONFIG_DEFAULT_VERBOSE,
+            'doc' => 'verbosity level
+0: really quiet
+1: somewhat quiet
+2: verbose
+3: debug',
+            'prompt' => 'Debug Log Level',
+            'group' => 'Advanced',
+            ),
+        'preferred_state' => array(
+            'type' => 'set',
+            'default' => PEAR_CONFIG_DEFAULT_PREFERRED_STATE,
+            'doc' => 'the installer will prefer releases with this state when installing packages without a version or state specified',
+            'valid_set' => array(
+                'stable', 'beta', 'alpha', 'devel', 'snapshot'),
+            'prompt' => 'Preferred Package State',
+            'group' => 'Advanced',
+            ),
+        'umask' => array(
+            'type' => 'mask',
+            'default' => PEAR_CONFIG_DEFAULT_UMASK,
+            'doc' => 'umask used when creating files (Unix-like systems only)',
+            'prompt' => 'Unix file mask',
+            'group' => 'Advanced',
+            ),
+        'cache_ttl' => array(
+            'type' => 'integer',
+            'default' => PEAR_CONFIG_DEFAULT_CACHE_TTL,
+            'doc' => 'amount of secs where the local cache is used and not updated',
+            'prompt' => 'Cache TimeToLive',
+            'group' => 'Advanced',
+            ),
+        'sig_type' => array(
+            'type' => 'set',
+            'default' => PEAR_CONFIG_DEFAULT_SIG_TYPE,
+            'doc' => 'which package signature mechanism to use',
+            'valid_set' => array('gpg'),
+            'prompt' => 'Package Signature Type',
+            'group' => 'Maintainers',
+            ),
+        'sig_bin' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_SIG_BIN,
+            'doc' => 'which package signature mechanism to use',
+            'prompt' => 'Signature Handling Program',
+            'group' => 'Maintainers',
+            ),
+        'sig_keyid' => array(
+            'type' => 'string',
+            'default' => '',
+            'doc' => 'which key to use for signing with',
+            'prompt' => 'Signature Key Id',
+            'group' => 'Maintainers',
+            ),
+        'sig_keydir' => array(
+            'type' => 'string',
+            'default' => PEAR_CONFIG_DEFAULT_SIG_KEYDIR,
+            'doc' => 'which package signature mechanism to use',
+            'prompt' => 'Signature Key Directory',
+            'group' => 'Maintainers',
+            ),
+        );
+
+    // }}}
+
+    // {{{ PEAR_Config([file], [defaults_file])
+
+    /**
+     * Constructor.
+     *
+     * @param string (optional) file to read user-defined options from
+     * @param string (optional) file to read system-wide defaults from
+     *
+     * @access public
+     *
+     * @see PEAR_Config::singleton
+     */
+    function PEAR_Config($user_file = '', $system_file = '')
+    {
+        $this->PEAR();
+        $sl = DIRECTORY_SEPARATOR;
+        if (empty($user_file)) {
+            if (OS_WINDOWS) {
+                $user_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.ini';
+            } else {
+                $user_file = getenv('HOME') . $sl . '.pearrc';
+            }
+        }
+        if (empty($system_file)) {
+            if (OS_WINDOWS) {
+                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pearsys.ini';
+            } else {
+                $system_file = PEAR_CONFIG_SYSCONFDIR . $sl . 'pear.conf';
+            }
+        }
+        $this->layers = array_keys($this->configuration);
+        $this->files['user'] = $user_file;
+        $this->files['system'] = $system_file;
+        if ($user_file && file_exists($user_file)) {
+            $this->readConfigFile($user_file);
+        }
+        if ($system_file && file_exists($system_file)) {
+            $this->mergeConfigFile($system_file, false, 'system');
+        }
+        foreach ($this->configuration_info as $key => $info) {
+            $this->configuration['default'][$key] = $info['default'];
+        }
+        //$GLOBALS['_PEAR_Config_instance'] = &$this;
+    }
+
+    // }}}
+    // {{{ singleton([file], [defaults_file])
+
+    /**
+     * Static singleton method.  If you want to keep only one instance
+     * of this class in use, this method will give you a reference to
+     * the last created PEAR_Config object if one exists, or create a
+     * new object.
+     *
+     * @param string (optional) file to read user-defined options from
+     * @param string (optional) file to read system-wide defaults from
+     *
+     * @return object an existing or new PEAR_Config instance
+     *
+     * @access public
+     *
+     * @see PEAR_Config::PEAR_Config
+     */
+    function &singleton($user_file = '', $system_file = '')
+    {
+        if (is_object($GLOBALS['_PEAR_Config_instance'])) {
+            return $GLOBALS['_PEAR_Config_instance'];
+        }
+        $GLOBALS['_PEAR_Config_instance'] =
+             &new PEAR_Config($user_file, $system_file);
+        return $GLOBALS['_PEAR_Config_instance'];
+    }
+
+    // }}}
+    // {{{ readConfigFile([file], [layer])
+
+    /**
+     * Reads configuration data from a file.  All existing values in
+     * the config layer are discarded and replaced with data from the
+     * file.
+     *
+     * @param string (optional) file to read from, if NULL or not
+     * specified, the last-used file for the same layer (second param)
+     * is used
+     *
+     * @param string (optional) config layer to insert data into
+     * ('user' or 'system')
+     *
+     * @return bool TRUE on success or a PEAR error on failure
+     *
+     * @access public
+     */
+    function readConfigFile($file = null, $layer = 'user')
+    {
+        if (empty($this->files[$layer])) {
+            return $this->raiseError("unknown config file type `$layer'");
+        }
+        if ($file === null) {
+            $file = $this->files[$layer];
+        }
+        $data = $this->_readConfigDataFrom($file);
+        if (PEAR::isError($data)) {
+            return $data;
+        }
+        $this->_decodeInput($data);
+        $this->configuration[$layer] = $data;
+        return true;
+    }
+
+    // }}}
+    // {{{ mergeConfigFile(file, [override], [layer])
+
+    /**
+     * Merges data into a config layer from a file.  Does the same
+     * thing as readConfigFile, except it does not replace all
+     * existing values in the config layer.
+     *
+     * @param string file to read from
+     *
+     * @param bool (optional) whether to overwrite existing data
+     * (default TRUE)
+     *
+     * @param string config layer to insert data into ('user' or
+     * 'system')
+     *
+     * @return bool TRUE on success or a PEAR error on failure
+     *
+     * @access public.
+     */
+    function mergeConfigFile($file, $override = true, $layer = 'user')
+    {
+        if (empty($this->files[$layer])) {
+            return $this->raiseError("unknown config file type `$layer'");
+        }
+        if ($file === null) {
+            $file = $this->files[$layer];
+        }
+        $data = $this->_readConfigDataFrom($file);
+        if (PEAR::isError($data)) {
+            return $data;
+        }
+        $this->_decodeInput($data);
+        if ($override) {
+            $this->configuration[$layer] = array_merge($this->configuration[$layer], $data);
+        } else {
+            $this->configuration[$layer] = array_merge($data, $this->configuration[$layer]);
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ writeConfigFile([file], [layer])
+
+    /**
+     * Writes data into a config layer from a file.
+     *
+     * @param string file to read from
+     *
+     * @param bool (optional) whether to overwrite existing data
+     * (default TRUE)
+     *
+     * @param string config layer to insert data into ('user' or
+     * 'system')
+     *
+     * @return bool TRUE on success or a PEAR error on failure
+     *
+     * @access public.
+     */
+    function writeConfigFile($file = null, $layer = 'user', $data = null)
+    {
+        if ($layer == 'both' || $layer == 'all') {
+            foreach ($this->files as $type => $file) {
+                $err = $this->writeConfigFile($file, $type, $data);
+                if (PEAR::isError($err)) {
+                    return $err;
+                }
+            }
+            return true;
+        }
+        if (empty($this->files[$layer])) {
+            return $this->raiseError("unknown config file type `$layer'");
+        }
+        if ($file === null) {
+            $file = $this->files[$layer];
+        }
+        $data = ($data === null) ? $this->configuration[$layer] : $data;
+        $this->_encodeOutput($data);
+        $opt = array('-p', dirname($file));
+        if (!@System::mkDir($opt)) {
+            return $this->raiseError("could not create directory: " . dirname($file));
+        }
+        if (@is_file($file) && !@is_writeable($file)) {
+            return $this->raiseError("no write access to $file!");
+        }
+        $fp = @fopen($file, "w");
+        if (!$fp) {
+            return $this->raiseError("PEAR_Config::writeConfigFile fopen('$file','w') failed");
+        }
+        $contents = "#PEAR_Config 0.9\n" . serialize($data);
+        if (!@fwrite($fp, $contents)) {
+            return $this->raiseError("PEAR_Config::writeConfigFile: fwrite failed");
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _readConfigDataFrom(file)
+
+    /**
+     * Reads configuration data from a file and returns the parsed data
+     * in an array.
+     *
+     * @param string file to read from
+     *
+     * @return array configuration data or a PEAR error on failure
+     *
+     * @access private
+     */
+    function _readConfigDataFrom($file)
+    {
+        $fp = @fopen($file, "r");
+        if (!$fp) {
+            return $this->raiseError("PEAR_Config::readConfigFile fopen('$file','r') failed");
+        }
+        $size = filesize($file);
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        $contents = fread($fp, $size);
+        set_magic_quotes_runtime($rt);
+        fclose($fp);
+        $version = '0.1';
+        if (preg_match('/^#PEAR_Config\s+(\S+)\s+/si', $contents, $matches)) {
+            $version = $matches[1];
+            $contents = substr($contents, strlen($matches[0]));
+        }
+        if (version_compare("$version", '1', '<')) {
+            $data = unserialize($contents);
+            if (!is_array($data)) {
+                if (strlen(trim($contents)) > 0) {
+                    $error = "PEAR_Config: bad data in $file";
+//                if (isset($this)) {
+                    return $this->raiseError($error);
+//                } else {
+//                    return PEAR::raiseError($error);
+                } else {
+                    $data = array();
+                }
+            }
+        // add parsing of newer formats here...
+        } else {
+            return $this->raiseError("$file: unknown version `$version'");
+        }
+        return $data;
+    }
+
+    // }}}
+    // {{{ getConfFile(layer)
+    /**
+    * Gets the file used for storing the config for a layer
+    *
+    * @param string $layer 'user' or 'system'
+    */
+
+    function getConfFile($layer)
+    {
+        return $this->files[$layer];
+    }
+
+    // }}}
+    // {{{ _encodeOutput(&data)
+
+    /**
+     * Encodes/scrambles configuration data before writing to files.
+     * Currently, 'password' values will be base64-encoded as to avoid
+     * that people spot cleartext passwords by accident.
+     *
+     * @param array (reference) array to encode values in
+     *
+     * @return bool TRUE on success
+     *
+     * @access private
+     */
+    function _encodeOutput(&$data)
+    {
+        foreach ($data as $key => $value) {
+            if (!isset($this->configuration_info[$key])) {
+                continue;
+            }
+            $type = $this->configuration_info[$key]['type'];
+            switch ($type) {
+                // we base64-encode passwords so they are at least
+                // not shown in plain by accident
+                case 'password': {
+                    $data[$key] = base64_encode($data[$key]);
+                    break;
+                }
+                case 'mask': {
+                    $data[$key] = octdec($data[$key]);
+                    break;
+                }
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _decodeInput(&data)
+
+    /**
+     * Decodes/unscrambles configuration data after reading from files.
+     *
+     * @param array (reference) array to encode values in
+     *
+     * @return bool TRUE on success
+     *
+     * @access private
+     *
+     * @see PEAR_Config::_encodeOutput
+     */
+    function _decodeInput(&$data)
+    {
+        if (!is_array($data)) {
+            return true;
+        }
+        foreach ($data as $key => $value) {
+            if (!isset($this->configuration_info[$key])) {
+                continue;
+            }
+            $type = $this->configuration_info[$key]['type'];
+            switch ($type) {
+                case 'password': {
+                    $data[$key] = base64_decode($data[$key]);
+                    break;
+                }
+                case 'mask': {
+                    $data[$key] = decoct($data[$key]);
+                    break;
+                }
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ get(key, [layer])
+
+    /**
+     * Returns a configuration value, prioritizing layers as per the
+     * layers property.
+     *
+     * @param string config key
+     *
+     * @return mixed the config value, or NULL if not found
+     *
+     * @access public
+     */
+    function get($key, $layer = null)
+    {
+        if ($layer === null) {
+            foreach ($this->layers as $layer) {
+                if (isset($this->configuration[$layer][$key])) {
+                    return $this->configuration[$layer][$key];
+                }
+            }
+        } elseif (isset($this->configuration[$layer][$key])) {
+            return $this->configuration[$layer][$key];
+        }
+        return null;
+    }
+
+    // }}}
+    // {{{ set(key, value, [layer])
+
+    /**
+     * Set a config value in a specific layer (defaults to 'user').
+     * Enforces the types defined in the configuration_info array.  An
+     * integer config variable will be cast to int, and a set config
+     * variable will be validated against its legal values.
+     *
+     * @param string config key
+     *
+     * @param string config value
+     *
+     * @param string (optional) config layer
+     *
+     * @return bool TRUE on success, FALSE on failure
+     *
+     * @access public
+     */
+    function set($key, $value, $layer = 'user')
+    {
+        if (empty($this->configuration_info[$key])) {
+            return false;
+        }
+        extract($this->configuration_info[$key]);
+        switch ($type) {
+            case 'integer':
+                $value = (int)$value;
+                break;
+            case 'set': {
+                // If a valid_set is specified, require the value to
+                // be in the set.  If there is no valid_set, accept
+                // any value.
+                if ($valid_set) {
+                    reset($valid_set);
+                    if ((key($valid_set) === 0 && !in_array($value, $valid_set)) ||
+                        (key($valid_set) !== 0 && empty($valid_set[$value])))
+                    {
+                        return false;
+                    }
+                }
+                break;
+            }
+        }
+        $this->configuration[$layer][$key] = $value;
+        return true;
+    }
+
+    // }}}
+    // {{{ getType(key)
+
+    /**
+     * Get the type of a config value.
+     *
+     * @param string  config key
+     *
+     * @return string type, one of "string", "integer", "file",
+     * "directory", "set" or "password".
+     *
+     * @access public
+     *
+     */
+    function getType($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['type'];
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ getDocs(key)
+
+    /**
+     * Get the documentation for a config value.
+     *
+     * @param string  config key
+     *
+     * @return string documentation string
+     *
+     * @access public
+     *
+     */
+    function getDocs($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['doc'];
+        }
+        return false;
+    }
+       // }}}
+    // {{{ getPrompt(key)
+
+    /**
+     * Get the short documentation for a config value.
+     *
+     * @param string  config key
+     *
+     * @return string short documentation string
+     *
+     * @access public
+     *
+     */
+    function getPrompt($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['prompt'];
+        }
+        return false;
+    }
+    // }}}
+    // {{{ getGroup(key)
+
+    /**
+     * Get the parameter group for a config key.
+     *
+     * @param string  config key
+     *
+     * @return string parameter group
+     *
+     * @access public
+     *
+     */
+    function getGroup($key)
+    {
+        if (isset($this->configuration_info[$key])) {
+            return $this->configuration_info[$key]['group'];
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ getGroups()
+
+    /**
+     * Get the list of parameter groups.
+     *
+     * @return array list of parameter groups
+     *
+     * @access public
+     *
+     */
+    function getGroups()
+    {
+        $tmp = array();
+        foreach ($this->configuration_info as $key => $info) {
+            $tmp[$info['group']] = 1;
+        }
+        return array_keys($tmp);
+    }
+
+    // }}}
+    // {{{ getGroupKeys()
+
+    /**
+     * Get the list of the parameters in a group.
+     *
+     * @param string $group parameter group
+     *
+     * @return array list of parameters in $group
+     *
+     * @access public
+     *
+     */
+    function getGroupKeys($group)
+    {
+        $keys = array();
+        foreach ($this->configuration_info as $key => $info) {
+            if ($info['group'] == $group) {
+                $keys[] = $key;
+            }
+        }
+        return $keys;
+    }
+
+    // }}}
+    // {{{ getSetValues(key)
+
+    /**
+     * Get the list of allowed set values for a config value.  Returns
+     * NULL for config values that are not sets.
+     *
+     * @param string  config key
+     *
+     * @return array enumerated array of set values, or NULL if the
+     *               config key is unknown or not a set
+     *
+     * @access public
+     *
+     */
+    function getSetValues($key)
+    {
+        if (isset($this->configuration_info[$key]) &&
+            isset($this->configuration_info[$key]['type']) &&
+            $this->configuration_info[$key]['type'] == 'set')
+        {
+            $valid_set = $this->configuration_info[$key]['valid_set'];
+            reset($valid_set);
+            if (key($valid_set) === 0) {
+                return $valid_set;
+            }
+            return array_keys($valid_set);
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ getKeys()
+
+    /**
+     * Get all the current config keys.
+     *
+     * @return array simple array of config keys
+     *
+     * @access public
+     */
+    function getKeys()
+    {
+        $keys = array();
+        foreach ($this->layers as $layer) {
+            $keys = array_merge($keys, $this->configuration[$layer]);
+        }
+        return array_keys($keys);
+    }
+
+    // }}}
+    // {{{ remove(key, [layer])
+
+    /**
+     * Remove the a config key from a specific config layer.
+     *
+     * @param string config key
+     *
+     * @param string (optional) config layer
+     *
+     * @return bool TRUE on success, FALSE on failure
+     *
+     * @access public
+     */
+    function remove($key, $layer = 'user')
+    {
+        if (isset($this->configuration[$layer][$key])) {
+            unset($this->configuration[$layer][$key]);
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ removeLayer(layer)
+
+    /**
+     * Temporarily remove an entire config layer.  USE WITH CARE!
+     *
+     * @param string config key
+     *
+     * @param string (optional) config layer
+     *
+     * @return bool TRUE on success, FALSE on failure
+     *
+     * @access public
+     */
+    function removeLayer($layer)
+    {
+        if (isset($this->configuration[$layer])) {
+            $this->configuration[$layer] = array();
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ store([layer])
+
+    /**
+     * Stores configuration data in a layer.
+     *
+     * @param string config layer to store
+     *
+     * @return bool TRUE on success, or PEAR error on failure
+     *
+     * @access public
+     */
+    function store($layer = 'user', $data = null)
+    {
+        return $this->writeConfigFile(null, $layer, $data);
+    }
+
+    // }}}
+    // {{{ toDefault(key)
+
+    /**
+     * Unset the user-defined value of a config key, reverting the
+     * value to the system-defined one.
+     *
+     * @param string config key
+     *
+     * @return bool TRUE on success, FALSE on failure
+     *
+     * @access public
+     */
+    function toDefault($key)
+    {
+        trigger_error("PEAR_Config::toDefault() deprecated, use PEAR_Config::remove() instead", E_USER_NOTICE);
+        return $this->remove($key, 'user');
+    }
+
+    // }}}
+    // {{{ definedBy(key)
+
+    /**
+     * Tells what config layer that gets to define a key.
+     *
+     * @param string config key
+     *
+     * @return string the config layer, or an empty string if not found
+     *
+     * @access public
+     */
+    function definedBy($key)
+    {
+        foreach ($this->layers as $layer) {
+            if (isset($this->configuration[$layer][$key])) {
+                return $layer;
+            }
+        }
+        return '';
+    }
+
+    // }}}
+    // {{{ isDefaulted(key)
+
+    /**
+     * Tells whether a config value has a system-defined value.
+     *
+     * @param string   config key
+     *
+     * @return bool
+     *
+     * @access public
+     *
+     * @deprecated
+     */
+    function isDefaulted($key)
+    {
+        trigger_error("PEAR_Config::isDefaulted() deprecated, use PEAR_Config::definedBy() instead", E_USER_NOTICE);
+        return $this->definedBy($key) == 'system';
+    }
+
+    // }}}
+    // {{{ isDefined(key)
+
+    /**
+     * Tells whether a given key exists as a config value.
+     *
+     * @param string config key
+     *
+     * @return bool whether <config key> exists in this object
+     *
+     * @access public
+     */
+    function isDefined($key)
+    {
+        foreach ($this->layers as $layer) {
+            if (isset($this->configuration[$layer][$key])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ isDefinedLayer(key)
+
+    /**
+     * Tells whether a given config layer exists.
+     *
+     * @param string config layer
+     *
+     * @return bool whether <config layer> exists in this object
+     *
+     * @access public
+     */
+    function isDefinedLayer($layer)
+    {
+        return isset($this->configuration[$layer]);
+    }
+
+    // }}}
+    // {{{ getLayers()
+
+    /**
+     * Returns the layers defined (except the 'default' one)
+     *
+     * @return array of the defined layers
+     */
+    function getLayers()
+    {
+        $cf = $this->configuration;
+        unset($cf['default']);
+        return array_keys($cf);
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Dependency.php b/3rdparty/PEAR/Dependency.php
new file mode 100644
index 0000000000000000000000000000000000000000..705167aa70fa158d03884b188ccadb3db8eca505
--- /dev/null
+++ b/3rdparty/PEAR/Dependency.php
@@ -0,0 +1,487 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com>                             |
+// |          Stig Bakken <ssb@php.net>                                   |
+// +----------------------------------------------------------------------+
+//
+// $Id: Dependency.php,v 1.36.4.1 2004/12/27 07:04:19 cellog Exp $
+
+require_once "PEAR.php";
+
+define('PEAR_DEPENDENCY_MISSING',        -1);
+define('PEAR_DEPENDENCY_CONFLICT',       -2);
+define('PEAR_DEPENDENCY_UPGRADE_MINOR',  -3);
+define('PEAR_DEPENDENCY_UPGRADE_MAJOR',  -4);
+define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5);
+define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6);
+define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL',       -7);
+define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL',  -8);
+define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL',  -9);
+
+/**
+ * Dependency check for PEAR packages
+ *
+ * The class is based on the dependency RFC that can be found at
+ * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1
+ *
+ * @author Tomas V.V.Vox <cox@idecnet.com>
+ * @author Stig Bakken <ssb@php.net>
+ */
+class PEAR_Dependency
+{
+    // {{{ constructor
+    /**
+     * Constructor
+     *
+     * @access public
+     * @param  object Registry object
+     * @return void
+     */
+    function PEAR_Dependency(&$registry)
+    {
+        $this->registry = &$registry;
+    }
+
+    // }}}
+    // {{{ callCheckMethod()
+
+    /**
+    * This method maps the XML dependency definition to the
+    * corresponding one from PEAR_Dependency
+    *
+    * <pre>
+    * $opts => Array
+    *    (
+    *        [type] => pkg
+    *        [rel] => ge
+    *        [version] => 3.4
+    *        [name] => HTML_Common
+    *        [optional] => false
+    *    )
+    * </pre>
+    *
+    * @param  string Error message
+    * @param  array  Options
+    * @return boolean
+    */
+    function callCheckMethod(&$errmsg, $opts)
+    {
+        $rel = isset($opts['rel']) ? $opts['rel'] : 'has';
+        $req = isset($opts['version']) ? $opts['version'] : null;
+        $name = isset($opts['name']) ? $opts['name'] : null;
+        $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ?
+            $opts['optional'] : null;
+        $errmsg = '';
+        switch ($opts['type']) {
+            case 'pkg':
+                return $this->checkPackage($errmsg, $name, $req, $rel, $opt);
+                break;
+            case 'ext':
+                return $this->checkExtension($errmsg, $name, $req, $rel, $opt);
+                break;
+            case 'php':
+                return $this->checkPHP($errmsg, $req, $rel);
+                break;
+            case 'prog':
+                return $this->checkProgram($errmsg, $name);
+                break;
+            case 'os':
+                return $this->checkOS($errmsg, $name);
+                break;
+            case 'sapi':
+                return $this->checkSAPI($errmsg, $name);
+                break;
+            case 'zend':
+                return $this->checkZend($errmsg, $name);
+                break;
+            default:
+                return "'{$opts['type']}' dependency type not supported";
+        }
+    }
+
+    // }}}
+    // {{{ checkPackage()
+
+    /**
+     * Package dependencies check method
+     *
+     * @param string $errmsg    Empty string, it will be populated with an error message, if any
+     * @param string $name      Name of the package to test
+     * @param string $req       The package version required
+     * @param string $relation  How to compare versions with each other
+     * @param bool   $opt       Whether the relationship is optional
+     *
+     * @return mixed bool false if no error or the error string
+     */
+    function checkPackage(&$errmsg, $name, $req = null, $relation = 'has',
+                          $opt = false)
+    {
+        if (is_string($req) && substr($req, 0, 2) == 'v.') {
+            $req = substr($req, 2);
+        }
+        switch ($relation) {
+            case 'has':
+                if (!$this->registry->packageExists($name)) {
+                    if ($opt) {
+                        $errmsg = "package `$name' is recommended to utilize some features.";
+                        return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+                    }
+                    $errmsg = "requires package `$name'";
+                    return PEAR_DEPENDENCY_MISSING;
+                }
+                return false;
+            case 'not':
+                if ($this->registry->packageExists($name)) {
+                    $errmsg = "conflicts with package `$name'";
+                    return PEAR_DEPENDENCY_CONFLICT;
+                }
+                return false;
+            case 'lt':
+            case 'le':
+            case 'eq':
+            case 'ne':
+            case 'ge':
+            case 'gt':
+                $version = $this->registry->packageInfo($name, 'version');
+                if (!$this->registry->packageExists($name)
+                    || !version_compare("$version", "$req", $relation))
+                {
+                    $code = $this->codeFromRelation($relation, $version, $req, $opt);
+                    if ($opt) {
+                        $errmsg = "package `$name' version " . $this->signOperator($relation) .
+                            " $req is recommended to utilize some features.";
+                        if ($version) {
+                            $errmsg .= "  Installed version is $version";
+                        }
+                        return $code;
+                    }
+                    $errmsg = "requires package `$name' " .
+                        $this->signOperator($relation) . " $req";
+                    return $code;
+                }
+                return false;
+        }
+        $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$name)";
+        return PEAR_DEPENDENCY_BAD_DEPENDENCY;
+    }
+
+    // }}}
+    // {{{ checkPackageUninstall()
+
+    /**
+     * Check package dependencies on uninstall
+     *
+     * @param string $error     The resultant error string
+     * @param string $warning   The resultant warning string
+     * @param string $name      Name of the package to test
+     *
+     * @return bool true if there were errors
+     */
+    function checkPackageUninstall(&$error, &$warning, $package)
+    {
+        $error = null;
+        $packages = $this->registry->listPackages();
+        foreach ($packages as $pkg) {
+            if ($pkg == $package) {
+                continue;
+            }
+            $deps = $this->registry->packageInfo($pkg, 'release_deps');
+            if (empty($deps)) {
+                continue;
+            }
+            foreach ($deps as $dep) {
+                if ($dep['type'] == 'pkg' && strcasecmp($dep['name'], $package) == 0) {
+                    if ($dep['rel'] == 'ne' || $dep['rel'] == 'not') {
+                        continue;
+                    }
+                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+                        $warning .= "\nWarning: Package '$pkg' optionally depends on '$package'";
+                    } else {
+                        $error .= "Package '$pkg' depends on '$package'\n";
+                    }
+                }
+            }
+        }
+        return ($error) ? true : false;
+    }
+
+    // }}}
+    // {{{ checkExtension()
+
+    /**
+     * Extension dependencies check method
+     *
+     * @param string $name        Name of the extension to test
+     * @param string $req_ext_ver Required extension version to compare with
+     * @param string $relation    How to compare versions with eachother
+     * @param bool   $opt         Whether the relationship is optional
+     *
+     * @return mixed bool false if no error or the error string
+     */
+    function checkExtension(&$errmsg, $name, $req = null, $relation = 'has',
+        $opt = false)
+    {
+        if ($relation == 'not') {
+            if (extension_loaded($name)) {
+                $errmsg = "conflicts with  PHP extension '$name'";
+                return PEAR_DEPENDENCY_CONFLICT;
+            } else {
+                return false;
+            }
+        }
+
+        if (!extension_loaded($name)) {
+            if ($relation == 'not') {
+                return false;
+            }
+            if ($opt) {
+                $errmsg = "'$name' PHP extension is recommended to utilize some features";
+                return PEAR_DEPENDENCY_MISSING_OPTIONAL;
+            }
+            $errmsg = "'$name' PHP extension is not installed";
+            return PEAR_DEPENDENCY_MISSING;
+        }
+        if ($relation == 'has') {
+            return false;
+        }
+        $code = false;
+        if (is_string($req) && substr($req, 0, 2) == 'v.') {
+            $req = substr($req, 2);
+        }
+        $ext_ver = phpversion($name);
+        $operator = $relation;
+        // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90)
+        if (!version_compare("$ext_ver", "$req", $operator)) {
+            $errmsg = "'$name' PHP extension version " .
+                $this->signOperator($operator) . " $req is required";
+            $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt);
+            if ($opt) {
+                $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) .
+                    " $req is recommended to utilize some features";
+                return $code;
+            }
+        }
+        return $code;
+    }
+
+    // }}}
+    // {{{ checkOS()
+
+    /**
+     * Operating system  dependencies check method
+     *
+     * @param string $os  Name of the operating system
+     *
+     * @return mixed bool false if no error or the error string
+     */
+    function checkOS(&$errmsg, $os)
+    {
+        // XXX Fixme: Implement a more flexible way, like
+        // comma separated values or something similar to PEAR_OS
+        static $myos;
+        if (empty($myos)) {
+            include_once "OS/Guess.php";
+            $myos = new OS_Guess();
+        }
+        // only 'has' relation is currently supported
+        if ($myos->matchSignature($os)) {
+            return false;
+        }
+        $errmsg = "'$os' operating system not supported";
+        return PEAR_DEPENDENCY_CONFLICT;
+    }
+
+    // }}}
+    // {{{ checkPHP()
+
+    /**
+     * PHP version check method
+     *
+     * @param string $req   which version to compare
+     * @param string $relation  how to compare the version
+     *
+     * @return mixed bool false if no error or the error string
+     */
+    function checkPHP(&$errmsg, $req, $relation = 'ge')
+    {
+        // this would be a bit stupid, but oh well :)
+        if ($relation == 'has') {
+            return false;
+        }
+        if ($relation == 'not') {
+            $errmsg = 'Invalid dependency - "not" is not allowed for php dependencies, ' .
+                'php cannot conflict with itself';
+            return PEAR_DEPENDENCY_BAD_DEPENDENCY;
+        }
+        if (substr($req, 0, 2) == 'v.') {
+            $req = substr($req,2, strlen($req) - 2);
+        }
+        $php_ver = phpversion();
+        $operator = $relation;
+        if (!version_compare("$php_ver", "$req", $operator)) {
+            $errmsg = "PHP version " . $this->signOperator($operator) .
+                " $req is required";
+            return PEAR_DEPENDENCY_CONFLICT;
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ checkProgram()
+
+    /**
+     * External program check method.  Looks for executable files in
+     * directories listed in the PATH environment variable.
+     *
+     * @param string $program   which program to look for
+     *
+     * @return mixed bool false if no error or the error string
+     */
+    function checkProgram(&$errmsg, $program)
+    {
+        // XXX FIXME honor safe mode
+        $exe_suffix = OS_WINDOWS ? '.exe' : '';
+        $path_elements = explode(PATH_SEPARATOR, getenv('PATH'));
+        foreach ($path_elements as $dir) {
+            $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix;
+            if (@file_exists($file) && @is_executable($file)) {
+                return false;
+            }
+        }
+        $errmsg = "'$program' program is not present in the PATH";
+        return PEAR_DEPENDENCY_MISSING;
+    }
+
+    // }}}
+    // {{{ checkSAPI()
+
+    /**
+     * SAPI backend check method.  Version comparison is not yet
+     * available here.
+     *
+     * @param string $name      name of SAPI backend
+     * @param string $req   which version to compare
+     * @param string $relation  how to compare versions (currently
+     *                          hardcoded to 'has')
+     * @return mixed bool false if no error or the error string
+     */
+    function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has')
+    {
+        // XXX Fixme: There is no way to know if the user has or
+        // not other SAPI backends installed than the installer one
+
+        $sapi_backend = php_sapi_name();
+        // Version comparisons not supported, sapi backends don't have
+        // version information yet.
+        if ($sapi_backend == $name) {
+            return false;
+        }
+        $errmsg = "'$sapi_backend' SAPI backend not supported";
+        return PEAR_DEPENDENCY_CONFLICT;
+    }
+
+    // }}}
+    // {{{ checkZend()
+
+    /**
+     * Zend version check method
+     *
+     * @param string $req   which version to compare
+     * @param string $relation  how to compare the version
+     *
+     * @return mixed bool false if no error or the error string
+     */
+    function checkZend(&$errmsg, $req, $relation = 'ge')
+    {
+        if (substr($req, 0, 2) == 'v.') {
+            $req = substr($req,2, strlen($req) - 2);
+        }
+        $zend_ver = zend_version();
+        $operator = substr($relation,0,2);
+        if (!version_compare("$zend_ver", "$req", $operator)) {
+            $errmsg = "Zend version " . $this->signOperator($operator) .
+                " $req is required";
+            return PEAR_DEPENDENCY_CONFLICT;
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ signOperator()
+
+    /**
+     * Converts text comparing operators to them sign equivalents
+     *
+     * Example: 'ge' to '>='
+     *
+     * @access public
+     * @param  string Operator
+     * @return string Sign equivalent
+     */
+    function signOperator($operator)
+    {
+        switch($operator) {
+            case 'lt': return '<';
+            case 'le': return '<=';
+            case 'gt': return '>';
+            case 'ge': return '>=';
+            case 'eq': return '==';
+            case 'ne': return '!=';
+            default:
+                return $operator;
+        }
+    }
+
+    // }}}
+    // {{{ codeFromRelation()
+
+    /**
+     * Convert relation into corresponding code
+     *
+     * @access public
+     * @param  string Relation
+     * @param  string Version
+     * @param  string Requirement
+     * @param  bool   Optional dependency indicator
+     * @return integer
+     */
+    function codeFromRelation($relation, $version, $req, $opt = false)
+    {
+        $code = PEAR_DEPENDENCY_BAD_DEPENDENCY;
+        switch ($relation) {
+            case 'gt': case 'ge': case 'eq':
+                // upgrade
+                $have_major = preg_replace('/\D.*/', '', $version);
+                $need_major = preg_replace('/\D.*/', '', $req);
+                if ($need_major > $have_major) {
+                    $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL :
+                                   PEAR_DEPENDENCY_UPGRADE_MAJOR;
+                } else {
+                    $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL :
+                                   PEAR_DEPENDENCY_UPGRADE_MINOR;
+                }
+                break;
+            case 'lt': case 'le': case 'ne':
+                $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL :
+                               PEAR_DEPENDENCY_CONFLICT;
+                break;
+        }
+        return $code;
+    }
+
+    // }}}
+}
+?>
diff --git a/3rdparty/PEAR/Downloader.php b/3rdparty/PEAR/Downloader.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e16dc5ffb1b74791687df743b8d850fe3f80157
--- /dev/null
+++ b/3rdparty/PEAR/Downloader.php
@@ -0,0 +1,680 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net>                                   |
+// |          Tomas V.V.Cox <cox@idecnet.com>                             |
+// |          Martin Jansen <mj@php.net>                                  |
+// +----------------------------------------------------------------------+
+//
+// $Id: Downloader.php,v 1.17.2.1 2004/10/22 22:54:03 cellog Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'PEAR/Registry.php';
+require_once 'PEAR/Dependency.php';
+require_once 'PEAR/Remote.php';
+require_once 'System.php';
+
+
+define('PEAR_INSTALLER_OK',       1);
+define('PEAR_INSTALLER_FAILED',   0);
+define('PEAR_INSTALLER_SKIPPED', -1);
+define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2);
+
+/**
+ * Administration class used to download PEAR packages and maintain the
+ * installed package database.
+ *
+ * @since PEAR 1.4
+ * @author Greg Beaver <cellog@php.net>
+ */
+class PEAR_Downloader extends PEAR_Common
+{
+    /**
+     * @var PEAR_Config
+     * @access private
+     */
+    var $_config;
+
+    /**
+     * @var PEAR_Registry
+     * @access private
+     */
+    var $_registry;
+
+    /**
+     * @var PEAR_Remote
+     * @access private
+     */
+    var $_remote;
+
+    /**
+     * Preferred Installation State (snapshot, devel, alpha, beta, stable)
+     * @var string|null
+     * @access private
+     */
+    var $_preferredState;
+
+    /**
+     * Options from command-line passed to Install.
+     *
+     * Recognized options:<br />
+     *  - onlyreqdeps   : install all required dependencies as well
+     *  - alldeps       : install all dependencies, including optional
+     *  - installroot   : base relative path to install files in
+     *  - force         : force a download even if warnings would prevent it
+     * @see PEAR_Command_Install
+     * @access private
+     * @var array
+     */
+    var $_options;
+
+    /**
+     * Downloaded Packages after a call to download().
+     *
+     * Format of each entry:
+     *
+     * <code>
+     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+     *    'info' => array() // parsed package.xml
+     * );
+     * </code>
+     * @access private
+     * @var array
+     */
+    var $_downloadedPackages = array();
+
+    /**
+     * Packages slated for download.
+     *
+     * This is used to prevent downloading a package more than once should it be a dependency
+     * for two packages to be installed.
+     * Format of each entry:
+     *
+     * <pre>
+     * array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml,
+     * );
+     * </pre>
+     * @access private
+     * @var array
+     */
+    var $_toDownload = array();
+
+    /**
+     * Array of every package installed, with names lower-cased.
+     *
+     * Format:
+     * <code>
+     * array('package1' => 0, 'package2' => 1, );
+     * </code>
+     * @var array
+     */
+    var $_installed = array();
+
+    /**
+     * @var array
+     * @access private
+     */
+    var $_errorStack = array();
+
+    // {{{ PEAR_Downloader()
+
+    function PEAR_Downloader(&$ui, $options, &$config)
+    {
+        $this->_options = $options;
+        $this->_config = &$config;
+        $this->_preferredState = $this->_config->get('preferred_state');
+        $this->ui = &$ui;
+        if (!$this->_preferredState) {
+            // don't inadvertantly use a non-set preferred_state
+            $this->_preferredState = null;
+        }
+
+        $php_dir = $this->_config->get('php_dir');
+        if (isset($this->_options['installroot'])) {
+            if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+                $this->_options['installroot'] = substr($this->_options['installroot'], 0, -1);
+            }
+            $php_dir = $this->_prependPath($php_dir, $this->_options['installroot']);
+        }
+        $this->_registry = &new PEAR_Registry($php_dir);
+        $this->_remote = &new PEAR_Remote($config);
+
+        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+            $this->_installed = $this->_registry->listPackages();
+            array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);'));
+            $this->_installed = array_flip($this->_installed);
+        }
+        parent::PEAR_Common();
+    }
+
+    // }}}
+    // {{{ configSet()
+    function configSet($key, $value, $layer = 'user')
+    {
+        $this->_config->set($key, $value, $layer);
+        $this->_preferredState = $this->_config->get('preferred_state');
+        if (!$this->_preferredState) {
+            // don't inadvertantly use a non-set preferred_state
+            $this->_preferredState = null;
+        }
+    }
+
+    // }}}
+    // {{{ setOptions()
+    function setOptions($options)
+    {
+        $this->_options = $options;
+    }
+
+    // }}}
+    // {{{ _downloadFile()
+    /**
+     * @param string filename to download
+     * @param string version/state
+     * @param string original value passed to command-line
+     * @param string|null preferred state (snapshot/devel/alpha/beta/stable)
+     *                    Defaults to configuration preferred state
+     * @return null|PEAR_Error|string
+     * @access private
+     */
+    function _downloadFile($pkgfile, $version, $origpkgfile, $state = null)
+    {
+        if (is_null($state)) {
+            $state = $this->_preferredState;
+        }
+        // {{{ check the package filename, and whether it's already installed
+        $need_download = false;
+        if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+            $need_download = true;
+        } elseif (!@is_file($pkgfile)) {
+            if ($this->validPackageName($pkgfile)) {
+                if ($this->_registry->packageExists($pkgfile)) {
+                    if (empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+                        $errors[] = "$pkgfile already installed";
+                        return;
+                    }
+                }
+                $pkgfile = $this->getPackageDownloadUrl($pkgfile, $version);
+                $need_download = true;
+            } else {
+                if (strlen($pkgfile)) {
+                    $errors[] = "Could not open the package file: $pkgfile";
+                } else {
+                    $errors[] = "No package file given";
+                }
+                return;
+            }
+        }
+        // }}}
+
+        // {{{ Download package -----------------------------------------------
+        if ($need_download) {
+            $downloaddir = $this->_config->get('download_dir');
+            if (empty($downloaddir)) {
+                if (PEAR::isError($downloaddir = System::mktemp('-d'))) {
+                    return $downloaddir;
+                }
+                $this->log(3, '+ tmp dir created at ' . $downloaddir);
+            }
+            $callback = $this->ui ? array(&$this, '_downloadCallback') : null;
+            $this->pushErrorHandling(PEAR_ERROR_RETURN);
+            $file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback);
+            $this->popErrorHandling();
+            if (PEAR::isError($file)) {
+                if ($this->validPackageName($origpkgfile)) {
+                    if (!PEAR::isError($info = $this->_remote->call('package.info',
+                          $origpkgfile))) {
+                        if (!count($info['releases'])) {
+                            return $this->raiseError('Package ' . $origpkgfile .
+                            ' has no releases');
+                        } else {
+                            return $this->raiseError('No releases of preferred state "'
+                            . $state . '" exist for package ' . $origpkgfile .
+                            '.  Use ' . $origpkgfile . '-state to install another' .
+                            ' state (like ' . $origpkgfile .'-beta)',
+                            PEAR_INSTALLER_ERROR_NO_PREF_STATE);
+                        }
+                    } else {
+                        return $pkgfile;
+                    }
+                } else {
+                    return $this->raiseError($file);
+                }
+            }
+            $pkgfile = $file;
+        }
+        // }}}
+        return $pkgfile;
+    }
+    // }}}
+    // {{{ getPackageDownloadUrl()
+
+    function getPackageDownloadUrl($package, $version = null)
+    {
+        if ($version) {
+            $package .= "-$version";
+        }
+        if ($this === null || $this->_config === null) {
+            $package = "http://pear.php.net/get/$package";
+        } else {
+            $package = "http://" . $this->_config->get('master_server') .
+                "/get/$package";
+        }
+        if (!extension_loaded("zlib")) {
+            $package .= '?uncompress=yes';
+        }
+        return $package;
+    }
+
+    // }}}
+    // {{{ extractDownloadFileName($pkgfile, &$version)
+
+    function extractDownloadFileName($pkgfile, &$version)
+    {
+        if (@is_file($pkgfile)) {
+            return $pkgfile;
+        }
+        // regex defined in Common.php
+        if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) {
+            $version = (isset($m[3])) ? $m[3] : null;
+            return $m[1];
+        }
+        $version = null;
+        return $pkgfile;
+    }
+
+    // }}}
+
+    // }}}
+    // {{{ getDownloadedPackages()
+
+    /**
+     * Retrieve a list of downloaded packages after a call to {@link download()}.
+     *
+     * Also resets the list of downloaded packages.
+     * @return array
+     */
+    function getDownloadedPackages()
+    {
+        $ret = $this->_downloadedPackages;
+        $this->_downloadedPackages = array();
+        $this->_toDownload = array();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ download()
+
+    /**
+     * Download any files and their dependencies, if necessary
+     *
+     * BC-compatible method name
+     * @param array a mixed list of package names, local files, or package.xml
+     */
+    function download($packages)
+    {
+        return $this->doDownload($packages);
+    }
+
+    // }}}
+    // {{{ doDownload()
+
+    /**
+     * Download any files and their dependencies, if necessary
+     *
+     * @param array a mixed list of package names, local files, or package.xml
+     */
+    function doDownload($packages)
+    {
+        $mywillinstall = array();
+        $state = $this->_preferredState;
+
+        // {{{ download files in this list if necessary
+        foreach($packages as $pkgfile) {
+            $need_download = false;
+            if (!is_file($pkgfile)) {
+                if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+                    $need_download = true;
+                }
+                $pkgfile = $this->_downloadNonFile($pkgfile);
+                if (PEAR::isError($pkgfile)) {
+                    return $pkgfile;
+                }
+                if ($pkgfile === false) {
+                    continue;
+                }
+            } // end is_file()
+
+            $tempinfo = $this->infoFromAny($pkgfile);
+            if ($need_download) {
+                $this->_toDownload[] = $tempinfo['package'];
+            }
+            if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+                // ignore dependencies if there are any errors
+                if (!PEAR::isError($tempinfo)) {
+                    $mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps'];
+                }
+            }
+            $this->_downloadedPackages[] = array('pkg' => $tempinfo['package'],
+                                       'file' => $pkgfile, 'info' => $tempinfo);
+        } // end foreach($packages)
+        // }}}
+
+        // {{{ extract dependencies from downloaded files and then download
+        // them if necessary
+        if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) {
+            $deppackages = array();
+            foreach ($mywillinstall as $package => $alldeps) {
+                if (!is_array($alldeps)) {
+                    // there are no dependencies
+                    continue;
+                }
+                $fail = false;
+                foreach ($alldeps as $info) {
+                    if ($info['type'] != 'pkg') {
+                        continue;
+                    }
+                    $ret = $this->_processDependency($package, $info, $mywillinstall);
+                    if ($ret === false) {
+                        continue;
+                    }
+                    if ($ret === 0) {
+                        $fail = true;
+                        continue;
+                    }
+                    if (PEAR::isError($ret)) {
+                        return $ret;
+                    }
+                    $deppackages[] = $ret;
+                } // foreach($alldeps
+                if ($fail) {
+                    $deppackages = array();
+                }
+            }
+
+            if (count($deppackages)) {
+                $this->doDownload($deppackages);
+            }
+        } // }}} if --alldeps or --onlyreqdeps
+    }
+
+    // }}}
+    // {{{ _downloadNonFile($pkgfile)
+
+    /**
+     * @return false|PEAR_Error|string false if loop should be broken out of,
+     *                                 string if the file was downloaded,
+     *                                 PEAR_Error on exception
+     * @access private
+     */
+    function _downloadNonFile($pkgfile)
+    {
+        $origpkgfile = $pkgfile;
+        $state = null;
+        $pkgfile = $this->extractDownloadFileName($pkgfile, $version);
+        if (preg_match('#^(http|ftp)://#', $pkgfile)) {
+            return $this->_downloadFile($pkgfile, $version, $origpkgfile);
+        }
+        if (!$this->validPackageName($pkgfile)) {
+            return $this->raiseError("Package name '$pkgfile' not valid");
+        }
+        // ignore packages that are installed unless we are upgrading
+        $curinfo = $this->_registry->packageInfo($pkgfile);
+        if ($this->_registry->packageExists($pkgfile)
+              && empty($this->_options['upgrade']) && empty($this->_options['force'])) {
+            $this->log(0, "Package '{$curinfo['package']}' already installed, skipping");
+            return false;
+        }
+        if (in_array($pkgfile, $this->_toDownload)) {
+            return false;
+        }
+        $releases = $this->_remote->call('package.info', $pkgfile, 'releases', true);
+        if (!count($releases)) {
+            return $this->raiseError("No releases found for package '$pkgfile'");
+        }
+        // Want a specific version/state
+        if ($version !== null) {
+            // Passed Foo-1.2
+            if ($this->validPackageVersion($version)) {
+                if (!isset($releases[$version])) {
+                    return $this->raiseError("No release with version '$version' found for '$pkgfile'");
+                }
+            // Passed Foo-alpha
+            } elseif (in_array($version, $this->getReleaseStates())) {
+                $state = $version;
+                $version = 0;
+                foreach ($releases as $ver => $inf) {
+                    if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) {
+                        $version = $ver;
+                        break;
+                    }
+                }
+                if ($version == 0) {
+                    return $this->raiseError("No release with state '$state' found for '$pkgfile'");
+                }
+            // invalid suffix passed
+            } else {
+                return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ".
+                                         "version number or release state");
+            }
+        // Guess what to download
+        } else {
+            $states = $this->betterStates($this->_preferredState, true);
+            $possible = false;
+            $version = 0;
+            $higher_version = 0;
+            $prev_hi_ver = 0;
+            foreach ($releases as $ver => $inf) {
+                if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) {
+                    $version = $ver;
+                    break;
+                } else {
+                    $ver = (string)$ver;
+                    if (version_compare($prev_hi_ver, $ver) < 0) {
+                        $prev_hi_ver = $higher_version = $ver;
+                    }
+				}
+            }
+            if ($version === 0 && !isset($this->_options['force'])) {
+                return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) .
+                                         "' found for '$pkgfile'");
+            } elseif ($version === 0) {
+                $this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " .
+                              "than state '$this->_preferredState'");
+            }
+        }
+        // Check if we haven't already the version
+        if (empty($this->_options['force']) && !is_null($curinfo)) {
+            if ($curinfo['version'] == $version) {
+                $this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping");
+                return false;
+            } elseif (version_compare("$version", "{$curinfo['version']}") < 0) {
+                $this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " .
+                              " is installed and {$curinfo['version']} is > requested '$version', skipping");
+                return false;
+            }
+        }
+        $this->_toDownload[] = $pkgfile;
+        return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state);
+    }
+
+    // }}}
+    // {{{ _processDependency($package, $info, $mywillinstall)
+
+    /**
+     * Process a dependency, download if necessary
+     * @param array dependency information from PEAR_Remote call
+     * @param array packages that will be installed in this iteration
+     * @return false|string|PEAR_Error
+     * @access private
+     * @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is
+     *       in fact lower than the required value.  This will be very important for BC dependencies
+     */
+    function _processDependency($package, $info, $mywillinstall)
+    {
+        $state = $this->_preferredState;
+        if (!isset($this->_options['alldeps']) && isset($info['optional']) &&
+              $info['optional'] == 'yes') {
+            // skip optional deps
+            $this->log(0, "skipping Package '$package' optional dependency '$info[name]'");
+            return false;
+        }
+        // {{{ get releases
+        $releases = $this->_remote->call('package.info', $info['name'], 'releases', true);
+        if (PEAR::isError($releases)) {
+            return $releases;
+        }
+        if (!count($releases)) {
+            if (!isset($this->_installed[strtolower($info['name'])])) {
+                $this->pushError("Package '$package' dependency '$info[name]' ".
+                            "has no releases");
+            }
+            return false;
+        }
+        $found = false;
+        $save = $releases;
+        while(count($releases) && !$found) {
+            if (!empty($state) && $state != 'any') {
+                list($release_version, $release) = each($releases);
+                if ($state != $release['state'] &&
+                    !in_array($release['state'], $this->betterStates($state)))
+                {
+                    // drop this release - it ain't stable enough
+                    array_shift($releases);
+                } else {
+                    $found = true;
+                }
+            } else {
+                $found = true;
+            }
+        }
+        if (!count($releases) && !$found) {
+            $get = array();
+            foreach($save as $release) {
+                $get = array_merge($get,
+                    $this->betterStates($release['state'], true));
+            }
+            $savestate = array_shift($get);
+            $this->pushError( "Release for '$package' dependency '$info[name]' " .
+                "has state '$savestate', requires '$state'");
+            return 0;
+        }
+        if (in_array(strtolower($info['name']), $this->_toDownload) ||
+              isset($mywillinstall[strtolower($info['name'])])) {
+            // skip upgrade check for packages we will install
+            return false;
+        }
+        if (!isset($this->_installed[strtolower($info['name'])])) {
+            // check to see if we can install the specific version required
+            if ($info['rel'] == 'eq') {
+                return $info['name'] . '-' . $info['version'];
+            }
+            // skip upgrade check for packages we don't have installed
+            return $info['name'];
+        }
+        // }}}
+
+        // {{{ see if a dependency must be upgraded
+        $inst_version = $this->_registry->packageInfo($info['name'], 'version');
+        if (!isset($info['version'])) {
+            // this is a rel='has' dependency, check against latest
+            if (version_compare($release_version, $inst_version, 'le')) {
+                return false;
+            } else {
+                return $info['name'];
+            }
+        }
+        if (version_compare($info['version'], $inst_version, 'le')) {
+            // installed version is up-to-date
+            return false;
+        }
+        return $info['name'];
+    }
+
+    // }}}
+    // {{{ _downloadCallback()
+
+    function _downloadCallback($msg, $params = null)
+    {
+        switch ($msg) {
+            case 'saveas':
+                $this->log(1, "downloading $params ...");
+                break;
+            case 'done':
+                $this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes');
+                break;
+            case 'bytesread':
+                static $bytes;
+                if (empty($bytes)) {
+                    $bytes = 0;
+                }
+                if (!($bytes % 10240)) {
+                    $this->log(1, '.', false);
+                }
+                $bytes += $params;
+                break;
+            case 'start':
+                $this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)");
+                break;
+        }
+        if (method_exists($this->ui, '_downloadCallback'))
+            $this->ui->_downloadCallback($msg, $params);
+    }
+
+    // }}}
+    // {{{ _prependPath($path, $prepend)
+
+    function _prependPath($path, $prepend)
+    {
+        if (strlen($prepend) > 0) {
+            if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) {
+                $path = $prepend . substr($path, 2);
+            } else {
+                $path = $prepend . $path;
+            }
+        }
+        return $path;
+    }
+    // }}}
+    // {{{ pushError($errmsg, $code)
+
+    /**
+     * @param string
+     * @param integer
+     */
+    function pushError($errmsg, $code = -1)
+    {
+        array_push($this->_errorStack, array($errmsg, $code));
+    }
+
+    // }}}
+    // {{{ getErrorMsgs()
+
+    function getErrorMsgs()
+    {
+        $msgs = array();
+        $errs = $this->_errorStack;
+        foreach ($errs as $err) {
+            $msgs[] = $err[0];
+        }
+        $this->_errorStack = array();
+        return $msgs;
+    }
+
+    // }}}
+}
+// }}}
+
+?>
diff --git a/3rdparty/PEAR/ErrorStack.php b/3rdparty/PEAR/ErrorStack.php
new file mode 100644
index 0000000000000000000000000000000000000000..da52f08d34543667a5716319e84873fbd7f665c5
--- /dev/null
+++ b/3rdparty/PEAR/ErrorStack.php
@@ -0,0 +1,981 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Gregory Beaver <cellog@php.net>                              |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: ErrorStack.php,v 1.7.2.5 2005/01/01 21:26:51 cellog Exp $
+
+/**
+ * Error Stack Implementation
+ * 
+ * This is an incredibly simple implementation of a very complex error handling
+ * facility.  It contains the ability
+ * to track multiple errors from multiple packages simultaneously.  In addition,
+ * it can track errors of many levels, save data along with the error, context
+ * information such as the exact file, line number, class and function that
+ * generated the error, and if necessary, it can raise a traditional PEAR_Error.
+ * It has built-in support for PEAR::Log, to log errors as they occur
+ * 
+ * Since version 0.2alpha, it is also possible to selectively ignore errors,
+ * through the use of an error callback, see {@link pushCallback()}
+ * 
+ * Since version 0.3alpha, it is possible to specify the exception class
+ * returned from {@link push()}
+ *
+ * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class.  This can
+ * still be done quite handily in an error callback or by manipulating the returned array
+ * @author Greg Beaver <cellog@php.net>
+ * @version PEAR1.3.2 (beta)
+ * @package PEAR_ErrorStack
+ * @category Debugging
+ * @license http://www.php.net/license/3_0.txt PHP License v3.0
+ */
+
+/**
+ * Singleton storage
+ * 
+ * Format:
+ * <pre>
+ * array(
+ *  'package1' => PEAR_ErrorStack object,
+ *  'package2' => PEAR_ErrorStack object,
+ *  ...
+ * )
+ * </pre>
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array();
+
+/**
+ * Global error callback (default)
+ * 
+ * This is only used if set to non-false.  * is the default callback for
+ * all packages, whereas specific packages may set a default callback
+ * for all instances, regardless of whether they are a singleton or not.
+ *
+ * To exclude non-singletons, only set the local callback for the singleton
+ * @see PEAR_ErrorStack::setDefaultCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array(
+    '*' => false,
+);
+
+/**
+ * Global Log object (default)
+ * 
+ * This is only used if set to non-false.  Use to set a default log object for
+ * all stacks, regardless of instantiation order or location
+ * @see PEAR_ErrorStack::setDefaultLogger()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false;
+
+/**
+ * Global Overriding Callback
+ * 
+ * This callback will override any error callbacks that specific loggers have set.
+ * Use with EXTREME caution
+ * @see PEAR_ErrorStack::staticPushCallback()
+ * @access private
+ * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']
+ */
+$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+
+/**#@+
+ * One of four possible return values from the error Callback
+ * @see PEAR_ErrorStack::_errorCallback()
+ */
+/**
+ * If this is returned, then the error will be both pushed onto the stack
+ * and logged.
+ */
+define('PEAR_ERRORSTACK_PUSHANDLOG', 1);
+/**
+ * If this is returned, then the error will only be pushed onto the stack,
+ * and not logged.
+ */
+define('PEAR_ERRORSTACK_PUSH', 2);
+/**
+ * If this is returned, then the error will only be logged, but not pushed
+ * onto the error stack.
+ */
+define('PEAR_ERRORSTACK_LOG', 3);
+/**
+ * If this is returned, then the error is completely ignored.
+ */
+define('PEAR_ERRORSTACK_IGNORE', 4);
+/**
+ * If this is returned, then the error is logged and die() is called.
+ */
+define('PEAR_ERRORSTACK_DIE', 5);
+/**#@-*/
+
+/**
+ * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in
+ * the singleton method.
+ */
+define('PEAR_ERRORSTACK_ERR_NONCLASS', 1);
+
+/**
+ * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()}
+ * that has no __toString() method
+ */
+define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2);
+/**
+ * Error Stack Implementation
+ *
+ * Usage:
+ * <code>
+ * // global error stack
+ * $global_stack = &PEAR_ErrorStack::singleton('MyPackage');
+ * // local error stack
+ * $local_stack = new PEAR_ErrorStack('MyPackage');
+ * </code>
+ * @copyright 2004 Gregory Beaver
+ * @package PEAR_ErrorStack
+ * @license http://www.php.net/license/3_0.txt PHP License
+ */
+class PEAR_ErrorStack {
+    /**
+     * Errors are stored in the order that they are pushed on the stack.
+     * @since 0.4alpha Errors are no longer organized by error level.
+     * This renders pop() nearly unusable, and levels could be more easily
+     * handled in a callback anyway
+     * @var array
+     * @access private
+     */
+    var $_errors = array();
+
+    /**
+     * Storage of errors by level.
+     *
+     * Allows easy retrieval and deletion of only errors from a particular level
+     * @since PEAR 1.4.0dev
+     * @var array
+     * @access private
+     */
+    var $_errorsByLevel = array();
+
+    /**
+     * Package name this error stack represents
+     * @var string
+     * @access protected
+     */
+    var $_package;
+    
+    /**
+     * Determines whether a PEAR_Error is thrown upon every error addition
+     * @var boolean
+     * @access private
+     */
+    var $_compat = false;
+    
+    /**
+     * If set to a valid callback, this will be used to generate the error
+     * message from the error code, otherwise the message passed in will be
+     * used
+     * @var false|string|array
+     * @access private
+     */
+    var $_msgCallback = false;
+    
+    /**
+     * If set to a valid callback, this will be used to generate the error
+     * context for an error.  For PHP-related errors, this will be a file
+     * and line number as retrieved from debug_backtrace(), but can be
+     * customized for other purposes.  The error might actually be in a separate
+     * configuration file, or in a database query.
+     * @var false|string|array
+     * @access protected
+     */
+    var $_contextCallback = false;
+    
+    /**
+     * If set to a valid callback, this will be called every time an error
+     * is pushed onto the stack.  The return value will be used to determine
+     * whether to allow an error to be pushed or logged.
+     * 
+     * The return value must be one an PEAR_ERRORSTACK_* constant
+     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+     * @var false|string|array
+     * @access protected
+     */
+    var $_errorCallback = array();
+    
+    /**
+     * PEAR::Log object for logging errors
+     * @var false|Log
+     * @access protected
+     */
+    var $_logger = false;
+    
+    /**
+     * Error messages - designed to be overridden
+     * @var array
+     * @abstract
+     */
+    var $_errorMsgs = array();
+    
+    /**
+     * Set up a new error stack
+     * 
+     * @param string   $package name of the package this error stack represents
+     * @param callback $msgCallback callback used for error message generation
+     * @param callback $contextCallback callback used for context generation,
+     *                 defaults to {@link getFileLine()}
+     * @param boolean  $throwPEAR_Error
+     */
+    function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false,
+                         $throwPEAR_Error = false)
+    {
+        $this->_package = $package;
+        $this->setMessageCallback($msgCallback);
+        $this->setContextCallback($contextCallback);
+        $this->_compat = $throwPEAR_Error;
+    }
+    
+    /**
+     * Return a single error stack for this package.
+     * 
+     * Note that all parameters are ignored if the stack for package $package
+     * has already been instantiated
+     * @param string   $package name of the package this error stack represents
+     * @param callback $msgCallback callback used for error message generation
+     * @param callback $contextCallback callback used for context generation,
+     *                 defaults to {@link getFileLine()}
+     * @param boolean  $throwPEAR_Error
+     * @param string   $stackClass class to instantiate
+     * @static
+     * @return PEAR_ErrorStack
+     */
+    function &singleton($package, $msgCallback = false, $contextCallback = false,
+                         $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack')
+    {
+        if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package];
+        }
+        if (!class_exists($stackClass)) {
+            if (function_exists('debug_backtrace')) {
+                $trace = debug_backtrace();
+            }
+            PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS,
+                'exception', array('stackclass' => $stackClass),
+                'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)',
+                false, $trace);
+        }
+        return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] =
+            &new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error);
+    }
+
+    /**
+     * Internal error handler for PEAR_ErrorStack class
+     * 
+     * Dies if the error is an exception (and would have died anyway)
+     * @access private
+     */
+    function _handleError($err)
+    {
+        if ($err['level'] == 'exception') {
+            $message = $err['message'];
+            if (isset($_SERVER['REQUEST_URI'])) {
+                echo '<br />';
+            } else {
+                echo "\n";
+            }
+            var_dump($err['context']);
+            die($message);
+        }
+    }
+    
+    /**
+     * Set up a PEAR::Log object for all error stacks that don't have one
+     * @param Log $log 
+     * @static
+     */
+    function setDefaultLogger(&$log)
+    {
+        if (is_object($log) && method_exists($log, 'log') ) {
+            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+        } elseif (is_callable($log)) {
+            $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log;
+	}
+    }
+    
+    /**
+     * Set up a PEAR::Log object for this error stack
+     * @param Log $log 
+     */
+    function setLogger(&$log)
+    {
+        if (is_object($log) && method_exists($log, 'log') ) {
+            $this->_logger = &$log;
+        } elseif (is_callable($log)) {
+            $this->_logger = &$log;
+        }
+    }
+    
+    /**
+     * Set an error code => error message mapping callback
+     * 
+     * This method sets the callback that can be used to generate error
+     * messages for any instance
+     * @param array|string Callback function/method
+     */
+    function setMessageCallback($msgCallback)
+    {
+        if (!$msgCallback) {
+            $this->_msgCallback = array(&$this, 'getErrorMessage');
+        } else {
+            if (is_callable($msgCallback)) {
+                $this->_msgCallback = $msgCallback;
+            }
+        }
+    }
+    
+    /**
+     * Get an error code => error message mapping callback
+     * 
+     * This method returns the current callback that can be used to generate error
+     * messages
+     * @return array|string|false Callback function/method or false if none
+     */
+    function getMessageCallback()
+    {
+        return $this->_msgCallback;
+    }
+    
+    /**
+     * Sets a default callback to be used by all error stacks
+     * 
+     * This method sets the callback that can be used to generate error
+     * messages for a singleton
+     * @param array|string Callback function/method
+     * @param string Package name, or false for all packages
+     * @static
+     */
+    function setDefaultCallback($callback = false, $package = false)
+    {
+        if (!is_callable($callback)) {
+            $callback = false;
+        }
+        $package = $package ? $package : '*';
+        $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback;
+    }
+    
+    /**
+     * Set a callback that generates context information (location of error) for an error stack
+     * 
+     * This method sets the callback that can be used to generate context
+     * information for an error.  Passing in NULL will disable context generation
+     * and remove the expensive call to debug_backtrace()
+     * @param array|string|null Callback function/method
+     */
+    function setContextCallback($contextCallback)
+    {
+        if ($contextCallback === null) {
+            return $this->_contextCallback = false;
+        }
+        if (!$contextCallback) {
+            $this->_contextCallback = array(&$this, 'getFileLine');
+        } else {
+            if (is_callable($contextCallback)) {
+                $this->_contextCallback = $contextCallback;
+            }
+        }
+    }
+    
+    /**
+     * Set an error Callback
+     * If set to a valid callback, this will be called every time an error
+     * is pushed onto the stack.  The return value will be used to determine
+     * whether to allow an error to be pushed or logged.
+     * 
+     * The return value must be one of the ERRORSTACK_* constants.
+     * 
+     * This functionality can be used to emulate PEAR's pushErrorHandling, and
+     * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of
+     * the error stack or logging
+     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+     * @see popCallback()
+     * @param string|array $cb
+     */
+    function pushCallback($cb)
+    {
+        array_push($this->_errorCallback, $cb);
+    }
+    
+    /**
+     * Remove a callback from the error callback stack
+     * @see pushCallback()
+     * @return array|string|false
+     */
+    function popCallback()
+    {
+        if (!count($this->_errorCallback)) {
+            return false;
+        }
+        return array_pop($this->_errorCallback);
+    }
+    
+    /**
+     * Set a temporary overriding error callback for every package error stack
+     *
+     * Use this to temporarily disable all existing callbacks (can be used
+     * to emulate the @ operator, for instance)
+     * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG
+     * @see staticPopCallback(), pushCallback()
+     * @param string|array $cb
+     * @static
+     */
+    function staticPushCallback($cb)
+    {
+        array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb);
+    }
+    
+    /**
+     * Remove a temporary overriding error callback
+     * @see staticPushCallback()
+     * @return array|string|false
+     * @static
+     */
+    function staticPopCallback()
+    {
+        $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']);
+        if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) {
+            $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array();
+        }
+        return $ret;
+    }
+    
+    /**
+     * Add an error to the stack
+     * 
+     * If the message generator exists, it is called with 2 parameters.
+     *  - the current Error Stack object
+     *  - an array that is in the same format as an error.  Available indices
+     *    are 'code', 'package', 'time', 'params', 'level', and 'context'
+     * 
+     * Next, if the error should contain context information, this is
+     * handled by the context grabbing method.
+     * Finally, the error is pushed onto the proper error stack
+     * @param int    $code      Package-specific error code
+     * @param string $level     Error level.  This is NOT spell-checked
+     * @param array  $params    associative array of error parameters
+     * @param string $msg       Error message, or a portion of it if the message
+     *                          is to be generated
+     * @param array  $repackage If this error re-packages an error pushed by
+     *                          another package, place the array returned from
+     *                          {@link pop()} in this parameter
+     * @param array  $backtrace Protected parameter: use this to pass in the
+     *                          {@link debug_backtrace()} that should be used
+     *                          to find error context
+     * @return PEAR_Error|array|Exception
+     *                          if compatibility mode is on, a PEAR_Error is also
+     *                          thrown.  If the class Exception exists, then one
+     *                          is returned to allow code like:
+     * <code>
+     * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
+     * </code>
+     * 
+     * The errorData property of the exception class will be set to the array
+     * that would normally be returned.  If a PEAR_Error is returned, the userinfo
+     * property is set to the array
+     * 
+     * Otherwise, an array is returned in this format:
+     * <code>
+     * array(
+     *    'code' => $code,
+     *    'params' => $params,
+     *    'package' => $this->_package,
+     *    'level' => $level,
+     *    'time' => time(),
+     *    'context' => $context,
+     *    'message' => $msg,
+     * //['repackage' => $err] repackaged error array/Exception class
+     * );
+     * </code>
+     */
+    function push($code, $level = 'error', $params = array(), $msg = false,
+                  $repackage = false, $backtrace = false)
+    {
+        $context = false;
+        // grab error context
+        if ($this->_contextCallback) {
+            if (!$backtrace) {
+                $backtrace = debug_backtrace();
+            }
+            $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
+        }
+        
+        // save error
+        $time = explode(' ', microtime());
+        $time = $time[1] + $time[0];
+        $err = array(
+                'code' => $code,
+                'params' => $params,
+                'package' => $this->_package,
+                'level' => $level,
+                'time' => $time,
+                'context' => $context,
+                'message' => $msg,
+               );
+
+        // set up the error message, if necessary
+        if ($this->_msgCallback) {
+            $msg = call_user_func_array($this->_msgCallback,
+                                        array(&$this, $err));
+            $err['message'] = $msg;
+        }        
+        
+        if ($repackage) {
+            $err['repackage'] = $repackage;
+        }
+        $push = $log = true;
+        $die = false;
+        // try the overriding callback first
+        $callback = $this->staticPopCallback();
+        if ($callback) {
+            $this->staticPushCallback($callback);
+        }
+        if (!is_callable($callback)) {
+            // try the local callback next
+            $callback = $this->popCallback();
+            if (is_callable($callback)) {
+                $this->pushCallback($callback);
+            } else {
+                // try the default callback
+                $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
+                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
+                    $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
+            }
+        }
+        if (is_callable($callback)) {
+            switch(call_user_func($callback, $err)){
+            	case PEAR_ERRORSTACK_IGNORE: 
+            		return $err;
+        		break;
+            	case PEAR_ERRORSTACK_PUSH: 
+            		$log = false;
+        		break;
+            	case PEAR_ERRORSTACK_LOG: 
+            		$push = false;
+        		break;
+            	case PEAR_ERRORSTACK_DIE: 
+            		$die = true;
+        		break;
+                // anything else returned has the same effect as pushandlog
+            }
+        }
+        if ($push) {
+            array_unshift($this->_errors, $err);
+            $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
+        }
+        if ($log) {
+            if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
+                $this->_log($err);
+            }
+        }
+        if ($die) {
+            die();
+        }
+        if ($this->_compat && $push) {
+            return $this->raiseError($msg, $code, null, null, $err);
+        }
+        return $err;
+    }
+    
+    /**
+     * Static version of {@link push()}
+     * 
+     * @param string $package   Package name this error belongs to
+     * @param int    $code      Package-specific error code
+     * @param string $level     Error level.  This is NOT spell-checked
+     * @param array  $params    associative array of error parameters
+     * @param string $msg       Error message, or a portion of it if the message
+     *                          is to be generated
+     * @param array  $repackage If this error re-packages an error pushed by
+     *                          another package, place the array returned from
+     *                          {@link pop()} in this parameter
+     * @param array  $backtrace Protected parameter: use this to pass in the
+     *                          {@link debug_backtrace()} that should be used
+     *                          to find error context
+     * @return PEAR_Error|null|Exception
+     *                          if compatibility mode is on, a PEAR_Error is also
+     *                          thrown.  If the class Exception exists, then one
+     *                          is returned to allow code like:
+     * <code>
+     * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
+     * </code>
+     * @static
+     */
+    function staticPush($package, $code, $level = 'error', $params = array(),
+                        $msg = false, $repackage = false, $backtrace = false)
+    {
+        $s = &PEAR_ErrorStack::singleton($package);
+        if ($s->_contextCallback) {
+            if (!$backtrace) {
+                if (function_exists('debug_backtrace')) {
+                    $backtrace = debug_backtrace();
+                }
+            }
+        }
+        return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
+    }
+    
+    /**
+     * Log an error using PEAR::Log
+     * @param array $err Error array
+     * @param array $levels Error level => Log constant map
+     * @access protected
+     */
+    function _log($err)
+    {
+        if ($this->_logger) {
+            $logger = &$this->_logger;
+        } else {
+            $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
+        }
+        if (is_a($logger, 'Log')) {
+            $levels = array(
+                'exception' => PEAR_LOG_CRIT,
+                'alert' => PEAR_LOG_ALERT,
+                'critical' => PEAR_LOG_CRIT,
+                'error' => PEAR_LOG_ERR,
+                'warning' => PEAR_LOG_WARNING,
+                'notice' => PEAR_LOG_NOTICE,
+                'info' => PEAR_LOG_INFO,
+                'debug' => PEAR_LOG_DEBUG);
+            if (isset($levels[$err['level']])) {
+                $level = $levels[$err['level']];
+            } else {
+                $level = PEAR_LOG_INFO;
+            }
+            $logger->log($err['message'], $level, $err);
+        } else { // support non-standard logs
+            call_user_func($logger, $err);
+        }
+    }
+
+    
+    /**
+     * Pop an error off of the error stack
+     * 
+     * @return false|array
+     * @since 0.4alpha it is no longer possible to specify a specific error
+     * level to return - the last error pushed will be returned, instead
+     */
+    function pop()
+    {
+        return @array_shift($this->_errors);
+    }
+    
+    /**
+     * Determine whether there are any errors on the stack
+     * @param string|array Level name.  Use to determine if any errors
+     * of level (string), or levels (array) have been pushed
+     * @return boolean
+     */
+    function hasErrors($level = false)
+    {
+        if ($level) {
+            return isset($this->_errorsByLevel[$level]);
+        }
+        return count($this->_errors);
+    }
+    
+    /**
+     * Retrieve all errors since last purge
+     * 
+     * @param boolean set in order to empty the error stack
+     * @param string level name, to return only errors of a particular severity
+     * @return array
+     */
+    function getErrors($purge = false, $level = false)
+    {
+        if (!$purge) {
+            if ($level) {
+                if (!isset($this->_errorsByLevel[$level])) {
+                    return array();
+                } else {
+                    return $this->_errorsByLevel[$level];
+                }
+            } else {
+                return $this->_errors;
+            }
+        }
+        if ($level) {
+            $ret = $this->_errorsByLevel[$level];
+            foreach ($this->_errorsByLevel[$level] as $i => $unused) {
+                // entries are references to the $_errors array
+                $this->_errorsByLevel[$level][$i] = false;
+            }
+            // array_filter removes all entries === false
+            $this->_errors = array_filter($this->_errors);
+            unset($this->_errorsByLevel[$level]);
+            return $ret;
+        }
+        $ret = $this->_errors;
+        $this->_errors = array();
+        $this->_errorsByLevel = array();
+        return $ret;
+    }
+    
+    /**
+     * Determine whether there are any errors on a single error stack, or on any error stack
+     *
+     * The optional parameter can be used to test the existence of any errors without the need of
+     * singleton instantiation
+     * @param string|false Package name to check for errors
+     * @param string Level name to check for a particular severity
+     * @return boolean
+     * @static
+     */
+    function staticHasErrors($package = false, $level = false)
+    {
+        if ($package) {
+            if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
+                return false;
+            }
+            return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
+        }
+        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+            if ($obj->hasErrors($level)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Get a list of all errors since last purge, organized by package
+     * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
+     * @param boolean $purge Set to purge the error stack of existing errors
+     * @param string  $level Set to a level name in order to retrieve only errors of a particular level
+     * @param boolean $merge Set to return a flat array, not organized by package
+     * @param array   $sortfunc Function used to sort a merged array - default
+     *        sorts by time, and should be good for most cases
+     * @static
+     * @return array 
+     */
+    function staticGetErrors($purge = false, $level = false, $merge = false,
+                             $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
+    {
+        $ret = array();
+        if (!is_callable($sortfunc)) {
+            $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
+        }
+        foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
+            $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
+            if ($test) {
+                if ($merge) {
+                    $ret = array_merge($ret, $test);
+                } else {
+                    $ret[$package] = $test;
+                }
+            }
+        }
+        if ($merge) {
+            usort($ret, $sortfunc);
+        }
+        return $ret;
+    }
+    
+    /**
+     * Error sorting function, sorts by time
+     * @access private
+     */
+    function _sortErrors($a, $b)
+    {
+        if ($a['time'] == $b['time']) {
+            return 0;
+        }
+        if ($a['time'] < $b['time']) {
+            return 1;
+        }
+        return -1;
+    }
+
+    /**
+     * Standard file/line number/function/class context callback
+     *
+     * This function uses a backtrace generated from {@link debug_backtrace()}
+     * and so will not work at all in PHP < 4.3.0.  The frame should
+     * reference the frame that contains the source of the error.
+     * @return array|false either array('file' => file, 'line' => line,
+     *         'function' => function name, 'class' => class name) or
+     *         if this doesn't work, then false
+     * @param unused
+     * @param integer backtrace frame.
+     * @param array Results of debug_backtrace()
+     * @static
+     */
+    function getFileLine($code, $params, $backtrace = null)
+    {
+        if ($backtrace === null) {
+            return false;
+        }
+        $frame = 0;
+        $functionframe = 1;
+        if (!isset($backtrace[1])) {
+            $functionframe = 0;
+        } else {
+            while (isset($backtrace[$functionframe]['function']) &&
+                  $backtrace[$functionframe]['function'] == 'eval' &&
+                  isset($backtrace[$functionframe + 1])) {
+                $functionframe++;
+            }
+        }
+        if (isset($backtrace[$frame])) {
+            if (!isset($backtrace[$frame]['file'])) {
+                $frame++;
+            }
+            $funcbacktrace = $backtrace[$functionframe];
+            $filebacktrace = $backtrace[$frame];
+            $ret = array('file' => $filebacktrace['file'],
+                         'line' => $filebacktrace['line']);
+            // rearrange for eval'd code or create function errors
+            if (strpos($filebacktrace['file'], '(') && 
+            	  preg_match(';^(.*?)\((\d+)\) : (.*?)$;', $filebacktrace['file'],
+                  $matches)) {
+                $ret['file'] = $matches[1];
+                $ret['line'] = $matches[2] + 0;
+            }
+            if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
+                if ($funcbacktrace['function'] != 'eval') {
+                    if ($funcbacktrace['function'] == '__lambda_func') {
+                        $ret['function'] = 'create_function() code';
+                    } else {
+                        $ret['function'] = $funcbacktrace['function'];
+                    }
+                }
+            }
+            if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
+                $ret['class'] = $funcbacktrace['class'];
+            }
+            return $ret;
+        }
+        return false;
+    }
+    
+    /**
+     * Standard error message generation callback
+     * 
+     * This method may also be called by a custom error message generator
+     * to fill in template values from the params array, simply
+     * set the third parameter to the error message template string to use
+     * 
+     * The special variable %__msg% is reserved: use it only to specify
+     * where a message passed in by the user should be placed in the template,
+     * like so:
+     * 
+     * Error message: %msg% - internal error
+     * 
+     * If the message passed like so:
+     * 
+     * <code>
+     * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
+     * </code>
+     * 
+     * The returned error message will be "Error message: server error 500 -
+     * internal error"
+     * @param PEAR_ErrorStack
+     * @param array
+     * @param string|false Pre-generated error message template
+     * @static
+     * @return string
+     */
+    function getErrorMessage(&$stack, $err, $template = false)
+    {
+        if ($template) {
+            $mainmsg = $template;
+        } else {
+            $mainmsg = $stack->getErrorMessageTemplate($err['code']);
+        }
+        $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
+        if (count($err['params'])) {
+            foreach ($err['params'] as $name => $val) {
+                if (is_array($val)) {
+                    // @ is needed in case $val is a multi-dimensional array
+                    $val = @implode(', ', $val);
+                }
+                if (is_object($val)) {
+                    if (method_exists($val, '__toString')) {
+                        $val = $val->__toString();
+                    } else {
+                        PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
+                            'warning', array('obj' => get_class($val)),
+                            'object %obj% passed into getErrorMessage, but has no __toString() method');
+                        $val = 'Object';
+                    }
+                }
+                $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
+            }
+        }
+        return $mainmsg;
+    }
+    
+    /**
+     * Standard Error Message Template generator from code
+     * @return string
+     */
+    function getErrorMessageTemplate($code)
+    {
+        if (!isset($this->_errorMsgs[$code])) {
+            return '%__msg%';
+        }
+        return $this->_errorMsgs[$code];
+    }
+    
+    /**
+     * Set the Error Message Template array
+     * 
+     * The array format must be:
+     * <pre>
+     * array(error code => 'message template',...)
+     * </pre>
+     * 
+     * Error message parameters passed into {@link push()} will be used as input
+     * for the error message.  If the template is 'message %foo% was %bar%', and the
+     * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
+     * be 'message one was six'
+     * @return string
+     */
+    function setErrorMessageTemplate($template)
+    {
+        $this->_errorMsgs = $template;
+    }
+    
+    
+    /**
+     * emulate PEAR::raiseError()
+     * 
+     * @return PEAR_Error
+     */
+    function raiseError()
+    {
+        require_once 'PEAR.php';
+        $args = func_get_args();
+        return call_user_func_array(array('PEAR', 'raiseError'), $args);
+    }
+}
+$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
+$stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
+?>
\ No newline at end of file
diff --git a/3rdparty/PEAR/Exception.php b/3rdparty/PEAR/Exception.php
new file mode 100644
index 0000000000000000000000000000000000000000..c735c16b3983f6dd2baba28271b976e0436623d7
--- /dev/null
+++ b/3rdparty/PEAR/Exception.php
@@ -0,0 +1,359 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
+// +----------------------------------------------------------------------+
+// | PEAR_Exception                                                       |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 2004 The PEAR Group                                    |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com>                             |
+// |          Hans Lellelid <hans@velum.net>                              |
+// |          Bertrand Mansion <bmansion@mamasam.com>                     |
+// |          Greg Beaver <cellog@php.net>                                |
+// +----------------------------------------------------------------------+
+//
+// $Id: Exception.php,v 1.4.2.2 2004/12/31 19:01:52 cellog Exp $
+
+
+/**
+ * Base PEAR_Exception Class
+ *
+ * WARNING: This code should be considered stable, but the API is
+ * subject to immediate and drastic change, so API stability is
+ * at best alpha
+ *
+ * 1) Features:
+ *
+ * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
+ * - Definable triggers, shot when exceptions occur
+ * - Pretty and informative error messages
+ * - Added more context info available (like class, method or cause)
+ * - cause can be a PEAR_Exception or an array of mixed
+ *   PEAR_Exceptions/PEAR_ErrorStack warnings
+ * - callbacks for specific exception classes and their children
+ *
+ * 2) Ideas:
+ *
+ * - Maybe a way to define a 'template' for the output
+ *
+ * 3) Inherited properties from PHP Exception Class:
+ *
+ * protected $message
+ * protected $code
+ * protected $line
+ * protected $file
+ * private   $trace
+ *
+ * 4) Inherited methods from PHP Exception Class:
+ *
+ * __clone
+ * __construct
+ * getMessage
+ * getCode
+ * getFile
+ * getLine
+ * getTraceSafe
+ * getTraceSafeAsString
+ * __toString
+ *
+ * 5) Usage example
+ *
+ * <code>
+ *  require_once 'PEAR/Exception.php';
+ *
+ *  class Test {
+ *     function foo() {
+ *         throw new PEAR_Exception('Error Message', ERROR_CODE);
+ *     }
+ *  }
+ *
+ *  function myLogger($pear_exception) {
+ *     echo $pear_exception->getMessage();
+ *  }
+ *  // each time a exception is thrown the 'myLogger' will be called
+ *  // (its use is completely optional)
+ *  PEAR_Exception::addObserver('myLogger');
+ *  $test = new Test;
+ *  try {
+ *     $test->foo();
+ *  } catch (PEAR_Exception $e) {
+ *     print $e;
+ *  }
+ * </code>
+ *
+ * @since PHP 5
+ * @package PEAR
+ * @version $Revision: 1.4.2.2 $
+ * @author Tomas V.V.Cox <cox@idecnet.com>
+ * @author Hans Lellelid <hans@velum.net>
+ * @author Bertrand Mansion <bmansion@mamasam.com>
+ *
+ */
+class PEAR_Exception extends Exception
+{
+    const OBSERVER_PRINT = -2;
+    const OBSERVER_TRIGGER = -4;
+    const OBSERVER_DIE = -8;
+    protected $cause;
+    private static $_observers = array();
+    private static $_uniqueid = 0;
+    private $_trace;
+
+    /**
+     * Supported signatures:
+     * PEAR_Exception(string $message);
+     * PEAR_Exception(string $message, int $code);
+     * PEAR_Exception(string $message, Exception $cause);
+     * PEAR_Exception(string $message, Exception $cause, int $code);
+     * PEAR_Exception(string $message, array $causes);
+     * PEAR_Exception(string $message, array $causes, int $code);
+     */
+    public function __construct($message, $p2 = null, $p3 = null)
+    {
+        if (is_int($p2)) {
+            $code = $p2;
+            $this->cause = null;
+        } elseif ($p2 instanceof Exception || is_array($p2)) {
+            $code = $p3;
+            if (is_array($p2) && isset($p2['message'])) {
+                // fix potential problem of passing in a single warning
+                $p2 = array($p2);
+            }
+            $this->cause = $p2;
+        } else {
+        $code = null;
+            $this->cause = null;
+        }
+        parent::__construct($message, $code);
+        $this->signal();
+    }
+
+    /**
+     * @param mixed $callback  - A valid php callback, see php func is_callable()
+     *                         - A PEAR_Exception::OBSERVER_* constant
+     *                         - An array(const PEAR_Exception::OBSERVER_*,
+     *                           mixed $options)
+     * @param string $label    The name of the observer. Use this if you want
+     *                         to remove it later with removeObserver()
+     */
+    public static function addObserver($callback, $label = 'default')
+    {
+        self::$_observers[$label] = $callback;
+    }
+
+    public static function removeObserver($label = 'default')
+    {
+        unset(self::$_observers[$label]);
+    }
+
+    /**
+     * @return int unique identifier for an observer
+     */
+    public static function getUniqueId()
+    {
+        return self::$_uniqueid++;
+    }
+
+    private function signal()
+    {
+        foreach (self::$_observers as $func) {
+            if (is_callable($func)) {
+                call_user_func($func, $this);
+                continue;
+            }
+            settype($func, 'array');
+            switch ($func[0]) {
+                case self::OBSERVER_PRINT :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    printf($f, $this->getMessage());
+                    break;
+                case self::OBSERVER_TRIGGER :
+                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
+                    trigger_error($this->getMessage(), $f);
+                    break;
+                case self::OBSERVER_DIE :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    die(printf($f, $this->getMessage()));
+                    break;
+                default:
+                    trigger_error('invalid observer type', E_USER_WARNING);
+            }
+        }
+    }
+
+    /**
+     * Return specific error information that can be used for more detailed
+     * error messages or translation.
+     *
+     * This method may be overridden in child exception classes in order
+     * to add functionality not present in PEAR_Exception and is a placeholder
+     * to define API
+     *
+     * The returned array must be an associative array of parameter => value like so:
+     * <pre>
+     * array('name' => $name, 'context' => array(...))
+     * </pre>
+     * @return array
+     */
+    public function getErrorData()
+    {
+        return array();
+    }
+
+    /**
+     * Returns the exception that caused this exception to be thrown
+     * @access public
+     * @return Exception|array The context of the exception
+     */
+    public function getCause()
+    {
+        return $this->cause;
+    }
+
+    /**
+     * Function must be public to call on caused exceptions
+     * @param array
+     */
+    public function getCauseMessage(&$causes)
+    {
+        $trace = $this->getTraceSafe();
+        $cause = array('class'   => get_class($this),
+                       'message' => $this->message,
+                       'file' => 'unknown',
+                       'line' => 'unknown');
+        if (isset($trace[0])) {
+            if (isset($trace[0]['file'])) {
+                $cause['file'] = $trace[0]['file'];
+                $cause['line'] = $trace[0]['line'];
+            }
+        }
+        if ($this->cause instanceof PEAR_Exception) {
+            $this->cause->getCauseMessage($causes);
+        }
+        if (is_array($this->cause)) {
+            foreach ($this->cause as $cause) {
+                if ($cause instanceof PEAR_Exception) {
+                    $cause->getCauseMessage($causes);
+                } elseif (is_array($cause) && isset($cause['message'])) {
+                    // PEAR_ErrorStack warning
+                    $causes[] = array(
+                        'class' => $cause['package'],
+                        'message' => $cause['message'],
+                        'file' => isset($cause['context']['file']) ?
+                                            $cause['context']['file'] :
+                                            'unknown',
+                        'line' => isset($cause['context']['line']) ?
+                                            $cause['context']['line'] :
+                                            'unknown',
+                    );
+                }
+            }
+        }
+    }
+
+    public function getTraceSafe()
+    {   
+        if (!isset($this->_trace)) {
+            $this->_trace = $this->getTrace();
+            if (empty($this->_trace)) {
+                $backtrace = debug_backtrace();
+                $this->_trace = array($backtrace[count($backtrace)-1]);
+            }
+        }
+        return $this->_trace;
+    }
+
+    public function getErrorClass()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['class'];
+    }
+
+    public function getErrorMethod()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['function'];
+    }
+
+    public function __toString()
+    {
+        if (isset($_SERVER['REQUEST_URI'])) {
+            return $this->toHtml();
+        }
+        return $this->toText();
+        }
+
+    public function toHtml()
+    {
+        $trace = $this->getTraceSafe();
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $html =  '<table border="1" cellspacing="0">' . "\n";
+        foreach ($causes as $i => $cause) {
+            $html .= '<tr><td colspan="3" bgcolor="#ff9999">'
+               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
+               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
+               . 'on line <b>' . $cause['line'] . '</b>'
+               . "</td></tr>\n";
+        }
+        $html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n"
+               . '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>'
+               . '<td align="center" bgcolor="#cccccc"><b>Function</b></td>'
+               . '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n";
+
+        foreach ($trace as $k => $v) {
+            $html .= '<tr><td align="center">' . $k . '</td>'
+                   . '<td>';
+            if (!empty($v['class'])) {
+                $html .= $v['class'] . $v['type'];
+            }
+            $html .= $v['function'];
+            $args = array();
+            if (!empty($v['args'])) {
+                foreach ($v['args'] as $arg) {
+                    if (is_null($arg)) $args[] = 'null';
+                    elseif (is_array($arg)) $args[] = 'Array';
+                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
+                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
+                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
+                    else {
+                        $arg = (string)$arg;
+                        $str = htmlspecialchars(substr($arg, 0, 16));
+                        if (strlen($arg) > 16) $str .= '&hellip;';
+                        $args[] = "'" . $str . "'";
+                    }
+                }
+            }
+            $html .= '(' . implode(', ',$args) . ')'
+                   . '</td>'
+                   . '<td>' . $v['file'] . ':' . $v['line'] . '</td></tr>' . "\n";
+        }
+        $html .= '<tr><td align="center">' . ($k+1) . '</td>'
+               . '<td>{main}</td>'
+               . '<td>&nbsp;</td></tr>' . "\n"
+               . '</table>';
+        return $html;
+    }
+
+    public function toText()
+    {
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $causeMsg = '';
+        foreach ($causes as $i => $cause) {
+            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
+                   . $cause['message'] . ' in ' . $cause['file']
+                   . ' on line ' . $cause['line'] . "\n";
+        }
+        return $causeMsg . $this->getTraceAsString();
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/3rdparty/PEAR/Frontend/CLI.php b/3rdparty/PEAR/Frontend/CLI.php
new file mode 100644
index 0000000000000000000000000000000000000000..832ddf09b3fb9939db70c752dba0c72da804ba68
--- /dev/null
+++ b/3rdparty/PEAR/Frontend/CLI.php
@@ -0,0 +1,509 @@
+<?php
+/*
+  +----------------------------------------------------------------------+
+  | PHP Version 5                                                        |
+  +----------------------------------------------------------------------+
+  | Copyright (c) 1997-2004 The PHP Group                                |
+  +----------------------------------------------------------------------+
+  | This source file is subject to version 3.0 of the PHP license,       |
+  | that is bundled with this package in the file LICENSE, and is        |
+  | available through the world-wide-web at the following url:           |
+  | http://www.php.net/license/3_0.txt.                                  |
+  | If you did not receive a copy of the PHP license and are unable to   |
+  | obtain it through the world-wide-web, please send a note to          |
+  | license@php.net so we can mail you a copy immediately.               |
+  +----------------------------------------------------------------------+
+  | Author: Stig Sæther Bakken <ssb@php.net>                             |
+  +----------------------------------------------------------------------+
+
+  $Id: CLI.php,v 1.41 2004/02/17 05:49:16 ssb Exp $
+*/
+
+require_once "PEAR.php";
+
+class PEAR_Frontend_CLI extends PEAR
+{
+    // {{{ properties
+
+    /**
+     * What type of user interface this frontend is for.
+     * @var string
+     * @access public
+     */
+    var $type = 'CLI';
+    var $lp = ''; // line prefix
+
+    var $params = array();
+    var $term = array(
+        'bold' => '',
+        'normal' => '',
+        );
+
+    // }}}
+
+    // {{{ constructor
+
+    function PEAR_Frontend_CLI()
+    {
+        parent::PEAR();
+        $term = getenv('TERM'); //(cox) $_ENV is empty for me in 4.1.1
+        if (function_exists('posix_isatty') && !posix_isatty(1)) {
+            // output is being redirected to a file or through a pipe
+        } elseif ($term) {
+            // XXX can use ncurses extension here, if available
+            if (preg_match('/^(xterm|vt220|linux)/', $term)) {
+                $this->term['bold'] = sprintf("%c%c%c%c", 27, 91, 49, 109);
+                $this->term['normal']=sprintf("%c%c%c", 27, 91, 109);
+            } elseif (preg_match('/^vt100/', $term)) {
+                $this->term['bold'] = sprintf("%c%c%c%c%c%c", 27, 91, 49, 109, 0, 0);
+                $this->term['normal']=sprintf("%c%c%c%c%c", 27, 91, 109, 0, 0);
+            }
+        } elseif (OS_WINDOWS) {
+            // XXX add ANSI codes here
+        }
+    }
+
+    // }}}
+
+    // {{{ displayLine(text)
+
+    function displayLine($text)
+    {
+        trigger_error("PEAR_Frontend_CLI::displayLine deprecated", E_USER_ERROR);
+    }
+
+    function _displayLine($text)
+    {
+        print "$this->lp$text\n";
+    }
+
+    // }}}
+    // {{{ display(text)
+
+    function display($text)
+    {
+        trigger_error("PEAR_Frontend_CLI::display deprecated", E_USER_ERROR);
+    }
+
+    function _display($text)
+    {
+        print $text;
+    }
+
+    // }}}
+    // {{{ displayError(eobj)
+
+    /**
+     * @param object PEAR_Error object
+     */
+    function displayError($eobj)
+    {
+        return $this->_displayLine($eobj->getMessage());
+    }
+
+    // }}}
+    // {{{ displayFatalError(eobj)
+
+    /**
+     * @param object PEAR_Error object
+     */
+    function displayFatalError($eobj)
+    {
+        $this->displayError($eobj);
+        exit(1);
+    }
+
+    // }}}
+    // {{{ displayHeading(title)
+
+    function displayHeading($title)
+    {
+        trigger_error("PEAR_Frontend_CLI::displayHeading deprecated", E_USER_ERROR);
+    }
+
+    function _displayHeading($title)
+    {
+        print $this->lp.$this->bold($title)."\n";
+        print $this->lp.str_repeat("=", strlen($title))."\n";
+    }
+
+    // }}}
+    // {{{ userDialog(prompt, [type], [default])
+
+    function userDialog($command, $prompts, $types = array(), $defaults = array())
+    {
+        $result = array();
+        if (is_array($prompts)) {
+            $fp = fopen("php://stdin", "r");
+            foreach ($prompts as $key => $prompt) {
+                $type = $types[$key];
+                $default = @$defaults[$key];
+                if ($type == 'password') {
+                    system('stty -echo');
+                }
+                print "$this->lp$prompt ";
+                if ($default) {
+                    print "[$default] ";
+                }
+                print ": ";
+                $line = fgets($fp, 2048);
+                if ($type == 'password') {
+                    system('stty echo');
+                    print "\n";
+                }
+                if ($default && trim($line) == "") {
+                    $result[$key] = $default;
+                } else {
+                    $result[$key] = $line;
+                }
+            }
+            fclose($fp);
+        }
+        return $result;
+    }
+
+    // }}}
+    // {{{ userConfirm(prompt, [default])
+
+    function userConfirm($prompt, $default = 'yes')
+    {
+        trigger_error("PEAR_Frontend_CLI::userConfirm not yet converted", E_USER_ERROR);
+        static $positives = array('y', 'yes', 'on', '1');
+        static $negatives = array('n', 'no', 'off', '0');
+        print "$this->lp$prompt [$default] : ";
+        $fp = fopen("php://stdin", "r");
+        $line = fgets($fp, 2048);
+        fclose($fp);
+        $answer = strtolower(trim($line));
+        if (empty($answer)) {
+            $answer = $default;
+        }
+        if (in_array($answer, $positives)) {
+            return true;
+        }
+        if (in_array($answer, $negatives)) {
+            return false;
+        }
+        if (in_array($default, $positives)) {
+            return true;
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ startTable([params])
+
+    function startTable($params = array())
+    {
+        trigger_error("PEAR_Frontend_CLI::startTable deprecated", E_USER_ERROR);
+    }
+
+    function _startTable($params = array())
+    {
+        $params['table_data'] = array();
+        $params['widest'] = array();  // indexed by column
+        $params['highest'] = array(); // indexed by row
+        $params['ncols'] = 0;
+        $this->params = $params;
+    }
+
+    // }}}
+    // {{{ tableRow(columns, [rowparams], [colparams])
+
+    function tableRow($columns, $rowparams = array(), $colparams = array())
+    {
+        trigger_error("PEAR_Frontend_CLI::tableRow deprecated", E_USER_ERROR);
+    }
+
+    function _tableRow($columns, $rowparams = array(), $colparams = array())
+    {
+        $highest = 1;
+        for ($i = 0; $i < sizeof($columns); $i++) {
+            $col = &$columns[$i];
+            if (isset($colparams[$i]) && !empty($colparams[$i]['wrap'])) {
+                $col = wordwrap($col, $colparams[$i]['wrap'], "\n", 0);
+            }
+            if (strpos($col, "\n") !== false) {
+                $multiline = explode("\n", $col);
+                $w = 0;
+                foreach ($multiline as $n => $line) {
+                    if (strlen($line) > $w) {
+                        $w = strlen($line);
+                    }
+                }
+                $lines = sizeof($multiline);
+            } else {
+                $w = strlen($col);
+            }
+            if ($w > @$this->params['widest'][$i]) {
+                $this->params['widest'][$i] = $w;
+            }
+            $tmp = count_chars($columns[$i], 1);
+            // handle unix, mac and windows formats
+            $lines = (isset($tmp[10]) ? $tmp[10] : @$tmp[13]) + 1;
+            if ($lines > $highest) {
+                $highest = $lines;
+            }
+        }
+        if (sizeof($columns) > $this->params['ncols']) {
+            $this->params['ncols'] = sizeof($columns);
+        }
+        $new_row = array(
+            'data' => $columns,
+            'height' => $highest,
+            'rowparams' => $rowparams,
+            'colparams' => $colparams,
+            );
+        $this->params['table_data'][] = $new_row;
+    }
+
+    // }}}
+    // {{{ endTable()
+
+    function endTable()
+    {
+        trigger_error("PEAR_Frontend_CLI::endTable deprecated", E_USER_ERROR);
+    }
+
+    function _endTable()
+    {
+        extract($this->params);
+        if (!empty($caption)) {
+            $this->_displayHeading($caption);
+        }
+        if (count($table_data) == 0) {
+            return;
+        }
+        if (!isset($width)) {
+            $width = $widest;
+        } else {
+            for ($i = 0; $i < $ncols; $i++) {
+                if (!isset($width[$i])) {
+                    $width[$i] = $widest[$i];
+                }
+            }
+        }
+        $border = false;
+        if (empty($border)) {
+            $cellstart = '';
+            $cellend = ' ';
+            $rowend = '';
+            $padrowend = false;
+            $borderline = '';
+        } else {
+            $cellstart = '| ';
+            $cellend = ' ';
+            $rowend = '|';
+            $padrowend = true;
+            $borderline = '+';
+            foreach ($width as $w) {
+                $borderline .= str_repeat('-', $w + strlen($cellstart) + strlen($cellend) - 1);
+                $borderline .= '+';
+            }
+        }
+        if ($borderline) {
+            $this->_displayLine($borderline);
+        }
+        for ($i = 0; $i < sizeof($table_data); $i++) {
+            extract($table_data[$i]);
+            if (!is_array($rowparams)) {
+                $rowparams = array();
+            }
+            if (!is_array($colparams)) {
+                $colparams = array();
+            }
+            $rowlines = array();
+            if ($height > 1) {
+                for ($c = 0; $c < sizeof($data); $c++) {
+                    $rowlines[$c] = preg_split('/(\r?\n|\r)/', $data[$c]);
+                    if (sizeof($rowlines[$c]) < $height) {
+                        $rowlines[$c] = array_pad($rowlines[$c], $height, '');
+                    }
+                }
+            } else {
+                for ($c = 0; $c < sizeof($data); $c++) {
+                    $rowlines[$c] = array($data[$c]);
+                }
+            }
+            for ($r = 0; $r < $height; $r++) {
+                $rowtext = '';
+                for ($c = 0; $c < sizeof($data); $c++) {
+                    if (isset($colparams[$c])) {
+                        $attribs = array_merge($rowparams, $colparams);
+                    } else {
+                        $attribs = $rowparams;
+                    }
+                    $w = isset($width[$c]) ? $width[$c] : 0;
+                    //$cell = $data[$c];
+                    $cell = $rowlines[$c][$r];
+                    $l = strlen($cell);
+                    if ($l > $w) {
+                        $cell = substr($cell, 0, $w);
+                    }
+                    if (isset($attribs['bold'])) {
+                        $cell = $this->bold($cell);
+                    }
+                    if ($l < $w) {
+                        // not using str_pad here because we may
+                        // add bold escape characters to $cell
+                        $cell .= str_repeat(' ', $w - $l);
+                    }
+
+                    $rowtext .= $cellstart . $cell . $cellend;
+                }
+                if (!$border) {
+                    $rowtext = rtrim($rowtext);
+                }
+                $rowtext .= $rowend;
+                $this->_displayLine($rowtext);
+            }
+        }
+        if ($borderline) {
+            $this->_displayLine($borderline);
+        }
+    }
+
+    // }}}
+    // {{{ outputData()
+
+    function outputData($data, $command = '_default')
+    {
+        switch ($command) {
+            case 'install':
+            case 'upgrade':
+            case 'upgrade-all':
+                if (isset($data['release_warnings'])) {
+                    $this->_displayLine('');
+                    $this->_startTable(array(
+                        'border' => false,
+                        'caption' => 'Release Warnings'
+                        ));
+                    $this->_tableRow(array($data['release_warnings']), null, array(1 => array('wrap' => 55)));
+                    $this->_endTable();
+                    $this->_displayLine('');
+                }
+                $this->_displayLine($data['data']);
+                break;
+            case 'search':
+                $this->_startTable($data);
+                if (isset($data['headline']) && is_array($data['headline'])) {
+                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+                }
+
+                foreach($data['data'] as $category) {
+                    foreach($category as $pkg) {
+                        $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+                    }
+                };
+                $this->_endTable();
+                break;
+            case 'list-all':
+                $this->_startTable($data);
+                if (isset($data['headline']) && is_array($data['headline'])) {
+                    $this->_tableRow($data['headline'], array('bold' => true), array(1 => array('wrap' => 55)));
+                }
+
+                foreach($data['data'] as $category) {
+                    foreach($category as $pkg) {
+                        unset($pkg[3]);
+                        unset($pkg[4]);
+                        $this->_tableRow($pkg, null, array(1 => array('wrap' => 55)));
+                    }
+                };
+                $this->_endTable();
+                break;
+            case 'config-show':
+                $data['border'] = false;
+                $opts = array(0 => array('wrap' => 30),
+                              1 => array('wrap' => 20),
+                              2 => array('wrap' => 35));
+                $this->_startTable($data);
+                if (isset($data['headline']) && is_array($data['headline'])) {
+                    $this->_tableRow($data['headline'],
+                                     array('bold' => true),
+                                     $opts);
+                }
+                foreach($data['data'] as $group) {
+                    foreach($group as $value) {
+                        if ($value[2] == '') {
+                            $value[2] = "<not set>";
+                        }
+                        $this->_tableRow($value, null, $opts);
+                    }
+                }
+                $this->_endTable();
+                break;
+            case 'remote-info':
+                $data = array(
+                    'caption' => 'Package details:',
+                    'border' => false,
+                    'data' => array(
+                        array("Latest",    $data['stable']),
+                        array("Installed", $data['installed']),
+                        array("Package",   $data['name']),
+                        array("License",   $data['license']),
+                        array("Category",  $data['category']),
+                        array("Summary",   $data['summary']),
+                        array("Description", $data['description']),
+                        ),
+                    );
+            default: {
+                if (is_array($data)) {
+                    $this->_startTable($data);
+                    $count = count($data['data'][0]);
+                    if ($count == 2) {
+                        $opts = array(0 => array('wrap' => 25),
+                                      1 => array('wrap' => 48)
+                        );
+                    } elseif ($count == 3) {
+                        $opts = array(0 => array('wrap' => 30),
+                                      1 => array('wrap' => 20),
+                                      2 => array('wrap' => 35)
+                        );
+                    } else {
+                        $opts = null;
+                    }
+                    if (isset($data['headline']) && is_array($data['headline'])) {
+                        $this->_tableRow($data['headline'],
+                                         array('bold' => true),
+                                         $opts);
+                    }
+                    foreach($data['data'] as $row) {
+                        $this->_tableRow($row, null, $opts);
+                    }
+                    $this->_endTable();
+                } else {
+                    $this->_displayLine($data);
+                }
+            }
+        }
+    }
+
+    // }}}
+    // {{{ log(text)
+
+
+    function log($text, $append_crlf = true)
+    {
+        if ($append_crlf) {
+            return $this->_displayLine($text);
+        }
+        return $this->_display($text);
+    }
+
+
+    // }}}
+    // {{{ bold($text)
+
+    function bold($text)
+    {
+        if (empty($this->term['bold'])) {
+            return strtoupper($text);
+        }
+        return $this->term['bold'] . $text . $this->term['normal'];
+    }
+
+    // }}}
+}
+
+?>
diff --git a/3rdparty/PEAR/Installer.php b/3rdparty/PEAR/Installer.php
new file mode 100644
index 0000000000000000000000000000000000000000..31e2cff81ee6be9e367e6e0f524a7346ce4ac58c
--- /dev/null
+++ b/3rdparty/PEAR/Installer.php
@@ -0,0 +1,1068 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net>                                   |
+// |          Tomas V.V.Cox <cox@idecnet.com>                             |
+// |          Martin Jansen <mj@php.net>                                  |
+// +----------------------------------------------------------------------+
+//
+// $Id: Installer.php,v 1.150.2.2 2005/02/17 17:47:55 cellog Exp $
+
+require_once 'PEAR/Downloader.php';
+
+/**
+ * Administration class used to install PEAR packages and maintain the
+ * installed package database.
+ *
+ * TODO:
+ *   - Check dependencies break on package uninstall (when no force given)
+ *   - add a guessInstallDest() method with the code from _installFile() and
+ *     use that method in Registry::_rebuildFileMap() & Command_Registry::doList(),
+ *     others..
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <ssb@php.net>
+ * @author Martin Jansen <mj@php.net>
+ * @author Greg Beaver <cellog@php.net>
+ */
+class PEAR_Installer extends PEAR_Downloader
+{
+    // {{{ properties
+
+    /** name of the package directory, for example Foo-1.0
+     * @var string
+     */
+    var $pkgdir;
+
+    /** directory where PHP code files go
+     * @var string
+     */
+    var $phpdir;
+
+    /** directory where PHP extension files go
+     * @var string
+     */
+    var $extdir;
+
+    /** directory where documentation goes
+     * @var string
+     */
+    var $docdir;
+
+    /** installation root directory (ala PHP's INSTALL_ROOT or
+     * automake's DESTDIR
+     * @var string
+     */
+    var $installroot = '';
+
+    /** debug level
+     * @var int
+     */
+    var $debug = 1;
+
+    /** temporary directory
+     * @var string
+     */
+    var $tmpdir;
+
+    /** PEAR_Registry object used by the installer
+     * @var object
+     */
+    var $registry;
+
+    /** List of file transactions queued for an install/upgrade/uninstall.
+     *
+     *  Format:
+     *    array(
+     *      0 => array("rename => array("from-file", "to-file")),
+     *      1 => array("delete" => array("file-to-delete")),
+     *      ...
+     *    )
+     *
+     * @var array
+     */
+    var $file_operations = array();
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Installer constructor.
+     *
+     * @param object $ui user interface object (instance of PEAR_Frontend_*)
+     *
+     * @access public
+     */
+    function PEAR_Installer(&$ui)
+    {
+        parent::PEAR_Common();
+        $this->setFrontendObject($ui);
+        $this->debug = $this->config->get('verbose');
+        //$this->registry = &new PEAR_Registry($this->config->get('php_dir'));
+    }
+
+    // }}}
+
+    // {{{ _deletePackageFiles()
+
+    /**
+     * Delete a package's installed files, does not remove empty directories.
+     *
+     * @param string $package package name
+     *
+     * @return bool TRUE on success, or a PEAR error on failure
+     *
+     * @access private
+     */
+    function _deletePackageFiles($package)
+    {
+        if (!strlen($package)) {
+            return $this->raiseError("No package to uninstall given");
+        }
+        $filelist = $this->registry->packageInfo($package, 'filelist');
+        if ($filelist == null) {
+            return $this->raiseError("$package not installed");
+        }
+        foreach ($filelist as $file => $props) {
+            if (empty($props['installed_as'])) {
+                continue;
+            }
+            $path = $this->_prependPath($props['installed_as'], $this->installroot);
+            $this->addFileOperation('delete', array($path));
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _installFile()
+
+    /**
+     * @param string filename
+     * @param array attributes from <file> tag in package.xml
+     * @param string path to install the file in
+     * @param array options from command-line
+     * @access private
+     */
+    function _installFile($file, $atts, $tmp_path, $options)
+    {
+        // {{{ return if this file is meant for another platform
+        static $os;
+        if (isset($atts['platform'])) {
+            if (empty($os)) {
+                include_once "OS/Guess.php";
+                $os = new OS_Guess();
+            }
+            if (strlen($atts['platform']) && $atts['platform']{0} == '!') {
+                $negate = true;
+                $platform = substr($atts['platform'], 1);
+            } else {
+                $negate = false;
+                $platform = $atts['platform'];
+            }
+            if ((bool) $os->matchSignature($platform) === $negate) {
+                $this->log(3, "skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
+                return PEAR_INSTALLER_SKIPPED;
+            }
+        }
+        // }}}
+
+        // {{{ assemble the destination paths
+        switch ($atts['role']) {
+            case 'doc':
+            case 'data':
+            case 'test':
+                $dest_dir = $this->config->get($atts['role'] . '_dir') .
+                            DIRECTORY_SEPARATOR . $this->pkginfo['package'];
+                unset($atts['baseinstalldir']);
+                break;
+            case 'ext':
+            case 'php':
+                $dest_dir = $this->config->get($atts['role'] . '_dir');
+                break;
+            case 'script':
+                $dest_dir = $this->config->get('bin_dir');
+                break;
+            case 'src':
+            case 'extsrc':
+                $this->source_files++;
+                return;
+            default:
+                return $this->raiseError("Invalid role `$atts[role]' for file $file");
+        }
+        $save_destdir = $dest_dir;
+        if (!empty($atts['baseinstalldir'])) {
+            $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
+        }
+        if (dirname($file) != '.' && empty($atts['install-as'])) {
+            $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
+        }
+        if (empty($atts['install-as'])) {
+            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . basename($file);
+        } else {
+            $dest_file = $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
+        }
+        $orig_file = $tmp_path . DIRECTORY_SEPARATOR . $file;
+
+        // Clean up the DIRECTORY_SEPARATOR mess
+        $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
+        list($dest_file, $orig_file) = preg_replace(array('!\\\\+!', '!/!', "!$ds2+!"),
+                                                    DIRECTORY_SEPARATOR,
+                                                    array($dest_file, $orig_file));
+        $installed_as = $dest_file;
+        $final_dest_file = $this->_prependPath($dest_file, $this->installroot);
+        $dest_dir = dirname($final_dest_file);
+        $dest_file = $dest_dir . DIRECTORY_SEPARATOR . '.tmp' . basename($final_dest_file);
+        // }}}
+
+        if (!@is_dir($dest_dir)) {
+            if (!$this->mkDirHier($dest_dir)) {
+                return $this->raiseError("failed to mkdir $dest_dir",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            $this->log(3, "+ mkdir $dest_dir");
+        }
+        if (empty($atts['replacements'])) {
+            if (!file_exists($orig_file)) {
+                return $this->raiseError("file does not exist",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            if (!@copy($orig_file, $dest_file)) {
+                return $this->raiseError("failed to write $dest_file",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            $this->log(3, "+ cp $orig_file $dest_file");
+            if (isset($atts['md5sum'])) {
+                $md5sum = md5_file($dest_file);
+            }
+        } else {
+            // {{{ file with replacements
+            if (!file_exists($orig_file)) {
+                return $this->raiseError("file does not exist",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            $fp = fopen($orig_file, "r");
+            $contents = fread($fp, filesize($orig_file));
+            fclose($fp);
+            if (isset($atts['md5sum'])) {
+                $md5sum = md5($contents);
+            }
+            $subst_from = $subst_to = array();
+            foreach ($atts['replacements'] as $a) {
+                $to = '';
+                if ($a['type'] == 'php-const') {
+                    if (preg_match('/^[a-z0-9_]+$/i', $a['to'])) {
+                        eval("\$to = $a[to];");
+                    } else {
+                        $this->log(0, "invalid php-const replacement: $a[to]");
+                        continue;
+                    }
+                } elseif ($a['type'] == 'pear-config') {
+                    $to = $this->config->get($a['to']);
+                    if (is_null($to)) {
+                        $this->log(0, "invalid pear-config replacement: $a[to]");
+                        continue;
+                    }
+                } elseif ($a['type'] == 'package-info') {
+                    if (isset($this->pkginfo[$a['to']]) && is_string($this->pkginfo[$a['to']])) {
+                        $to = $this->pkginfo[$a['to']];
+                    } else {
+                        $this->log(0, "invalid package-info replacement: $a[to]");
+                        continue;
+                    }
+                }
+                if (!is_null($to)) {
+                    $subst_from[] = $a['from'];
+                    $subst_to[] = $to;
+                }
+            }
+            $this->log(3, "doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
+            if (sizeof($subst_from)) {
+                $contents = str_replace($subst_from, $subst_to, $contents);
+            }
+            $wp = @fopen($dest_file, "wb");
+            if (!is_resource($wp)) {
+                return $this->raiseError("failed to create $dest_file: $php_errormsg",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            if (!fwrite($wp, $contents)) {
+                return $this->raiseError("failed writing to $dest_file: $php_errormsg",
+                                         PEAR_INSTALLER_FAILED);
+            }
+            fclose($wp);
+            // }}}
+        }
+        // {{{ check the md5
+        if (isset($md5sum)) {
+            if (strtolower($md5sum) == strtolower($atts['md5sum'])) {
+                $this->log(2, "md5sum ok: $final_dest_file");
+            } else {
+                if (empty($options['force'])) {
+                    // delete the file
+                    @unlink($dest_file);
+                    return $this->raiseError("bad md5sum for file $final_dest_file",
+                                             PEAR_INSTALLER_FAILED);
+                } else {
+                    $this->log(0, "warning : bad md5sum for file $final_dest_file");
+                }
+            }
+        }
+        // }}}
+        // {{{ set file permissions
+        if (!OS_WINDOWS) {
+            if ($atts['role'] == 'script') {
+                $mode = 0777 & ~(int)octdec($this->config->get('umask'));
+                $this->log(3, "+ chmod +x $dest_file");
+            } else {
+                $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+            }
+            $this->addFileOperation("chmod", array($mode, $dest_file));
+            if (!@chmod($dest_file, $mode)) {
+                $this->log(0, "failed to change mode of $dest_file");
+            }
+        }
+        // }}}
+        $this->addFileOperation("rename", array($dest_file, $final_dest_file));
+        // Store the full path where the file was installed for easy unistall
+        $this->addFileOperation("installed_as", array($file, $installed_as,
+                                $save_destdir, dirname(substr($dest_file, strlen($save_destdir)))));
+
+        //$this->log(2, "installed: $dest_file");
+        return PEAR_INSTALLER_OK;
+    }
+
+    // }}}
+    // {{{ addFileOperation()
+
+    /**
+     * Add a file operation to the current file transaction.
+     *
+     * @see startFileTransaction()
+     * @var string $type This can be one of:
+     *    - rename:  rename a file ($data has 2 values)
+     *    - chmod:   change permissions on a file ($data has 2 values)
+     *    - delete:  delete a file ($data has 1 value)
+     *    - rmdir:   delete a directory if empty ($data has 1 value)
+     *    - installed_as: mark a file as installed ($data has 4 values).
+     * @var array $data For all file operations, this array must contain the
+     *    full path to the file or directory that is being operated on.  For
+     *    the rename command, the first parameter must be the file to rename,
+     *    the second its new name.
+     *
+     *    The installed_as operation contains 4 elements in this order:
+     *    1. Filename as listed in the filelist element from package.xml
+     *    2. Full path to the installed file
+     *    3. Full path from the php_dir configuration variable used in this
+     *       installation
+     *    4. Relative path from the php_dir that this file is installed in
+     */
+    function addFileOperation($type, $data)
+    {
+        if (!is_array($data)) {
+            return $this->raiseError('Internal Error: $data in addFileOperation'
+                . ' must be an array, was ' . gettype($data));
+        }
+        if ($type == 'chmod') {
+            $octmode = decoct($data[0]);
+            $this->log(3, "adding to transaction: $type $octmode $data[1]");
+        } else {
+            $this->log(3, "adding to transaction: $type " . implode(" ", $data));
+        }
+        $this->file_operations[] = array($type, $data);
+    }
+
+    // }}}
+    // {{{ startFileTransaction()
+
+    function startFileTransaction($rollback_in_case = false)
+    {
+        if (count($this->file_operations) && $rollback_in_case) {
+            $this->rollbackFileTransaction();
+        }
+        $this->file_operations = array();
+    }
+
+    // }}}
+    // {{{ commitFileTransaction()
+
+    function commitFileTransaction()
+    {
+        $n = count($this->file_operations);
+        $this->log(2, "about to commit $n file operations");
+        // {{{ first, check permissions and such manually
+        $errors = array();
+        foreach ($this->file_operations as $tr) {
+            list($type, $data) = $tr;
+            switch ($type) {
+                case 'rename':
+                    if (!file_exists($data[0])) {
+                        $errors[] = "cannot rename file $data[0], doesn't exist";
+                    }
+                    // check that dest dir. is writable
+                    if (!is_writable(dirname($data[1]))) {
+                        $errors[] = "permission denied ($type): $data[1]";
+                    }
+                    break;
+                case 'chmod':
+                    // check that file is writable
+                    if (!is_writable($data[1])) {
+                        $errors[] = "permission denied ($type): $data[1] " . decoct($data[0]);
+                    }
+                    break;
+                case 'delete':
+                    if (!file_exists($data[0])) {
+                        $this->log(2, "warning: file $data[0] doesn't exist, can't be deleted");
+                    }
+                    // check that directory is writable
+                    if (file_exists($data[0]) && !is_writable(dirname($data[0]))) {
+                        $errors[] = "permission denied ($type): $data[0]";
+                    }
+                    break;
+            }
+
+        }
+        // }}}
+        $m = sizeof($errors);
+        if ($m > 0) {
+            foreach ($errors as $error) {
+                $this->log(1, $error);
+            }
+            return false;
+        }
+        // {{{ really commit the transaction
+        foreach ($this->file_operations as $tr) {
+            list($type, $data) = $tr;
+            switch ($type) {
+                case 'rename':
+                    @unlink($data[1]);
+                    @rename($data[0], $data[1]);
+                    $this->log(3, "+ mv $data[0] $data[1]");
+                    break;
+                case 'chmod':
+                    @chmod($data[1], $data[0]);
+                    $octmode = decoct($data[0]);
+                    $this->log(3, "+ chmod $octmode $data[1]");
+                    break;
+                case 'delete':
+                    @unlink($data[0]);
+                    $this->log(3, "+ rm $data[0]");
+                    break;
+                case 'rmdir':
+                    @rmdir($data[0]);
+                    $this->log(3, "+ rmdir $data[0]");
+                    break;
+                case 'installed_as':
+                    $this->pkginfo['filelist'][$data[0]]['installed_as'] = $data[1];
+                    if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
+                        $this->pkginfo['filelist']['dirtree'][dirname($data[1])] = true;
+                        while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
+                              && $data[3] != '.') {
+                            $this->pkginfo['filelist']['dirtree']
+                                [$this->_prependPath($data[3], $data[2])] = true;
+                            $data[3] = dirname($data[3]);
+                        }
+                    }
+                    break;
+            }
+        }
+        // }}}
+        $this->log(2, "successfully committed $n file operations");
+        $this->file_operations = array();
+        return true;
+    }
+
+    // }}}
+    // {{{ rollbackFileTransaction()
+
+    function rollbackFileTransaction()
+    {
+        $n = count($this->file_operations);
+        $this->log(2, "rolling back $n file operations");
+        foreach ($this->file_operations as $tr) {
+            list($type, $data) = $tr;
+            switch ($type) {
+                case 'rename':
+                    @unlink($data[0]);
+                    $this->log(3, "+ rm $data[0]");
+                    break;
+                case 'mkdir':
+                    @rmdir($data[0]);
+                    $this->log(3, "+ rmdir $data[0]");
+                    break;
+                case 'chmod':
+                    break;
+                case 'delete':
+                    break;
+                case 'installed_as':
+                    if (isset($this->pkginfo['filelist'])) {
+                        unset($this->pkginfo['filelist'][$data[0]]['installed_as']);
+                    }
+                    if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
+                        unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]);
+                        while(!empty($data[3]) && $data[3] != '/' && $data[3] != '\\'
+                              && $data[3] != '.') {
+                            unset($this->pkginfo['filelist']['dirtree']
+                                [$this->_prependPath($data[3], $data[2])]);
+                            $data[3] = dirname($data[3]);
+                        }
+                    }
+                    if (isset($this->pkginfo['filelist']['dirtree'])
+                          && !count($this->pkginfo['filelist']['dirtree'])) {
+                        unset($this->pkginfo['filelist']['dirtree']);
+                    }
+                    break;
+            }
+        }
+        $this->file_operations = array();
+    }
+
+    // }}}
+    // {{{ mkDirHier($dir)
+
+    function mkDirHier($dir)
+    {
+        $this->addFileOperation('mkdir', array($dir));
+        return parent::mkDirHier($dir);
+    }
+
+    // }}}
+    // {{{ download()
+
+    /**
+     * Download any files and their dependencies, if necessary
+     *
+     * @param array a mixed list of package names, local files, or package.xml
+     * @param PEAR_Config
+     * @param array options from the command line
+     * @param array this is the array that will be populated with packages to
+     *              install.  Format of each entry:
+     *
+     * <code>
+     * array('pkg' => 'package_name', 'file' => '/path/to/local/file',
+     *    'info' => array() // parsed package.xml
+     * );
+     * </code>
+     * @param array this will be populated with any error messages
+     * @param false private recursion variable
+     * @param false private recursion variable
+     * @param false private recursion variable
+     * @deprecated in favor of PEAR_Downloader
+     */
+    function download($packages, $options, &$config, &$installpackages,
+                      &$errors, $installed = false, $willinstall = false, $state = false)
+    {
+        // trickiness: initialize here
+        parent::PEAR_Downloader($this->ui, $options, $config);
+        $ret = parent::download($packages);
+        $errors = $this->getErrorMsgs();
+        $installpackages = $this->getDownloadedPackages();
+        trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
+                      "in favor of PEAR_Downloader class", E_USER_WARNING);
+        return $ret;
+    }
+
+    // }}}
+    // {{{ install()
+
+    /**
+     * Installs the files within the package file specified.
+     *
+     * @param string $pkgfile path to the package file
+     * @param array $options
+     * recognized options:
+     * - installroot   : optional prefix directory for installation
+     * - force         : force installation
+     * - register-only : update registry but don't install files
+     * - upgrade       : upgrade existing install
+     * - soft          : fail silently
+     * - nodeps        : ignore dependency conflicts/missing dependencies
+     * - alldeps       : install all dependencies
+     * - onlyreqdeps   : install only required dependencies
+     *
+     * @return array|PEAR_Error package info if successful
+     */
+
+    function install($pkgfile, $options = array())
+    {
+        $php_dir = $this->config->get('php_dir');
+        if (isset($options['installroot'])) {
+            if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+                $options['installroot'] = substr($options['installroot'], 0, -1);
+            }
+            $php_dir = $this->_prependPath($php_dir, $options['installroot']);
+            $this->installroot = $options['installroot'];
+        } else {
+            $this->installroot = '';
+        }
+        $this->registry = &new PEAR_Registry($php_dir);
+        //  ==> XXX should be removed later on
+        $flag_old_format = false;
+
+        if (substr($pkgfile, -4) == '.xml') {
+            $descfile = $pkgfile;
+        } else {
+            // {{{ Decompress pack in tmp dir -------------------------------------
+
+            // To allow relative package file names
+            $pkgfile = realpath($pkgfile);
+
+            if (PEAR::isError($tmpdir = System::mktemp('-d'))) {
+                return $tmpdir;
+            }
+            $this->log(3, '+ tmp dir created at ' . $tmpdir);
+
+            $tar = new Archive_Tar($pkgfile);
+            if (!@$tar->extract($tmpdir)) {
+                return $this->raiseError("unable to unpack $pkgfile");
+            }
+
+            // {{{ Look for existing package file
+            $descfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
+
+            if (!is_file($descfile)) {
+                // ----- Look for old package archive format
+                // In this format the package.xml file was inside the
+                // Package-n.n directory
+                $dp = opendir($tmpdir);
+                do {
+                    $pkgdir = readdir($dp);
+                } while ($pkgdir{0} == '.');
+
+                $descfile = $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml';
+                $flag_old_format = true;
+                $this->log(0, "warning : you are using an archive with an old format");
+            }
+            // }}}
+            // <== XXX This part should be removed later on
+            // }}}
+        }
+
+        if (!is_file($descfile)) {
+            return $this->raiseError("no package.xml file after extracting the archive");
+        }
+
+        // Parse xml file -----------------------------------------------
+        $pkginfo = $this->infoFromDescriptionFile($descfile);
+        if (PEAR::isError($pkginfo)) {
+            return $pkginfo;
+        }
+        $this->validatePackageInfo($pkginfo, $errors, $warnings);
+        // XXX We allow warnings, do we have to do it?
+        if (count($errors)) {
+            if (empty($options['force'])) {
+                return $this->raiseError("The following errors where found (use force option to install anyway):\n".
+                                         implode("\n", $errors));
+            } else {
+                $this->log(0, "warning : the following errors were found:\n".
+                           implode("\n", $errors));
+            }
+        }
+
+        $pkgname = $pkginfo['package'];
+
+        // {{{ Check dependencies -------------------------------------------
+        if (isset($pkginfo['release_deps']) && empty($options['nodeps'])) {
+            $dep_errors = '';
+            $error = $this->checkDeps($pkginfo, $dep_errors);
+            if ($error == true) {
+                if (empty($options['soft'])) {
+                    $this->log(0, substr($dep_errors, 1));
+                }
+                return $this->raiseError("$pkgname: Dependencies failed");
+            } else if (!empty($dep_errors)) {
+                // Print optional dependencies
+                if (empty($options['soft'])) {
+                    $this->log(0, $dep_errors);
+                }
+            }
+        }
+        // }}}
+
+        // {{{ checks to do when not in "force" mode
+        if (empty($options['force'])) {
+            $test = $this->registry->checkFileMap($pkginfo);
+            if (sizeof($test)) {
+                $tmp = $test;
+                foreach ($tmp as $file => $pkg) {
+                    if ($pkg == $pkgname) {
+                        unset($test[$file]);
+                    }
+                }
+                if (sizeof($test)) {
+                    $msg = "$pkgname: conflicting files found:\n";
+                    $longest = max(array_map("strlen", array_keys($test)));
+                    $fmt = "%${longest}s (%s)\n";
+                    foreach ($test as $file => $pkg) {
+                        $msg .= sprintf($fmt, $file, $pkg);
+                    }
+                    return $this->raiseError($msg);
+                }
+            }
+        }
+        // }}}
+
+        $this->startFileTransaction();
+
+        if (empty($options['upgrade'])) {
+            // checks to do only when installing new packages
+            if (empty($options['force']) && $this->registry->packageExists($pkgname)) {
+                return $this->raiseError("$pkgname already installed");
+            }
+        } else {
+            if ($this->registry->packageExists($pkgname)) {
+                $v1 = $this->registry->packageInfo($pkgname, 'version');
+                $v2 = $pkginfo['version'];
+                $cmp = version_compare("$v1", "$v2", 'gt');
+                if (empty($options['force']) && !version_compare("$v2", "$v1", 'gt')) {
+                    return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
+                }
+                if (empty($options['register-only'])) {
+                    // when upgrading, remove old release's files first:
+                    if (PEAR::isError($err = $this->_deletePackageFiles($pkgname))) {
+                        return $this->raiseError($err);
+                    }
+                }
+            }
+        }
+
+        // {{{ Copy files to dest dir ---------------------------------------
+
+        // info from the package it self we want to access from _installFile
+        $this->pkginfo = &$pkginfo;
+        // used to determine whether we should build any C code
+        $this->source_files = 0;
+
+        if (empty($options['register-only'])) {
+            if (!is_dir($php_dir)) {
+                return $this->raiseError("no script destination directory\n",
+                                         null, PEAR_ERROR_DIE);
+            }
+
+            $tmp_path = dirname($descfile);
+            if (substr($pkgfile, -4) != '.xml') {
+                $tmp_path .= DIRECTORY_SEPARATOR . $pkgname . '-' . $pkginfo['version'];
+            }
+
+            //  ==> XXX This part should be removed later on
+            if ($flag_old_format) {
+                $tmp_path = dirname($descfile);
+            }
+            // <== XXX This part should be removed later on
+
+            // {{{ install files
+            foreach ($pkginfo['filelist'] as $file => $atts) {
+                $this->expectError(PEAR_INSTALLER_FAILED);
+                $res = $this->_installFile($file, $atts, $tmp_path, $options);
+                $this->popExpect();
+                if (PEAR::isError($res)) {
+                    if (empty($options['ignore-errors'])) {
+                        $this->rollbackFileTransaction();
+                        if ($res->getMessage() == "file does not exist") {
+                            $this->raiseError("file $file in package.xml does not exist");
+                        }
+                        return $this->raiseError($res);
+                    } else {
+                        $this->log(0, "Warning: " . $res->getMessage());
+                    }
+                }
+                if ($res != PEAR_INSTALLER_OK) {
+                    // Do not register files that were not installed
+                    unset($pkginfo['filelist'][$file]);
+                }
+            }
+            // }}}
+
+            // {{{ compile and install source files
+            if ($this->source_files > 0 && empty($options['nobuild'])) {
+                $this->log(1, "$this->source_files source files, building");
+                $bob = &new PEAR_Builder($this->ui);
+                $bob->debug = $this->debug;
+                $built = $bob->build($descfile, array(&$this, '_buildCallback'));
+                if (PEAR::isError($built)) {
+                    $this->rollbackFileTransaction();
+                    return $built;
+                }
+                $this->log(1, "\nBuild process completed successfully");
+                foreach ($built as $ext) {
+                    $bn = basename($ext['file']);
+                    list($_ext_name, $_ext_suff) = explode('.', $bn);
+                    if ($_ext_suff == '.so' || $_ext_suff == '.dll') {
+                        if (extension_loaded($_ext_name)) {
+                            $this->raiseError("Extension '$_ext_name' already loaded. " .
+                                              'Please unload it in your php.ini file ' .
+                                              'prior to install or upgrade');
+                        }
+                        $role = 'ext';
+                    } else {
+                        $role = 'src';
+                    }
+                    $dest = $ext['dest'];
+                    $this->log(1, "Installing '$ext[file]'");
+                    $copyto = $this->_prependPath($dest, $this->installroot);
+                    $copydir = dirname($copyto);
+                    if (!@is_dir($copydir)) {
+                        if (!$this->mkDirHier($copydir)) {
+                            return $this->raiseError("failed to mkdir $copydir",
+                                PEAR_INSTALLER_FAILED);
+                        }
+                        $this->log(3, "+ mkdir $copydir");
+                    }
+                    if (!@copy($ext['file'], $copyto)) {
+                        return $this->raiseError("failed to write $copyto", PEAR_INSTALLER_FAILED);
+                    }
+                    $this->log(3, "+ cp $ext[file] $copyto");
+                    if (!OS_WINDOWS) {
+                        $mode = 0666 & ~(int)octdec($this->config->get('umask'));
+                        $this->addFileOperation('chmod', array($mode, $copyto));
+                        if (!@chmod($copyto, $mode)) {
+                            $this->log(0, "failed to change mode of $copyto");
+                        }
+                    }
+                    $this->addFileOperation('rename', array($ext['file'], $copyto));
+
+                    $pkginfo['filelist'][$bn] = array(
+                        'role' => $role,
+                        'installed_as' => $dest,
+                        'php_api' => $ext['php_api'],
+                        'zend_mod_api' => $ext['zend_mod_api'],
+                        'zend_ext_api' => $ext['zend_ext_api'],
+                        );
+                }
+            }
+            // }}}
+        }
+
+        if (!$this->commitFileTransaction()) {
+            $this->rollbackFileTransaction();
+            return $this->raiseError("commit failed", PEAR_INSTALLER_FAILED);
+        }
+        // }}}
+
+        $ret = false;
+        // {{{ Register that the package is installed -----------------------
+        if (empty($options['upgrade'])) {
+            // if 'force' is used, replace the info in registry
+            if (!empty($options['force']) && $this->registry->packageExists($pkgname)) {
+                $this->registry->deletePackage($pkgname);
+            }
+            $ret = $this->registry->addPackage($pkgname, $pkginfo);
+        } else {
+            // new: upgrade installs a package if it isn't installed
+            if (!$this->registry->packageExists($pkgname)) {
+                $ret = $this->registry->addPackage($pkgname, $pkginfo);
+            } else {
+                $ret = $this->registry->updatePackage($pkgname, $pkginfo, false);
+            }
+        }
+        if (!$ret) {
+            return $this->raiseError("Adding package $pkgname to registry failed");
+        }
+        // }}}
+        return $pkginfo;
+    }
+
+    // }}}
+    // {{{ uninstall()
+
+    /**
+     * Uninstall a package
+     *
+     * This method removes all files installed by the application, and then
+     * removes any empty directories.
+     * @param string package name
+     * @param array Command-line options.  Possibilities include:
+     *
+     *              - installroot: base installation dir, if not the default
+     *              - nodeps: do not process dependencies of other packages to ensure
+     *                        uninstallation does not break things
+     */
+    function uninstall($package, $options = array())
+    {
+        $php_dir = $this->config->get('php_dir');
+        if (isset($options['installroot'])) {
+            if (substr($options['installroot'], -1) == DIRECTORY_SEPARATOR) {
+                $options['installroot'] = substr($options['installroot'], 0, -1);
+            }
+            $this->installroot = $options['installroot'];
+            $php_dir = $this->_prependPath($php_dir, $this->installroot);
+        } else {
+            $this->installroot = '';
+        }
+        $this->registry = &new PEAR_Registry($php_dir);
+        $filelist = $this->registry->packageInfo($package, 'filelist');
+        if ($filelist == null) {
+            return $this->raiseError("$package not installed");
+        }
+        if (empty($options['nodeps'])) {
+            $depchecker = &new PEAR_Dependency($this->registry);
+            $error = $depchecker->checkPackageUninstall($errors, $warning, $package);
+            if ($error) {
+                return $this->raiseError($errors . 'uninstall failed');
+            }
+            if ($warning) {
+                $this->log(0, $warning);
+            }
+        }
+        // {{{ Delete the files
+        $this->startFileTransaction();
+        if (PEAR::isError($err = $this->_deletePackageFiles($package))) {
+            $this->rollbackFileTransaction();
+            return $this->raiseError($err);
+        }
+        if (!$this->commitFileTransaction()) {
+            $this->rollbackFileTransaction();
+            return $this->raiseError("uninstall failed");
+        } else {
+            $this->startFileTransaction();
+            if (!isset($filelist['dirtree']) || !count($filelist['dirtree'])) {
+                return $this->registry->deletePackage($package);
+            }
+            // attempt to delete empty directories
+            uksort($filelist['dirtree'], array($this, '_sortDirs'));
+            foreach($filelist['dirtree'] as $dir => $notused) {
+                $this->addFileOperation('rmdir', array($dir));
+            }
+            if (!$this->commitFileTransaction()) {
+                $this->rollbackFileTransaction();
+            }
+        }
+        // }}}
+
+        // Register that the package is no longer installed
+        return $this->registry->deletePackage($package);
+    }
+
+    // }}}
+    // {{{ _sortDirs()
+    function _sortDirs($a, $b)
+    {
+        if (strnatcmp($a, $b) == -1) return 1;
+        if (strnatcmp($a, $b) == 1) return -1;
+        return 0;
+    }
+
+    // }}}
+    // {{{ checkDeps()
+
+    /**
+     * Check if the package meets all dependencies
+     *
+     * @param  array   Package information (passed by reference)
+     * @param  string  Error message (passed by reference)
+     * @return boolean False when no error occured, otherwise true
+     */
+    function checkDeps(&$pkginfo, &$errors)
+    {
+        if (empty($this->registry)) {
+            $this->registry = &new PEAR_Registry($this->config->get('php_dir'));
+        }
+        $depchecker = &new PEAR_Dependency($this->registry);
+        $error = $errors = '';
+        $failed_deps = $optional_deps = array();
+        if (is_array($pkginfo['release_deps'])) {
+            foreach($pkginfo['release_deps'] as $dep) {
+                $code = $depchecker->callCheckMethod($error, $dep);
+                if ($code) {
+                    if (isset($dep['optional']) && $dep['optional'] == 'yes') {
+                        $optional_deps[] = array($dep, $code, $error);
+                    } else {
+                        $failed_deps[] = array($dep, $code, $error);
+                    }
+                }
+            }
+            // {{{ failed dependencies
+            $n = count($failed_deps);
+            if ($n > 0) {
+                for ($i = 0; $i < $n; $i++) {
+                    if (isset($failed_deps[$i]['type'])) {
+                        $type = $failed_deps[$i]['type'];
+                    } else {
+                        $type = 'pkg';
+                    }
+                    switch ($failed_deps[$i][1]) {
+                        case PEAR_DEPENDENCY_MISSING:
+                            if ($type == 'pkg') {
+                                // install
+                            }
+                            $errors .= "\n" . $failed_deps[$i][2];
+                            break;
+                        case PEAR_DEPENDENCY_UPGRADE_MINOR:
+                            if ($type == 'pkg') {
+                                // upgrade
+                            }
+                            $errors .= "\n" . $failed_deps[$i][2];
+                            break;
+                        default:
+                            $errors .= "\n" . $failed_deps[$i][2];
+                            break;
+                    }
+                }
+                return true;
+            }
+            // }}}
+
+            // {{{ optional dependencies
+            $count_optional = count($optional_deps);
+            if ($count_optional > 0) {
+                $errors = "Optional dependencies:";
+
+                for ($i = 0; $i < $count_optional; $i++) {
+                    if (isset($optional_deps[$i]['type'])) {
+                        $type = $optional_deps[$i]['type'];
+                    } else {
+                        $type = 'pkg';
+                    }
+                    switch ($optional_deps[$i][1]) {
+                        case PEAR_DEPENDENCY_MISSING:
+                        case PEAR_DEPENDENCY_UPGRADE_MINOR:
+                        default:
+                            $errors .= "\n" . $optional_deps[$i][2];
+                            break;
+                    }
+                }
+                return false;
+            }
+            // }}}
+        }
+        return false;
+    }
+
+    // }}}
+    // {{{ _buildCallback()
+
+    function _buildCallback($what, $data)
+    {
+        if (($what == 'cmdoutput' && $this->debug > 1) ||
+            ($what == 'output' && $this->debug > 0)) {
+            $this->ui->outputData(rtrim($data), 'build');
+        }
+    }
+
+    // }}}
+}
+
+// {{{ md5_file() utility function
+if (!function_exists("md5_file")) {
+    function md5_file($filename) {
+        $fp = fopen($filename, "r");
+        if (!$fp) return null;
+        $contents = fread($fp, filesize($filename));
+        fclose($fp);
+        return md5($contents);
+    }
+}
+// }}}
+
+?>
diff --git a/3rdparty/PEAR/Packager.php b/3rdparty/PEAR/Packager.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ed209be8b28c1da639558c4b41bacdc69bc1b98
--- /dev/null
+++ b/3rdparty/PEAR/Packager.php
@@ -0,0 +1,165 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Stig Bakken <ssb@php.net>                                   |
+// |          Tomas V.V.Cox <cox@idecnet.com>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: Packager.php,v 1.53 2004/06/13 14:06:01 pajoye Exp $
+
+require_once 'PEAR/Common.php';
+require_once 'System.php';
+
+/**
+ * Administration class used to make a PEAR release tarball.
+ *
+ * TODO:
+ *  - add an extra param the dir where to place the created package
+ *
+ * @since PHP 4.0.2
+ * @author Stig Bakken <ssb@php.net>
+ */
+class PEAR_Packager extends PEAR_Common
+{
+    // {{{ constructor
+
+    function PEAR_Packager()
+    {
+        parent::PEAR_Common();
+    }
+
+    // }}}
+    // {{{ destructor
+
+    function _PEAR_Packager()
+    {
+        parent::_PEAR_Common();
+    }
+
+    // }}}
+
+    // {{{ package()
+
+    function package($pkgfile = null, $compress = true)
+    {
+        // {{{ validate supplied package.xml file
+        if (empty($pkgfile)) {
+            $pkgfile = 'package.xml';
+        }
+        // $this->pkginfo gets populated inside
+        $pkginfo = $this->infoFromDescriptionFile($pkgfile);
+        if (PEAR::isError($pkginfo)) {
+            return $this->raiseError($pkginfo);
+        }
+
+        $pkgdir = dirname(realpath($pkgfile));
+        $pkgfile = basename($pkgfile);
+
+        $errors = $warnings = array();
+        $this->validatePackageInfo($pkginfo, $errors, $warnings, $pkgdir);
+        foreach ($warnings as $w) {
+            $this->log(1, "Warning: $w");
+        }
+        foreach ($errors as $e) {
+            $this->log(0, "Error: $e");
+        }
+        if (sizeof($errors) > 0) {
+            return $this->raiseError('Errors in package');
+        }
+        // }}}
+
+        $pkgver = $pkginfo['package'] . '-' . $pkginfo['version'];
+
+        // {{{ Create the package file list
+        $filelist = array();
+        $i = 0;
+
+        foreach ($pkginfo['filelist'] as $fname => $atts) {
+            $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
+            if (!file_exists($file)) {
+                return $this->raiseError("File does not exist: $fname");
+            } else {
+                $filelist[$i++] = $file;
+                if (empty($pkginfo['filelist'][$fname]['md5sum'])) {
+                    $md5sum = md5_file($file);
+                    $pkginfo['filelist'][$fname]['md5sum'] = $md5sum;
+                }
+                $this->log(2, "Adding file $fname");
+            }
+        }
+        // }}}
+
+        // {{{ regenerate package.xml
+        $new_xml = $this->xmlFromInfo($pkginfo);
+        if (PEAR::isError($new_xml)) {
+            return $this->raiseError($new_xml);
+        }
+        if (!($tmpdir = System::mktemp(array('-d')))) {
+            return $this->raiseError("PEAR_Packager: mktemp failed");
+        }
+        $newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
+        $np = @fopen($newpkgfile, 'wb');
+        if (!$np) {
+            return $this->raiseError("PEAR_Packager: unable to rewrite $pkgfile as $newpkgfile");
+        }
+        fwrite($np, $new_xml);
+        fclose($np);
+        // }}}
+
+        // {{{ TAR the Package -------------------------------------------
+        $ext = $compress ? '.tgz' : '.tar';
+        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
+        $tar =& new Archive_Tar($dest_package, $compress);
+        $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
+        // ----- Creates with the package.xml file
+        $ok = $tar->createModify(array($newpkgfile), '', $tmpdir);
+        if (PEAR::isError($ok)) {
+            return $this->raiseError($ok);
+        } elseif (!$ok) {
+            return $this->raiseError('PEAR_Packager: tarball creation failed');
+        }
+        // ----- Add the content of the package
+        if (!$tar->addModify($filelist, $pkgver, $pkgdir)) {
+            return $this->raiseError('PEAR_Packager: tarball creation failed');
+        }
+        $this->log(1, "Package $dest_package done");
+        if (file_exists("$pkgdir/CVS/Root")) {
+            $cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pkginfo['version']);
+            $cvstag = "RELEASE_$cvsversion";
+            $this->log(1, "Tag the released code with `pear cvstag $pkgfile'");
+            $this->log(1, "(or set the CVS tag $cvstag by hand)");
+        }
+        // }}}
+
+        return $dest_package;
+    }
+
+    // }}}
+}
+
+// {{{ md5_file() utility function
+if (!function_exists('md5_file')) {
+    function md5_file($file) {
+        if (!$fd = @fopen($file, 'r')) {
+            return false;
+        }
+        $md5 = md5(fread($fd, filesize($file)));
+        fclose($fd);
+        return $md5;
+    }
+}
+// }}}
+
+?>
diff --git a/3rdparty/PEAR/Registry.php b/3rdparty/PEAR/Registry.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2510a38f15c0243b5a261fe00aef897eadc661d
--- /dev/null
+++ b/3rdparty/PEAR/Registry.php
@@ -0,0 +1,538 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// |         Tomas V.V.Cox <cox@idecnet.com>                              |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: Registry.php,v 1.50.4.3 2004/10/26 19:19:56 cellog Exp $
+
+/*
+TODO:
+    - Transform into singleton()
+    - Add application level lock (avoid change the registry from the cmdline
+      while using the GTK interface, for ex.)
+*/
+require_once "System.php";
+require_once "PEAR.php";
+
+define('PEAR_REGISTRY_ERROR_LOCK',   -2);
+define('PEAR_REGISTRY_ERROR_FORMAT', -3);
+define('PEAR_REGISTRY_ERROR_FILE',   -4);
+
+/**
+ * Administration class used to maintain the installed package database.
+ */
+class PEAR_Registry extends PEAR
+{
+    // {{{ properties
+
+    /** Directory where registry files are stored.
+     * @var string
+     */
+    var $statedir = '';
+
+    /** File where the file map is stored
+     * @var string
+     */
+    var $filemap = '';
+
+    /** Name of file used for locking the registry
+     * @var string
+     */
+    var $lockfile = '';
+
+    /** File descriptor used during locking
+     * @var resource
+     */
+    var $lock_fp = null;
+
+    /** Mode used during locking
+     * @var int
+     */
+    var $lock_mode = 0; // XXX UNUSED
+
+    /** Cache of package information.  Structure:
+     * array(
+     *   'package' => array('id' => ... ),
+     *   ... )
+     * @var array
+     */
+    var $pkginfo_cache = array();
+
+    /** Cache of file map.  Structure:
+     * array( '/path/to/file' => 'package', ... )
+     * @var array
+     */
+    var $filemap_cache = array();
+
+    // }}}
+
+    // {{{ constructor
+
+    /**
+     * PEAR_Registry constructor.
+     *
+     * @param string (optional) PEAR install directory (for .php files)
+     *
+     * @access public
+     */
+    function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR)
+    {
+        parent::PEAR();
+        $ds = DIRECTORY_SEPARATOR;
+        $this->install_dir = $pear_install_dir;
+        $this->statedir = $pear_install_dir.$ds.'.registry';
+        $this->filemap  = $pear_install_dir.$ds.'.filemap';
+        $this->lockfile = $pear_install_dir.$ds.'.lock';
+
+        // XXX Compatibility code should be removed in the future
+        // rename all registry files if any to lowercase
+        if (!OS_WINDOWS && $handle = @opendir($this->statedir)) {
+            $dest = $this->statedir . DIRECTORY_SEPARATOR;
+            while (false !== ($file = readdir($handle))) {
+                if (preg_match('/^.*[A-Z].*\.reg$/', $file)) {
+                    rename($dest . $file, $dest . strtolower($file));
+                }
+            }
+            closedir($handle);
+        }
+        if (!file_exists($this->filemap)) {
+            $this->rebuildFileMap();
+        }
+    }
+
+    // }}}
+    // {{{ destructor
+
+    /**
+     * PEAR_Registry destructor.  Makes sure no locks are forgotten.
+     *
+     * @access private
+     */
+    function _PEAR_Registry()
+    {
+        parent::_PEAR();
+        if (is_resource($this->lock_fp)) {
+            $this->_unlock();
+        }
+    }
+
+    // }}}
+
+    // {{{ _assertStateDir()
+
+    /**
+     * Make sure the directory where we keep registry files exists.
+     *
+     * @return bool TRUE if directory exists, FALSE if it could not be
+     * created
+     *
+     * @access private
+     */
+    function _assertStateDir()
+    {
+        if (!@is_dir($this->statedir)) {
+            if (!System::mkdir(array('-p', $this->statedir))) {
+                return $this->raiseError("could not create directory '{$this->statedir}'");
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _packageFileName()
+
+    /**
+     * Get the name of the file where data for a given package is stored.
+     *
+     * @param string package name
+     *
+     * @return string registry file name
+     *
+     * @access public
+     */
+    function _packageFileName($package)
+    {
+        return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg';
+    }
+
+    // }}}
+    // {{{ _openPackageFile()
+
+    function _openPackageFile($package, $mode)
+    {
+        $this->_assertStateDir();
+        $file = $this->_packageFileName($package);
+        $fp = @fopen($file, $mode);
+        if (!$fp) {
+            return null;
+        }
+        return $fp;
+    }
+
+    // }}}
+    // {{{ _closePackageFile()
+
+    function _closePackageFile($fp)
+    {
+        fclose($fp);
+    }
+
+    // }}}
+    // {{{ rebuildFileMap()
+
+    function rebuildFileMap()
+    {
+        $packages = $this->listPackages();
+        $files = array();
+        foreach ($packages as $package) {
+            $version = $this->packageInfo($package, 'version');
+            $filelist = $this->packageInfo($package, 'filelist');
+            if (!is_array($filelist)) {
+                continue;
+            }
+            foreach ($filelist as $name => $attrs) {
+                if (isset($attrs['role']) && $attrs['role'] != 'php') {
+                    continue;
+                }
+                if (isset($attrs['baseinstalldir'])) {
+                    $file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+                } else {
+                    $file = $name;
+                }
+                $file = preg_replace(',^/+,', '', $file);
+                $files[$file] = $package;
+            }
+        }
+        $this->_assertStateDir();
+        $fp = @fopen($this->filemap, 'wb');
+        if (!$fp) {
+            return false;
+        }
+        $this->filemap_cache = $files;
+        fwrite($fp, serialize($files));
+        fclose($fp);
+        return true;
+    }
+
+    // }}}
+    // {{{ readFileMap()
+
+    function readFileMap()
+    {
+        $fp = @fopen($this->filemap, 'r');
+        if (!$fp) {
+            return $this->raiseError('PEAR_Registry: could not open filemap', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg);
+        }
+        $fsize = filesize($this->filemap);
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        $data = fread($fp, $fsize);
+        set_magic_quotes_runtime($rt);
+        fclose($fp);
+        $tmp = unserialize($data);
+        if (!$tmp && $fsize > 7) {
+            return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data);
+        }
+        $this->filemap_cache = $tmp;
+        return true;
+    }
+
+    // }}}
+    // {{{ _lock()
+
+    /**
+     * Lock the registry.
+     *
+     * @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN.
+     *                See flock manual for more information.
+     *
+     * @return bool TRUE on success, FALSE if locking failed, or a
+     *              PEAR error if some other error occurs (such as the
+     *              lock file not being writable).
+     *
+     * @access private
+     */
+    function _lock($mode = LOCK_EX)
+    {
+        if (!eregi('Windows 9', php_uname())) {
+            if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
+                // XXX does not check type of lock (LOCK_SH/LOCK_EX)
+                return true;
+            }
+            if (PEAR::isError($err = $this->_assertStateDir())) {
+                return $err;
+            }
+            $open_mode = 'w';
+            // XXX People reported problems with LOCK_SH and 'w'
+            if ($mode === LOCK_SH || $mode === LOCK_UN) {
+                if (@!is_file($this->lockfile)) {
+                    touch($this->lockfile);
+                }
+                $open_mode = 'r';
+            }
+
+            if (!is_resource($this->lock_fp)) {
+                $this->lock_fp = @fopen($this->lockfile, $open_mode);
+            }
+
+            if (!is_resource($this->lock_fp)) {
+                return $this->raiseError("could not create lock file" .
+                                         (isset($php_errormsg) ? ": " . $php_errormsg : ""));
+            }
+            if (!(int)flock($this->lock_fp, $mode)) {
+                switch ($mode) {
+                    case LOCK_SH: $str = 'shared';    break;
+                    case LOCK_EX: $str = 'exclusive'; break;
+                    case LOCK_UN: $str = 'unlock';    break;
+                    default:      $str = 'unknown';   break;
+                }
+                return $this->raiseError("could not acquire $str lock ($this->lockfile)",
+                                         PEAR_REGISTRY_ERROR_LOCK);
+            }
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ _unlock()
+
+    function _unlock()
+    {
+        $ret = $this->_lock(LOCK_UN);
+        if (is_resource($this->lock_fp)) {
+            fclose($this->lock_fp);
+        }
+        $this->lock_fp = null;
+        return $ret;
+    }
+
+    // }}}
+    // {{{ _packageExists()
+
+    function _packageExists($package)
+    {
+        return file_exists($this->_packageFileName($package));
+    }
+
+    // }}}
+    // {{{ _packageInfo()
+
+    function _packageInfo($package = null, $key = null)
+    {
+        if ($package === null) {
+            return array_map(array($this, '_packageInfo'),
+                             $this->_listPackages());
+        }
+        $fp = $this->_openPackageFile($package, 'r');
+        if ($fp === null) {
+            return null;
+        }
+        $rt = get_magic_quotes_runtime();
+        set_magic_quotes_runtime(0);
+        $data = fread($fp, filesize($this->_packageFileName($package)));
+        set_magic_quotes_runtime($rt);
+        $this->_closePackageFile($fp);
+        $data = unserialize($data);
+        if ($key === null) {
+            return $data;
+        }
+        if (isset($data[$key])) {
+            return $data[$key];
+        }
+        return null;
+    }
+
+    // }}}
+    // {{{ _listPackages()
+
+    function _listPackages()
+    {
+        $pkglist = array();
+        $dp = @opendir($this->statedir);
+        if (!$dp) {
+            return $pkglist;
+        }
+        while ($ent = readdir($dp)) {
+            if ($ent{0} == '.' || substr($ent, -4) != '.reg') {
+                continue;
+            }
+            $pkglist[] = substr($ent, 0, -4);
+        }
+        return $pkglist;
+    }
+
+    // }}}
+
+    // {{{ packageExists()
+
+    function packageExists($package)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_packageExists($package);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ packageInfo()
+
+    function packageInfo($package = null, $key = null)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_packageInfo($package, $key);
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ listPackages()
+
+    function listPackages()
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_SH))) {
+            return $e;
+        }
+        $ret = $this->_listPackages();
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ addPackage()
+
+    function addPackage($package, $info)
+    {
+        if ($this->packageExists($package)) {
+            return false;
+        }
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+        $fp = $this->_openPackageFile($package, 'wb');
+        if ($fp === null) {
+            $this->_unlock();
+            return false;
+        }
+        $info['_lastmodified'] = time();
+        fwrite($fp, serialize($info));
+        $this->_closePackageFile($fp);
+        $this->_unlock();
+        return true;
+    }
+
+    // }}}
+    // {{{ deletePackage()
+
+    function deletePackage($package)
+    {
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+        $file = $this->_packageFileName($package);
+        $ret = @unlink($file);
+        $this->rebuildFileMap();
+        $this->_unlock();
+        return $ret;
+    }
+
+    // }}}
+    // {{{ updatePackage()
+
+    function updatePackage($package, $info, $merge = true)
+    {
+        $oldinfo = $this->packageInfo($package);
+        if (empty($oldinfo)) {
+            return false;
+        }
+        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
+            return $e;
+        }
+        $fp = $this->_openPackageFile($package, 'w');
+        if ($fp === null) {
+            $this->_unlock();
+            return false;
+        }
+        $info['_lastmodified'] = time();
+        if ($merge) {
+            fwrite($fp, serialize(array_merge($oldinfo, $info)));
+        } else {
+            fwrite($fp, serialize($info));
+        }
+        $this->_closePackageFile($fp);
+        if (isset($info['filelist'])) {
+            $this->rebuildFileMap();
+        }
+        $this->_unlock();
+        return true;
+    }
+
+    // }}}
+    // {{{ checkFileMap()
+
+    /**
+     * Test whether a file belongs to a package.
+     *
+     * @param string $path file path, absolute or relative to the pear
+     * install dir
+     *
+     * @return string which package the file belongs to, or an empty
+     * string if the file does not belong to an installed package
+     *
+     * @access public
+     */
+    function checkFileMap($path)
+    {
+        if (is_array($path)) {
+            static $notempty;
+            if (empty($notempty)) {
+                $notempty = create_function('$a','return !empty($a);');
+            }
+            $pkgs = array();
+            foreach ($path as $name => $attrs) {
+                if (is_array($attrs) && isset($attrs['baseinstalldir'])) {
+                    $name = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name;
+                }
+                $pkgs[$name] = $this->checkFileMap($name);
+            }
+            return array_filter($pkgs, $notempty);
+        }
+        if (empty($this->filemap_cache) && PEAR::isError($this->readFileMap())) {
+            return $err;
+        }
+        if (isset($this->filemap_cache[$path])) {
+            return $this->filemap_cache[$path];
+        }
+        $l = strlen($this->install_dir);
+        if (substr($path, 0, $l) == $this->install_dir) {
+            $path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l));
+        }
+        if (isset($this->filemap_cache[$path])) {
+            return $this->filemap_cache[$path];
+        }
+        return '';
+    }
+
+    // }}}
+
+}
+
+?>
diff --git a/3rdparty/PEAR/Remote.php b/3rdparty/PEAR/Remote.php
new file mode 100644
index 0000000000000000000000000000000000000000..7b1e314f903af685e4481fdd8289ede22ccdb8f9
--- /dev/null
+++ b/3rdparty/PEAR/Remote.php
@@ -0,0 +1,394 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@php.net>                                    |
+// +----------------------------------------------------------------------+
+//
+// $Id: Remote.php,v 1.50 2004/06/08 18:03:46 cellog Exp $
+
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+/**
+ * This is a class for doing remote operations against the central
+ * PEAR database.
+ *
+ * @nodep XML_RPC_Value
+ * @nodep XML_RPC_Message
+ * @nodep XML_RPC_Client
+ */
+class PEAR_Remote extends PEAR
+{
+    // {{{ properties
+
+    var $config = null;
+    var $cache  = null;
+
+    // }}}
+
+    // {{{ PEAR_Remote(config_object)
+
+    function PEAR_Remote(&$config)
+    {
+        $this->PEAR();
+        $this->config = &$config;
+    }
+
+    // }}}
+
+    // {{{ getCache()
+
+
+    function getCache($args)
+    {
+        $id       = md5(serialize($args));
+        $cachedir = $this->config->get('cache_dir');
+        $filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id;
+        if (!file_exists($filename)) {
+            return null;
+        };
+
+        $fp = fopen($filename, 'rb');
+        if (!$fp) {
+            return null;
+        }
+        $content  = fread($fp, filesize($filename));
+        fclose($fp);
+        $result   = array(
+            'age'        => time() - filemtime($filename),
+            'lastChange' => filemtime($filename),
+            'content'    => unserialize($content),
+            );
+        return $result;
+    }
+
+    // }}}
+
+    // {{{ saveCache()
+
+    function saveCache($args, $data)
+    {
+        $id       = md5(serialize($args));
+        $cachedir = $this->config->get('cache_dir');
+        if (!file_exists($cachedir)) {
+            System::mkdir(array('-p', $cachedir));
+        }
+        $filename = $cachedir.'/xmlrpc_cache_'.$id;
+
+        $fp = @fopen($filename, "wb");
+        if ($fp) {
+            fwrite($fp, serialize($data));
+            fclose($fp);
+        };
+    }
+
+    // }}}
+
+    // {{{ call(method, [args...])
+
+    function call($method)
+    {
+        $_args = $args = func_get_args();
+
+        $this->cache = $this->getCache($args);
+        $cachettl = $this->config->get('cache_ttl');
+        // If cache is newer than $cachettl seconds, we use the cache!
+        if ($this->cache !== null && $this->cache['age'] < $cachettl) {
+            return $this->cache['content'];
+        };
+
+        if (extension_loaded("xmlrpc")) {
+            $result = call_user_func_array(array(&$this, 'call_epi'), $args);
+            if (!PEAR::isError($result)) {
+                $this->saveCache($_args, $result);
+            };
+            return $result;
+        }
+        if (!@include_once("XML/RPC.php")) {
+            return $this->raiseError("For this remote PEAR operation you need to install the XML_RPC package");
+        }
+        array_shift($args);
+        $server_host = $this->config->get('master_server');
+        $username = $this->config->get('username');
+        $password = $this->config->get('password');
+        $eargs = array();
+        foreach($args as $arg) $eargs[] = $this->_encode($arg);
+        $f = new XML_RPC_Message($method, $eargs);
+        if ($this->cache !== null) {
+            $maxAge = '?maxAge='.$this->cache['lastChange'];
+        } else {
+            $maxAge = '';
+        };
+        $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+        if ($proxy = parse_url($this->config->get('http_proxy'))) {
+            $proxy_host = @$proxy['host'];
+            $proxy_port = @$proxy['port'];
+            $proxy_user = @urldecode(@$proxy['user']);
+            $proxy_pass = @urldecode(@$proxy['pass']);
+        }
+        $c = new XML_RPC_Client('/xmlrpc.php'.$maxAge, $server_host, 80, $proxy_host, $proxy_port, $proxy_user, $proxy_pass);
+        if ($username && $password) {
+            $c->setCredentials($username, $password);
+        }
+        if ($this->config->get('verbose') >= 3) {
+            $c->setDebug(1);
+        }
+        $r = $c->send($f);
+        if (!$r) {
+            return $this->raiseError("XML_RPC send failed");
+        }
+        $v = $r->value();
+        if ($e = $r->faultCode()) {
+            if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) {
+                return $this->cache['content'];
+            }
+            return $this->raiseError($r->faultString(), $e);
+        }
+
+        $result = XML_RPC_decode($v);
+        $this->saveCache($_args, $result);
+        return $result;
+    }
+
+    // }}}
+
+    // {{{ call_epi(method, [args...])
+
+    function call_epi($method)
+    {
+        do {
+            if (extension_loaded("xmlrpc")) {
+                break;
+            }
+            if (OS_WINDOWS) {
+                $ext = 'dll';
+            } elseif (PHP_OS == 'HP-UX') {
+                $ext = 'sl';
+            } elseif (PHP_OS == 'AIX') {
+                $ext = 'a';
+            } else {
+                $ext = 'so';
+            }
+            $ext = OS_WINDOWS ? 'dll' : 'so';
+            @dl("xmlrpc-epi.$ext");
+            if (extension_loaded("xmlrpc")) {
+                break;
+            }
+            @dl("xmlrpc.$ext");
+            if (extension_loaded("xmlrpc")) {
+                break;
+            }
+            return $this->raiseError("unable to load xmlrpc extension");
+        } while (false);
+        $params = func_get_args();
+        array_shift($params);
+        $method = str_replace("_", ".", $method);
+        $request = xmlrpc_encode_request($method, $params);
+        $server_host = $this->config->get("master_server");
+        if (empty($server_host)) {
+            return $this->raiseError("PEAR_Remote::call: no master_server configured");
+        }
+        $server_port = 80;
+        if ($http_proxy = $this->config->get('http_proxy')) {
+            $proxy = parse_url($http_proxy);
+            $proxy_host = $proxy_port = $proxy_user = $proxy_pass = '';
+            $proxy_host = @$proxy['host'];
+            $proxy_port = @$proxy['port'];
+            $proxy_user = @urldecode(@$proxy['user']);
+            $proxy_pass = @urldecode(@$proxy['pass']);
+            $fp = @fsockopen($proxy_host, $proxy_port);
+            $use_proxy = true;
+        } else {
+            $use_proxy = false;
+            $fp = @fsockopen($server_host, $server_port);
+        }
+        if (!$fp && $http_proxy) {
+            return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed");
+        } elseif (!$fp) {
+            return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed");
+        }
+        $len = strlen($request);
+        $req_headers = "Host: $server_host:$server_port\r\n" .
+             "Content-type: text/xml\r\n" .
+             "Content-length: $len\r\n";
+        $username = $this->config->get('username');
+        $password = $this->config->get('password');
+        if ($username && $password) {
+            $req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n";
+            $tmp = base64_encode("$username:$password");
+            $req_headers .= "Authorization: Basic $tmp\r\n";
+        }
+        if ($this->cache !== null) {
+            $maxAge = '?maxAge='.$this->cache['lastChange'];
+        } else {
+            $maxAge = '';
+        };
+
+        if ($use_proxy && $proxy_host != '' && $proxy_user != '') {
+            $req_headers .= 'Proxy-Authorization: Basic '
+                .base64_encode($proxy_user.':'.$proxy_pass)
+                ."\r\n";
+        }
+
+        if ($this->config->get('verbose') > 3) {
+            print "XMLRPC REQUEST HEADERS:\n";
+            var_dump($req_headers);
+            print "XMLRPC REQUEST BODY:\n";
+            var_dump($request);
+        }
+
+        if ($use_proxy && $proxy_host != '') {
+            $post_string = "POST http://".$server_host;
+            if ($proxy_port > '') {
+                $post_string .= ':'.$server_port;
+            }
+        } else {
+            $post_string = "POST ";
+        }
+
+        fwrite($fp, ($post_string."/xmlrpc.php$maxAge HTTP/1.0\r\n$req_headers\r\n$request"));
+        $response = '';
+        $line1 = fgets($fp, 2048);
+        if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) {
+            return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server");
+        }
+        switch ($matches[1]) {
+            case "200": // OK
+                break;
+            case "304": // Not Modified
+                return $this->cache['content'];
+            case "401": // Unauthorized
+                if ($username && $password) {
+                    return $this->raiseError("PEAR_Remote: authorization failed", 401);
+                } else {
+                    return $this->raiseError("PEAR_Remote: authorization required, please log in first", 401);
+                }
+            default:
+                return $this->raiseError("PEAR_Remote: unexpected HTTP response", (int)$matches[1], null, null, "$matches[1] $matches[2]");
+        }
+        while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
+        while ($chunk = fread($fp, 10240)) {
+            $response .= $chunk;
+        }
+        fclose($fp);
+        if ($this->config->get('verbose') > 3) {
+            print "XMLRPC RESPONSE:\n";
+            var_dump($response);
+        }
+        $ret = xmlrpc_decode($response);
+        if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) {
+            if ($ret['__PEAR_TYPE__'] == 'error') {
+                if (isset($ret['__PEAR_CLASS__'])) {
+                    $class = $ret['__PEAR_CLASS__'];
+                } else {
+                    $class = "PEAR_Error";
+                }
+                if ($ret['code']     === '') $ret['code']     = null;
+                if ($ret['message']  === '') $ret['message']  = null;
+                if ($ret['userinfo'] === '') $ret['userinfo'] = null;
+                if (strtolower($class) == 'db_error') {
+                    $ret = $this->raiseError(PEAR::errorMessage($ret['code']),
+                                             $ret['code'], null, null,
+                                             $ret['userinfo']);
+                } else {
+                    $ret = $this->raiseError($ret['message'], $ret['code'],
+                                             null, null, $ret['userinfo']);
+                }
+            }
+        } elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0])
+                  && is_array($ret[0]) &&
+                  !empty($ret[0]['faultString']) &&
+                  !empty($ret[0]['faultCode'])) {
+            extract($ret[0]);
+            $faultString = "XML-RPC Server Fault: " .
+                 str_replace("\n", " ", $faultString);
+            return $this->raiseError($faultString, $faultCode);
+        }
+        return $ret;
+    }
+
+    // }}}
+
+    // {{{ _encode
+
+    // a slightly extended version of XML_RPC_encode
+    function _encode($php_val)
+    {
+        global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double;
+        global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct;
+
+        $type = gettype($php_val);
+        $xmlrpcval = new XML_RPC_Value;
+
+        switch($type) {
+            case "array":
+                reset($php_val);
+                $firstkey = key($php_val);
+                end($php_val);
+                $lastkey = key($php_val);
+                if ($firstkey === 0 && is_int($lastkey) &&
+                    ($lastkey + 1) == count($php_val)) {
+                    $is_continuous = true;
+                    reset($php_val);
+                    $size = count($php_val);
+                    for ($expect = 0; $expect < $size; $expect++, next($php_val)) {
+                        if (key($php_val) !== $expect) {
+                            $is_continuous = false;
+                            break;
+                        }
+                    }
+                    if ($is_continuous) {
+                        reset($php_val);
+                        $arr = array();
+                        while (list($k, $v) = each($php_val)) {
+                            $arr[$k] = $this->_encode($v);
+                        }
+                        $xmlrpcval->addArray($arr);
+                        break;
+                    }
+                }
+                // fall though if not numerical and continuous
+            case "object":
+                $arr = array();
+                while (list($k, $v) = each($php_val)) {
+                    $arr[$k] = $this->_encode($v);
+                }
+                $xmlrpcval->addStruct($arr);
+                break;
+            case "integer":
+                $xmlrpcval->addScalar($php_val, $XML_RPC_Int);
+                break;
+            case "double":
+                $xmlrpcval->addScalar($php_val, $XML_RPC_Double);
+                break;
+            case "string":
+            case "NULL":
+                $xmlrpcval->addScalar($php_val, $XML_RPC_String);
+                break;
+            case "boolean":
+                $xmlrpcval->addScalar($php_val, $XML_RPC_Boolean);
+                break;
+            case "unknown type":
+            default:
+                return null;
+        }
+        return $xmlrpcval;
+    }
+
+    // }}}
+
+}
+
+?>
diff --git a/3rdparty/PEAR/RunTest.php b/3rdparty/PEAR/RunTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1aa02aab9dfd44de3f891c4fa64a34bb2c1419dd
--- /dev/null
+++ b/3rdparty/PEAR/RunTest.php
@@ -0,0 +1,363 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com>                             |
+// |          Greg Beaver <cellog@php.net>                                |
+// |                                                                      |
+// +----------------------------------------------------------------------+
+//
+// $Id: RunTest.php,v 1.3.2.4 2005/02/17 17:47:55 cellog Exp $
+//
+
+/**
+ * Simplified version of PHP's test suite
+ * -- EXPERIMENTAL --
+
+ Try it with:
+
+ $ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);'
+
+
+TODO:
+
+Actually finish the development and testing
+
+ */
+
+require_once 'PEAR.php';
+require_once 'PEAR/Config.php';
+
+define('DETAILED', 1);
+putenv("PHP_PEAR_RUNTESTS=1");
+
+class PEAR_RunTest
+{
+    var $_logger;
+
+    /**
+     * An object that supports the PEAR_Common->log() signature, or null
+     * @param PEAR_Common|null
+     */
+    function PEAR_RunTest($logger = null)
+    {
+        $this->_logger = $logger;
+    }
+
+    //
+    //  Run an individual test case.
+    //
+
+    function run($file, $ini_settings = '')
+    {
+        $cwd = getcwd();
+        $conf = &PEAR_Config::singleton();
+        $php = $conf->get('php_bin');
+        //var_dump($php);exit;
+        global $log_format, $info_params, $ini_overwrites;
+
+        $info_params = '';
+        $log_format = 'LEOD';
+
+        // Load the sections of the test file.
+        $section_text = array(
+            'TEST'    => '(unnamed test)',
+            'SKIPIF'  => '',
+            'GET'     => '',
+            'ARGS'    => '',
+        );
+
+        if (!is_file($file) || !$fp = fopen($file, "r")) {
+            return PEAR::raiseError("Cannot open test file: $file");
+        }
+
+        $section = '';
+        while (!feof($fp)) {
+            $line = fgets($fp);
+
+            // Match the beginning of a section.
+            if (ereg('^--([A-Z]+)--',$line,$r)) {
+                $section = $r[1];
+                $section_text[$section] = '';
+                continue;
+            }
+
+            // Add to the section text.
+            $section_text[$section] .= $line;
+        }
+        fclose($fp);
+
+        $shortname = str_replace($cwd.'/', '', $file);
+        $tested = trim($section_text['TEST'])." [$shortname]";
+
+        $tmp = realpath(dirname($file));
+        $tmp_skipif = $tmp . uniqid('/phpt.');
+        $tmp_file   = ereg_replace('\.phpt$','.php',$file);
+        $tmp_post   = $tmp . uniqid('/phpt.');
+
+        @unlink($tmp_skipif);
+        @unlink($tmp_file);
+        @unlink($tmp_post);
+
+        // unlink old test results
+        @unlink(ereg_replace('\.phpt$','.diff',$file));
+        @unlink(ereg_replace('\.phpt$','.log',$file));
+        @unlink(ereg_replace('\.phpt$','.exp',$file));
+        @unlink(ereg_replace('\.phpt$','.out',$file));
+
+        // Check if test should be skipped.
+        $info = '';
+        $warn = false;
+        if (array_key_exists('SKIPIF', $section_text)) {
+            if (trim($section_text['SKIPIF'])) {
+                $this->save_text($tmp_skipif, $section_text['SKIPIF']);
+                //$extra = substr(PHP_OS, 0, 3) !== "WIN" ?
+                //    "unset REQUEST_METHOD;": "";
+
+                //$output = `$extra $php $info_params -f $tmp_skipif`;
+                $output = `$php $info_params -f $tmp_skipif`;
+                unlink($tmp_skipif);
+                if (eregi("^skip", trim($output))) {
+                    $skipreason = "SKIP $tested";
+                    $reason = (eregi("^skip[[:space:]]*(.+)\$", trim($output))) ? eregi_replace("^skip[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
+                    if ($reason) {
+                        $skipreason .= " (reason: $reason)";
+                    }
+                    $this->_logger->log(0, $skipreason);
+                    if (isset($old_php)) {
+                        $php = $old_php;
+                    }
+                    return 'SKIPPED';
+                }
+                if (eregi("^info", trim($output))) {
+                    $reason = (ereg("^info[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^info[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
+                    if ($reason) {
+                        $info = " (info: $reason)";
+                    }
+                }
+                if (eregi("^warn", trim($output))) {
+                    $reason = (ereg("^warn[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^warn[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE;
+                    if ($reason) {
+                        $warn = true; /* only if there is a reason */
+                        $info = " (warn: $reason)";
+                    }
+                }
+            }
+        }
+
+        // We've satisfied the preconditions - run the test!
+        $this->save_text($tmp_file,$section_text['FILE']);
+
+        $args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : '';
+
+        $cmd = "$php$ini_settings -f $tmp_file$args 2>&1";
+        if (isset($this->_logger)) {
+            $this->_logger->log(2, 'Running command "' . $cmd . '"');
+        }
+
+        $savedir = getcwd(); // in case the test moves us around
+        if (isset($section_text['RETURNS'])) {
+            ob_start();
+            system($cmd, $return_value);
+            $out = ob_get_contents();
+            ob_end_clean();
+            @unlink($tmp_post);
+            $section_text['RETURNS'] = (int) trim($section_text['RETURNS']);
+            $returnfail = ($return_value != $section_text['RETURNS']);
+        } else {
+            $out = `$cmd`;
+            $returnfail = false;
+        }
+        chdir($savedir);
+        // Does the output match what is expected?
+        $output = trim($out);
+        $output = preg_replace('/\r\n/', "\n", $output);
+
+        if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) {
+            if (isset($section_text['EXPECTF'])) {
+                $wanted = trim($section_text['EXPECTF']);
+            } else {
+                $wanted = trim($section_text['EXPECTREGEX']);
+            }
+            $wanted_re = preg_replace('/\r\n/',"\n",$wanted);
+            if (isset($section_text['EXPECTF'])) {
+                $wanted_re = preg_quote($wanted_re, '/');
+                // Stick to basics
+                $wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
+                $wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re);
+                $wanted_re = str_replace("%d", "[0-9]+", $wanted_re);
+                $wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re);
+                $wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re);
+                $wanted_re = str_replace("%c", ".", $wanted_re);
+                // %f allows two points "-.0.0" but that is the best *simple* expression
+            }
+    /* DEBUG YOUR REGEX HERE
+            var_dump($wanted_re);
+            print(str_repeat('=', 80) . "\n");
+            var_dump($output);
+    */
+            if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) {
+                @unlink($tmp_file);
+                $this->_logger->log(0, "PASS $tested$info");
+                if (isset($old_php)) {
+                    $php = $old_php;
+                }
+                return 'PASSED';
+            }
+
+        } else {
+            $wanted = trim($section_text['EXPECT']);
+            $wanted = preg_replace('/\r\n/',"\n",$wanted);
+        // compare and leave on success
+            $ok = (0 == strcmp($output,$wanted));
+            if (!$returnfail && $ok) {
+                @unlink($tmp_file);
+                $this->_logger->log(0, "PASS $tested$info");
+                if (isset($old_php)) {
+                    $php = $old_php;
+                }
+                return 'PASSED';
+            }
+        }
+
+        // Test failed so we need to report details.
+        if ($warn) {
+            $this->_logger->log(0, "WARN $tested$info");
+        } else {
+            $this->_logger->log(0, "FAIL $tested$info");
+        }
+
+        if (isset($section_text['RETURNS'])) {
+            $GLOBALS['__PHP_FAILED_TESTS__'][] = array(
+                            'name' => $file,
+                            'test_name' => $tested,
+                            'output' => ereg_replace('\.phpt$','.log', $file),
+                            'diff'   => ereg_replace('\.phpt$','.diff', $file),
+                            'info'   => $info,
+                            'return' => $return_value
+                            );
+        } else {
+            $GLOBALS['__PHP_FAILED_TESTS__'][] = array(
+                            'name' => $file,
+                            'test_name' => $tested,
+                            'output' => ereg_replace('\.phpt$','.log', $file),
+                            'diff'   => ereg_replace('\.phpt$','.diff', $file),
+                            'info'   => $info,
+                            );
+        }
+
+        // write .exp
+        if (strpos($log_format,'E') !== FALSE) {
+            $logname = ereg_replace('\.phpt$','.exp',$file);
+            if (!$log = fopen($logname,'w')) {
+                return PEAR::raiseError("Cannot create test log - $logname");
+            }
+            fwrite($log,$wanted);
+            fclose($log);
+        }
+
+        // write .out
+        if (strpos($log_format,'O') !== FALSE) {
+            $logname = ereg_replace('\.phpt$','.out',$file);
+            if (!$log = fopen($logname,'w')) {
+                return PEAR::raiseError("Cannot create test log - $logname");
+            }
+            fwrite($log,$output);
+            fclose($log);
+        }
+
+        // write .diff
+        if (strpos($log_format,'D') !== FALSE) {
+            $logname = ereg_replace('\.phpt$','.diff',$file);
+            if (!$log = fopen($logname,'w')) {
+                return PEAR::raiseError("Cannot create test log - $logname");
+            }
+            fwrite($log, $this->generate_diff($wanted, $output,
+                isset($section_text['RETURNS']) ? array(trim($section_text['RETURNS']),
+                    $return_value) : null));
+            fclose($log);
+        }
+
+        // write .log
+        if (strpos($log_format,'L') !== FALSE) {
+            $logname = ereg_replace('\.phpt$','.log',$file);
+            if (!$log = fopen($logname,'w')) {
+                return PEAR::raiseError("Cannot create test log - $logname");
+            }
+            fwrite($log,"
+---- EXPECTED OUTPUT
+$wanted
+---- ACTUAL OUTPUT
+$output
+---- FAILED
+");
+            if ($returnfail) {
+                fwrite($log,"
+---- EXPECTED RETURN
+$section_text[RETURNS]
+---- ACTUAL RETURN
+$return_value
+");
+            }
+            fclose($log);
+            //error_report($file,$logname,$tested);
+        }
+
+        if (isset($old_php)) {
+            $php = $old_php;
+        }
+
+        return $warn ? 'WARNED' : 'FAILED';
+    }
+
+    function generate_diff($wanted, $output, $return_value)
+    {
+        $w = explode("\n", $wanted);
+        $o = explode("\n", $output);
+        $w1 = array_diff_assoc($w,$o);
+        $o1 = array_diff_assoc($o,$w);
+        $w2 = array();
+        $o2 = array();
+        foreach($w1 as $idx => $val) $w2[sprintf("%03d<",$idx)] = sprintf("%03d- ", $idx+1).$val;
+        foreach($o1 as $idx => $val) $o2[sprintf("%03d>",$idx)] = sprintf("%03d+ ", $idx+1).$val;
+        $diff = array_merge($w2, $o2);
+        ksort($diff);
+        if ($return_value) {
+            $extra = "##EXPECTED: $return_value[0]\r\n##RETURNED: $return_value[1]";
+        } else {
+            $extra = '';
+        }
+        return implode("\r\n", $diff) . $extra;
+    }
+
+    //
+    //  Write the given text to a temporary file, and return the filename.
+    //
+
+    function save_text($filename, $text)
+    {
+        if (!$fp = fopen($filename, 'w')) {
+            return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)");
+        }
+        fwrite($fp,$text);
+        fclose($fp);
+    if (1 < DETAILED) echo "
+FILE $filename {{{
+$text
+}}}
+";
+    }
+
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/Sabre.includes.php b/3rdparty/Sabre.includes.php
new file mode 100644
index 0000000000000000000000000000000000000000..9d389288c78484005db03fb20e3501b614908a1d
--- /dev/null
+++ b/3rdparty/Sabre.includes.php
@@ -0,0 +1,127 @@
+<?php
+
+/**
+ * Library include file
+ *
+ * This file contains all includes to the rest of the SabreDAV library
+ * Make sure the lib/ directory is in PHP's include_path
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/* Utilities */
+include 'Sabre/HTTP/Util.php';
+include 'Sabre/HTTP/Response.php';
+include 'Sabre/HTTP/Request.php';
+include 'Sabre/HTTP/AbstractAuth.php';
+include 'Sabre/HTTP/BasicAuth.php';
+include 'Sabre/HTTP/DigestAuth.php';
+include 'Sabre/HTTP/AWSAuth.php';
+
+/* Version */
+include 'Sabre/DAV/Version.php';
+include 'Sabre/HTTP/Version.php';
+
+/* Exceptions */
+include 'Sabre/DAV/Exception.php';
+include 'Sabre/DAV/Exception/BadRequest.php';
+include 'Sabre/DAV/Exception/Conflict.php';
+include 'Sabre/DAV/Exception/FileNotFound.php';
+include 'Sabre/DAV/Exception/InsufficientStorage.php';
+include 'Sabre/DAV/Exception/Locked.php';
+include 'Sabre/DAV/Exception/LockTokenMatchesRequestUri.php';
+include 'Sabre/DAV/Exception/MethodNotAllowed.php';
+include 'Sabre/DAV/Exception/NotImplemented.php';
+include 'Sabre/DAV/Exception/Forbidden.php';
+include 'Sabre/DAV/Exception/PreconditionFailed.php';
+include 'Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php';
+include 'Sabre/DAV/Exception/UnsupportedMediaType.php';
+include 'Sabre/DAV/Exception/NotAuthenticated.php';
+
+include 'Sabre/DAV/Exception/ConflictingLock.php';
+include 'Sabre/DAV/Exception/ReportNotImplemented.php';
+include 'Sabre/DAV/Exception/InvalidResourceType.php';
+
+/* Properties */
+include 'Sabre/DAV/Property.php';
+include 'Sabre/DAV/Property/GetLastModified.php';
+include 'Sabre/DAV/Property/ResourceType.php';
+include 'Sabre/DAV/Property/SupportedLock.php';
+include 'Sabre/DAV/Property/LockDiscovery.php';
+include 'Sabre/DAV/Property/IHref.php';
+include 'Sabre/DAV/Property/Href.php';
+include 'Sabre/DAV/Property/HrefList.php';
+include 'Sabre/DAV/Property/SupportedReportSet.php';
+include 'Sabre/DAV/Property/Response.php';
+include 'Sabre/DAV/Property/ResponseList.php';
+
+/* Node interfaces */
+include 'Sabre/DAV/INode.php';
+include 'Sabre/DAV/IFile.php';
+include 'Sabre/DAV/ICollection.php';
+include 'Sabre/DAV/IProperties.php';
+include 'Sabre/DAV/ILockable.php';
+include 'Sabre/DAV/IQuota.php';
+include 'Sabre/DAV/IExtendedCollection.php';
+
+/* Node abstract implementations */
+include 'Sabre/DAV/Node.php';
+include 'Sabre/DAV/File.php';
+include 'Sabre/DAV/Directory.php';
+
+/* Utilities */
+include 'Sabre/DAV/SimpleCollection.php';
+include 'Sabre/DAV/SimpleDirectory.php';
+include 'Sabre/DAV/XMLUtil.php';
+include 'Sabre/DAV/URLUtil.php';
+
+/* Filesystem implementation */
+include 'Sabre/DAV/FS/Node.php';
+include 'Sabre/DAV/FS/File.php';
+include 'Sabre/DAV/FS/Directory.php';
+
+/* Advanced filesystem implementation */
+include 'Sabre/DAV/FSExt/Node.php';
+include 'Sabre/DAV/FSExt/File.php';
+include 'Sabre/DAV/FSExt/Directory.php';
+
+/* Trees */
+include 'Sabre/DAV/Tree.php';
+include 'Sabre/DAV/ObjectTree.php';
+include 'Sabre/DAV/Tree/Filesystem.php';
+
+/* Server */
+include 'Sabre/DAV/Server.php';
+include 'Sabre/DAV/ServerPlugin.php';
+
+/* Browser */
+include 'Sabre/DAV/Browser/Plugin.php';
+include 'Sabre/DAV/Browser/MapGetToPropFind.php';
+include 'Sabre/DAV/Browser/GuessContentType.php';
+
+/* Locks */
+include 'Sabre/DAV/Locks/LockInfo.php';
+include 'Sabre/DAV/Locks/Plugin.php';
+include 'Sabre/DAV/Locks/Backend/Abstract.php';
+include 'Sabre/DAV/Locks/Backend/FS.php';
+include 'Sabre/DAV/Locks/Backend/PDO.php';
+
+/* Temporary File Filter plugin */
+include 'Sabre/DAV/TemporaryFileFilterPlugin.php';
+
+/* Authentication plugin */
+include 'Sabre/DAV/Auth/Plugin.php';
+include 'Sabre/DAV/Auth/IBackend.php';
+include 'Sabre/DAV/Auth/Backend/AbstractDigest.php';
+include 'Sabre/DAV/Auth/Backend/AbstractBasic.php';
+include 'Sabre/DAV/Auth/Backend/File.php';
+include 'Sabre/DAV/Auth/Backend/PDO.php';
+
+/* DavMount plugin */
+include 'Sabre/DAV/Mount/Plugin.php';
+
+
diff --git a/3rdparty/Sabre/CalDAV/Backend/Abstract.php b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ac282f44a0f70b08d00c5128ba5a73e2a8b7ee1
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Backend/Abstract.php
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * Abstract Calendaring backend. Extend this class to create your own backends.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_CalDAV_Backend_Abstract {
+
+    /**
+     * Returns a list of calendars for a principal.
+     *
+     * Every project is an array with the following keys:
+     *  * id, a unique id that will be used by other functions to modify the
+     *    calendar. This can be the same as the uri or a database key.
+     *  * uri, which the basename of the uri with which the calendar is 
+     *    accessed.
+     *  * principalUri. The owner of the calendar. Almost always the same as
+     *    principalUri passed to this method.
+     *
+     * Furthermore it can contain webdav properties in clark notation. A very
+     * common one is '{DAV:}displayname'. 
+     *
+     * @param string $principalUri 
+     * @return array 
+     */
+    abstract function getCalendarsForUser($principalUri);
+
+    /**
+     * Creates a new calendar for a principal.
+     *
+     * If the creation was a success, an id must be returned that can be used to reference
+     * this calendar in other methods, such as updateCalendar.
+     *
+     * This function must return a server-wide unique id that can be used 
+     * later to reference the calendar.
+     *
+     * @param string $principalUri
+     * @param string $calendarUri
+     * @param array $properties
+     * @return string|int 
+     */
+    abstract function createCalendar($principalUri,$calendarUri,array $properties); 
+
+    /**
+     * Updates properties on this node,
+     *
+     * The properties array uses the propertyName in clark-notation as key,
+     * and the array value for the property value. In the case a property
+     * should be deleted, the property value will be null.
+     *
+     * This method must be atomic. If one property cannot be changed, the
+     * entire operation must fail.
+     *
+     * If the operation was successful, true can be returned.
+     * If the operation failed, false can be returned.
+     *
+     * Deletion of a non-existant property is always succesful.
+     *
+     * Lastly, it is optional to return detailed information about any
+     * failures. In this case an array should be returned with the following
+     * structure:
+     *
+     * array(
+     *   403 => array(
+     *      '{DAV:}displayname' => null,
+     *   ),
+     *   424 => array(
+     *      '{DAV:}owner' => null,
+     *   )
+     * )
+     *
+     * In this example it was forbidden to update {DAV:}displayname. 
+     * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+     * (424 Failed Dependency) because the request needs to be atomic.
+     *
+     * @param string $calendarId
+     * @param array $properties
+     * @return bool|array 
+     */
+    public function updateCalendar($calendarId, array $properties) {
+        
+        return false; 
+
+    }
+
+    /**
+     * Delete a calendar and all it's objects 
+     * 
+     * @param string $calendarId 
+     * @return void
+     */
+    abstract function deleteCalendar($calendarId);
+
+    /**
+     * Returns all calendar objects within a calendar object.
+     *
+     * Every item contains an array with the following keys:
+     *   * id - unique identifier which will be used for subsequent updates
+     *   * calendardata - The iCalendar-compatible calnedar data
+     *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+     *   * lastmodified - a timestamp of the last modification time
+     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.: 
+     *   '  "abcdef"')
+     *   * calendarid - The calendarid as it was passed to this function.
+     *
+     * Note that the etag is optional, but it's highly encouraged to return for 
+     * speed reasons.
+     *
+     * The calendardata is also optional. If it's not returned 
+     * 'getCalendarObject' will be called later, which *is* expected to return 
+     * calendardata.
+     * 
+     * @param string $calendarId 
+     * @return array 
+     */
+    abstract function getCalendarObjects($calendarId);
+
+    /**
+     * Returns information from a single calendar object, based on it's object
+     * uri.
+     *
+     * The returned array must have the same keys as getCalendarObjects. The 
+     * 'calendardata' object is required here though, while it's not required 
+     * for getCalendarObjects.
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @return array 
+     */
+    abstract function getCalendarObject($calendarId,$objectUri);
+
+    /**
+     * Creates a new calendar object. 
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @param string $calendarData 
+     * @return void
+     */
+    abstract function createCalendarObject($calendarId,$objectUri,$calendarData);
+
+    /**
+     * Updates an existing calendarobject, based on it's uri. 
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @param string $calendarData 
+     * @return void
+     */
+    abstract function updateCalendarObject($calendarId,$objectUri,$calendarData);
+
+    /**
+     * Deletes an existing calendar object. 
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @return void
+     */
+    abstract function deleteCalendarObject($calendarId,$objectUri);
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Backend/PDO.php b/3rdparty/Sabre/CalDAV/Backend/PDO.php
new file mode 100644
index 0000000000000000000000000000000000000000..d1785bb8f8fc162d9f10c8fb7abad0b2409e8102
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Backend/PDO.php
@@ -0,0 +1,387 @@
+<?php
+
+/**
+ * PDO CalDAV backend
+ *
+ * This backend is used to store calendar-data in a PDO database, such as 
+ * sqlite or MySQL
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Backend_PDO extends Sabre_CalDAV_Backend_Abstract {
+
+    /**
+     * pdo 
+     * 
+     * @var PDO
+     */
+    protected $pdo;
+
+    /**
+     * The table name that will be used for calendars 
+     * 
+     * @var string 
+     */
+    protected $calendarTableName;
+
+    /**
+     * The table name that will be used for calendar objects  
+     * 
+     * @var string 
+     */
+    protected $calendarObjectTableName;
+
+    /**
+     * List of CalDAV properties, and how they map to database fieldnames
+     *
+     * Add your own properties by simply adding on to this array
+     * 
+     * @var array
+     */
+    public $propertyMap = array(
+        '{DAV:}displayname'                          => 'displayname',
+        '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
+        '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
+        '{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
+        '{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
+    );
+
+    /**
+     * Creates the backend 
+     * 
+     * @param PDO $pdo 
+     */
+    public function __construct(PDO $pdo, $calendarTableName = 'calendars', $calendarObjectTableName = 'calendarobjects') {
+
+        $this->pdo = $pdo;
+        $this->calendarTableName = $calendarTableName;
+        $this->calendarObjectTableName = $calendarObjectTableName;
+
+    }
+
+    /**
+     * Returns a list of calendars for a principal.
+     *
+     * Every project is an array with the following keys:
+     *  * id, a unique id that will be used by other functions to modify the
+     *    calendar. This can be the same as the uri or a database key.
+     *  * uri, which the basename of the uri with which the calendar is 
+     *    accessed.
+     *  * principalUri. The owner of the calendar. Almost always the same as
+     *    principalUri passed to this method.
+     *
+     * Furthermore it can contain webdav properties in clark notation. A very
+     * common one is '{DAV:}displayname'. 
+     *
+     * @param string $principalUri 
+     * @return array 
+     */
+    public function getCalendarsForUser($principalUri) {
+
+        $fields = array_values($this->propertyMap);
+        $fields[] = 'id';
+        $fields[] = 'uri';
+        $fields[] = 'ctag';
+        $fields[] = 'components';
+        $fields[] = 'principaluri';
+
+        // Making fields a comma-delimited list 
+        $fields = implode(', ', $fields);
+        $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM `".$this->calendarTableName."` WHERE principaluri = ?"); 
+        $stmt->execute(array($principalUri));
+
+        $calendars = array();
+        while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+
+            $components = explode(',',$row['components']);
+
+            $calendar = array(
+                'id' => $row['id'],
+                'uri' => $row['uri'],
+                'principaluri' => $row['principaluri'],
+                '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
+                '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
+            );
+        
+
+            foreach($this->propertyMap as $xmlName=>$dbName) {
+                $calendar[$xmlName] = $row[$dbName];
+            }
+
+            $calendars[] = $calendar;
+
+        }
+
+        return $calendars;
+
+    }
+
+    /**
+     * Creates a new calendar for a principal.
+     *
+     * If the creation was a success, an id must be returned that can be used to reference
+     * this calendar in other methods, such as updateCalendar
+     *
+     * @param string $principalUri
+     * @param string $calendarUri
+     * @param array $properties
+     * @return mixed
+     */
+    public function createCalendar($principalUri,$calendarUri, array $properties) {
+
+        $fieldNames = array(
+            'principaluri',
+            'uri',
+            'ctag',
+        );
+        $values = array(
+            ':principaluri' => $principalUri,
+            ':uri'          => $calendarUri,
+            ':ctag'         => 1,
+        );
+
+        // Default value
+        $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+        $fieldNames[] = 'components';
+        if (!isset($properties[$sccs])) {
+            $values[':components'] = 'VEVENT,VTODO';
+        } else {
+            if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
+                throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
+            }
+            $values[':components'] = implode(',',$properties[$sccs]->getValue());
+        }
+
+        foreach($this->propertyMap as $xmlName=>$dbName) {
+            if (isset($properties[$xmlName])) {
+
+                $myValue = $properties[$xmlName];
+                $values[':' . $dbName] = $properties[$xmlName];
+                $fieldNames[] = $dbName;
+            }
+        }
+
+        $stmt = $this->pdo->prepare("INSERT INTO `".$this->calendarTableName."` (".implode(', ', $fieldNames).") VALUES (".implode(', ',array_keys($values)).")");
+        $stmt->execute($values);
+
+        return $this->pdo->lastInsertId();
+
+    }
+
+    /**
+     * Updates a calendars properties 
+     *
+     * The properties array uses the propertyName in clark-notation as key,
+     * and the array value for the property value. In the case a property
+     * should be deleted, the property value will be null.
+     *
+     * This method must be atomic. If one property cannot be changed, the
+     * entire operation must fail.
+     *
+     * If the operation was successful, true can be returned.
+     * If the operation failed, false can be returned.
+     *
+     * Deletion of a non-existant property is always succesful.
+     *
+     * Lastly, it is optional to return detailed information about any
+     * failures. In this case an array should be returned with the following
+     * structure:
+     *
+     * array(
+     *   403 => array(
+     *      '{DAV:}displayname' => null,
+     *   ),
+     *   424 => array(
+     *      '{DAV:}owner' => null,
+     *   )
+     * )
+     *
+     * In this example it was forbidden to update {DAV:}displayname. 
+     * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+     * (424 Failed Dependency) because the request needs to be atomic.
+     *
+     * @param string $calendarId
+     * @param array $properties
+     * @return bool|array 
+     */
+    public function updateCalendar($calendarId, array $properties) {
+
+        $newValues = array();
+        $result = array(
+            200 => array(), // Ok
+            403 => array(), // Forbidden
+            424 => array(), // Failed Dependency
+        );
+
+        $hasError = false;
+
+        foreach($properties as $propertyName=>$propertyValue) {
+
+            // We don't know about this property. 
+            if (!isset($this->propertyMap[$propertyName])) {
+                $hasError = true;
+                $result[403][$propertyName] = null;
+                unset($properties[$propertyName]);
+                continue;
+            }
+
+            $fieldName = $this->propertyMap[$propertyName];
+            $newValues[$fieldName] = $propertyValue;
+                
+        }
+
+        // If there were any errors we need to fail the request
+        if ($hasError) {
+            // Properties has the remaining properties
+            foreach($properties as $propertyName=>$propertyValue) {
+                $result[424][$propertyName] = null;
+            }
+
+            // Removing unused statuscodes for cleanliness
+            foreach($result as $status=>$properties) {
+                if (is_array($properties) && count($properties)===0) unset($result[$status]);
+            }
+
+            return $result;
+
+        }
+
+        // Success
+
+        // Now we're generating the sql query.
+        $valuesSql = array();
+        foreach($newValues as $fieldName=>$value) {
+            $valuesSql[] = $fieldName . ' = ?';
+        }
+        $valuesSql[] = 'ctag = ctag + 1';
+
+        $stmt = $this->pdo->prepare("UPDATE `" . $this->calendarTableName . "` SET " . implode(', ',$valuesSql) . " WHERE id = ?");
+        $newValues['id'] = $calendarId; 
+        $stmt->execute(array_values($newValues));
+
+        return true; 
+
+    }
+
+    /**
+     * Delete a calendar and all it's objects 
+     * 
+     * @param string $calendarId 
+     * @return void
+     */
+    public function deleteCalendar($calendarId) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?');
+        $stmt->execute(array($calendarId));
+
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarTableName.'` WHERE id = ?');
+        $stmt->execute(array($calendarId));
+
+    }
+
+    /**
+     * Returns all calendar objects within a calendar object.
+     *
+     * Every item contains an array with the following keys:
+     *   * id - unique identifier which will be used for subsequent updates
+     *   * calendardata - The iCalendar-compatible calnedar data
+     *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+     *   * lastmodified - a timestamp of the last modification time
+     *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.: 
+     *   '  "abcdef"')
+     *   * calendarid - The calendarid as it was passed to this function.
+     *
+     * Note that the etag is optional, but it's highly encouraged to return for 
+     * speed reasons.
+     *
+     * The calendardata is also optional. If it's not returned 
+     * 'getCalendarObject' will be called later, which *is* expected to return 
+     * calendardata.
+     * 
+     * @param string $calendarId 
+     * @return array 
+     */
+    public function getCalendarObjects($calendarId) {
+
+        $stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ?');
+        $stmt->execute(array($calendarId));
+        return $stmt->fetchAll();
+
+    }
+
+    /**
+     * Returns information from a single calendar object, based on it's object
+     * uri.
+     *
+     * The returned array must have the same keys as getCalendarObjects. The 
+     * 'calendardata' object is required here though, while it's not required 
+     * for getCalendarObjects.
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @return array 
+     */
+    public function getCalendarObject($calendarId,$objectUri) {
+
+        $stmt = $this->pdo->prepare('SELECT * FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?');
+        $stmt->execute(array($calendarId, $objectUri));
+        return $stmt->fetch();
+
+    }
+
+    /**
+     * Creates a new calendar object. 
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @param string $calendarData 
+     * @return void
+     */
+    public function createCalendarObject($calendarId,$objectUri,$calendarData) {
+
+        $stmt = $this->pdo->prepare('INSERT INTO `'.$this->calendarObjectTableName.'` (calendarid, uri, calendardata, lastmodified) VALUES (?,?,?,?)');
+        $stmt->execute(array($calendarId,$objectUri,$calendarData,time()));
+        $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt->execute(array($calendarId));
+
+    }
+
+    /**
+     * Updates an existing calendarobject, based on it's uri. 
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @param string $calendarData 
+     * @return void
+     */
+    public function updateCalendarObject($calendarId,$objectUri,$calendarData) {
+
+        $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarObjectTableName.'` SET calendardata = ?, lastmodified = ? WHERE calendarid = ? AND uri = ?');
+        $stmt->execute(array($calendarData,time(),$calendarId,$objectUri));
+        $stmt = $this->pdo->prepare('UPDATE `'.$this->calendarTableName.'` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt->execute(array($calendarId));
+
+    }
+
+    /**
+     * Deletes an existing calendar object. 
+     * 
+     * @param string $calendarId 
+     * @param string $objectUri 
+     * @return void
+     */
+    public function deleteCalendarObject($calendarId,$objectUri) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->calendarObjectTableName.'` WHERE calendarid = ? AND uri = ?');
+        $stmt->execute(array($calendarId,$objectUri));
+        $stmt = $this->pdo->prepare('UPDATE `'. $this->calendarTableName .'` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt->execute(array($calendarId));
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Calendar.php b/3rdparty/Sabre/CalDAV/Calendar.php
new file mode 100644
index 0000000000000000000000000000000000000000..a50aef12b4f6fc4e4515e211efa15f0e46c6bc3e
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Calendar.php
@@ -0,0 +1,316 @@
+<?php
+
+/**
+ * This object represents a CalDAV calendar.
+ *
+ * A calendar can contain multiple TODO and or Events. These are represented
+ * as Sabre_CalDAV_CalendarObject objects.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Calendar implements Sabre_DAV_ICollection, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
+
+    /**
+     * This is an array with calendar information 
+     * 
+     * @var array 
+     */
+    protected $calendarInfo;
+
+    /**
+     * CalDAV backend 
+     * 
+     * @var Sabre_CalDAV_Backend_Abstract 
+     */
+    protected $caldavBackend;
+
+    /**
+     * Principal backend
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend
+     */
+    protected $principalBackend;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CalDAV_Backend_Abstract $caldavBackend 
+     * @param array $calendarInfo 
+     * @return void
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $calendarInfo) {
+
+        $this->caldavBackend = $caldavBackend;
+        $this->principalBackend = $principalBackend;
+        $this->calendarInfo = $calendarInfo;
+
+
+    }
+
+    /**
+     * Returns the name of the calendar 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->calendarInfo['uri'];
+
+    }
+
+    /**
+     * Updates properties such as the display name and description 
+     * 
+     * @param array $mutations 
+     * @return array 
+     */
+    public function updateProperties($mutations) {
+
+        return $this->caldavBackend->updateCalendar($this->calendarInfo['id'],$mutations);
+
+    }
+
+    /**
+     * Returns the list of properties 
+     * 
+     * @param array $properties 
+     * @return array 
+     */
+    public function getProperties($requestedProperties) {
+
+        $response = array();
+
+        foreach($requestedProperties as $prop) switch($prop) {
+
+            case '{urn:ietf:params:xml:ns:caldav}supported-calendar-data' : 
+                $response[$prop] = new Sabre_CalDAV_Property_SupportedCalendarData(); 
+                break;
+            case '{urn:ietf:params:xml:ns:caldav}supported-collation-set' : 
+                $response[$prop] =  new Sabre_CalDAV_Property_SupportedCollationSet(); 
+                break;
+            case '{DAV:}owner' :
+                $response[$prop] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF,$this->calendarInfo['principaluri']);
+                break;
+            default : 
+                if (isset($this->calendarInfo[$prop])) $response[$prop] = $this->calendarInfo[$prop];
+                break;
+
+        }
+        return $response;
+
+    }
+
+    /**
+     * Returns a calendar object
+     *
+     * The contained calendar objects are for example Events or Todo's.
+     * 
+     * @param string $name 
+     * @return Sabre_DAV_ICalendarObject 
+     */
+    public function getChild($name) {
+
+        $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
+        if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Calendar object not found');
+        return new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
+
+    }
+
+    /**
+     * Returns the full list of calendar objects  
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']);
+        $children = array();
+        foreach($objs as $obj) {
+            $children[] = new Sabre_CalDAV_CalendarObject($this->caldavBackend,$this->calendarInfo,$obj);
+        }
+        return $children;
+
+    }
+
+    /**
+     * Checks if a child-node exists. 
+     * 
+     * @param string $name 
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'],$name);
+        if (!$obj) 
+            return false;
+        else 
+            return true;
+
+    }
+
+    /**
+     * Creates a new directory
+     *
+     * We actually block this, as subdirectories are not allowed in calendars.
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in calendar objects is not allowed');
+
+    }
+
+    /**
+     * Creates a new file
+     *
+     * The contents of the new file must be a valid ICalendar string.
+     * 
+     * @param string $name 
+     * @param resource $calendarData 
+     * @return void
+     */
+    public function createFile($name,$calendarData = null) {
+
+        $calendarData = stream_get_contents($calendarData);
+
+        $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set'];
+        if ($supportedComponents) {
+            $supportedComponents = $supportedComponents->getValue();
+        } else {
+            $supportedComponents = null;
+        }
+        Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
+
+        $this->caldavBackend->createCalendarObject($this->calendarInfo['id'],$name,$calendarData);
+
+    }
+
+    /**
+     * Deletes the calendar. 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        $this->caldavBackend->deleteCalendar($this->calendarInfo['id']);
+
+    }
+
+    /**
+     * Renames the calendar. Note that most calendars use the 
+     * {DAV:}displayname to display a name to display a name. 
+     * 
+     * @param string $newName 
+     * @return void
+     */
+    public function setName($newName) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming calendars is not yet supported');
+
+    }
+
+    /**
+     * Returns the last modification date as a unix timestamp.
+     * 
+     * @return void
+     */
+    public function getLastModified() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->calendarInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/CalendarObject.php b/3rdparty/Sabre/CalDAV/CalendarObject.php
new file mode 100644
index 0000000000000000000000000000000000000000..b5c4e49b838ce669eac8cd74a3b82491c05e3f65
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarObject.php
@@ -0,0 +1,257 @@
+<?php
+
+/**
+ * The CalendarObject represents a single VEVENT or VTODO within a Calendar. 
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_CalendarObject extends Sabre_DAV_File implements Sabre_DAVACL_IACL {
+
+    /**
+     * Sabre_CalDAV_Backend_Abstract 
+     * 
+     * @var array 
+     */
+    protected $caldavBackend;
+
+    /**
+     * Array with information about this CalendarObject 
+     * 
+     * @var array 
+     */
+    protected $objectData;
+
+    /**
+     * Array with information about the containing calendar
+     * 
+     * @var array 
+     */
+    protected $calendarInfo;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CalDAV_Backend_Abstract $caldavBackend
+     * @param array $calendarInfo
+     * @param array $objectData 
+     */
+    public function __construct(Sabre_CalDAV_Backend_Abstract $caldavBackend,array $calendarInfo,array $objectData) {
+
+        $this->caldavBackend = $caldavBackend;
+
+        if (!isset($objectData['calendarid'])) {
+            throw new InvalidArgumentException('The objectData argument must contain a \'calendarid\' property');
+        }
+        if (!isset($objectData['uri'])) {
+            throw new InvalidArgumentException('The objectData argument must contain an \'uri\' property');
+        }
+
+        $this->calendarInfo = $calendarInfo;
+        $this->objectData = $objectData;
+
+    }
+
+    /**
+     * Returns the uri for this object 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->objectData['uri'];
+
+    }
+
+    /**
+     * Returns the ICalendar-formatted object 
+     * 
+     * @return string 
+     */
+    public function get() {
+
+        // Pre-populating the 'calendardata' is optional, if we don't have it
+        // already we fetch it from the backend.
+        if (!isset($this->objectData['calendardata'])) {
+            $this->objectData = $this->caldavBackend->getCalendarObject($this->objectData['calendarid'], $this->objectData['uri']);
+        }
+        return $this->objectData['calendardata'];
+
+    }
+
+    /**
+     * Updates the ICalendar-formatted object 
+     * 
+     * @param string $calendarData 
+     * @return void 
+     */
+    public function put($calendarData) {
+
+        if (is_resource($calendarData))
+            $calendarData = stream_get_contents($calendarData);
+
+        $supportedComponents = $this->calendarInfo['{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set'];
+        if ($supportedComponents) {
+            $supportedComponents = $supportedComponents->getValue();
+        } else {
+            $supportedComponents = null;
+        }
+        Sabre_CalDAV_ICalendarUtil::validateICalendarObject($calendarData, $supportedComponents);
+
+        $this->caldavBackend->updateCalendarObject($this->calendarInfo['id'],$this->objectData['uri'],$calendarData);
+        $this->objectData['calendardata'] = $calendarData;
+
+    }
+
+    /**
+     * Deletes the calendar object 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        $this->caldavBackend->deleteCalendarObject($this->calendarInfo['id'],$this->objectData['uri']);
+
+    }
+
+    /**
+     * Returns the mime content-type 
+     * 
+     * @return string 
+     */
+    public function getContentType() {
+
+        return 'text/calendar';
+
+    }
+
+    /**
+     * Returns an ETag for this object.
+     *
+     * The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
+     * 
+     * @return string 
+     */
+    public function getETag() {
+
+        if (isset($this->objectData['etag'])) {
+            return $this->objectData['etag'];
+        } else {
+            return '"' . md5($this->get()). '"';
+        }
+
+    }
+
+    /**
+     * Returns the last modification date as a unix timestamp
+     * 
+     * @return time 
+     */
+    public function getLastModified() {
+
+        return $this->objectData['lastmodified'];
+
+    }
+
+    /**
+     * Returns the size of this object in bytes 
+     * 
+     * @return int
+     */
+    public function getSize() {
+
+        return strlen($this->objectData['calendardata']);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->calendarInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->calendarInfo['principaluri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+}
+
diff --git a/3rdparty/Sabre/CalDAV/CalendarRootNode.php b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
new file mode 100644
index 0000000000000000000000000000000000000000..69669a9d7fd96a5612661cf463621f242c8fcd3e
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/CalendarRootNode.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * Users collection 
+ *
+ * This object is responsible for generating a collection of users.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_CalendarRootNode extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * CalDAV backend 
+     * 
+     * @var Sabre_CalDAV_Backend_Abstract 
+     */
+    protected $caldavBackend;
+
+    /**
+     * Constructor 
+     *
+     * This constructor needs both an authentication and a caldav backend.
+     *
+     * By default this class will show a list of calendar collections for 
+     * principals in the 'principals' collection. If your main principals are 
+     * actually located in a different path, use the $principalPrefix argument 
+     * to override this.
+     *
+     *
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param Sabre_CalDAV_Backend_Abstract $caldavBackend 
+     * @param string $principalPrefix
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CalDAV_Backend_Abstract $caldavBackend, $principalPrefix = 'principals') {
+
+        parent::__construct($principalBackend, $principalPrefix);
+        $this->caldavBackend = $caldavBackend;
+
+    }
+
+    /**
+     * Returns the nodename
+     *
+     * We're overriding this, because the default will be the 'principalPrefix',
+     * and we want it to be Sabre_CalDAV_Plugin::CALENDAR_ROOT 
+     * 
+     * @return void
+     */
+    public function getName() {
+
+        return Sabre_CalDAV_Plugin::CALENDAR_ROOT;
+
+    }
+
+    /**
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
+     * 
+     * @param array $principal 
+     * @return Sabre_DAV_INode 
+     */
+    public function getChildForPrincipal(array $principal) {
+
+        return new Sabre_CalDAV_UserCalendars($this->principalBackend, $this->caldavBackend, $principal['uri']);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Exception/InvalidICalendarObject.php b/3rdparty/Sabre/CalDAV/Exception/InvalidICalendarObject.php
new file mode 100644
index 0000000000000000000000000000000000000000..656b7286a66538dbeb487983a226384705e7c6b9
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Exception/InvalidICalendarObject.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * InvalidICalendarObject
+ *
+ * This exception is thrown when an attempt is made to create or update
+ * an invalid ICalendar object
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Exception_InvalidICalendarObject extends Sabre_DAV_Exception_PreconditionFailed {
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICSExportPlugin.php b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e2c6eb93d286da842e1ecb9035d031b0cb885c2
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICSExportPlugin.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * ICS Exporter
+ *
+ * This plugin adds the ability to export entire calendars as .ics files.
+ * This is useful for clients that don't support CalDAV yet. They often do 
+ * support ics files.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_ICSExportPlugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Reference to Server class 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * Initializes the plugin and registers event handlers 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+
+    }
+
+    /**
+     * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+     * with ?export
+     * 
+     * @param string $method
+     * @param string $uri
+     * @return void
+     */
+    public function beforeMethod($method, $uri) {
+
+        if ($method!='GET') return;
+        if ($this->server->httpRequest->getQueryString()!='export') return;
+
+        // splitting uri
+        list($uri) = explode('?',$uri,2);
+
+        $node = $this->server->tree->getNodeForPath($uri);
+
+        if (!($node instanceof Sabre_CalDAV_Calendar)) return;
+
+        $this->server->httpResponse->setHeader('Content-Type','text/calendar');
+        $this->server->httpResponse->sendStatus(200);
+        $this->server->httpResponse->sendBody($this->generateICS($this->server->tree->getChildren($uri)));
+
+        // Returning false to break the event chain
+        return false;
+
+    }
+
+    /**
+     * Merges all calendar objects, and builds one big ics export 
+     * 
+     * @param array $nodes 
+     * @return void
+     */
+    public function generateICS(array $nodes) {
+
+        $calendar = new Sabre_VObject_Component('vcalendar');
+        $calendar->version = '2.0';
+        $calendar->prodid = '-//SabreDAV//SabreDAV ' . Sabre_DAV_Version::VERSION . '//EN';
+        $calendar->calscale = 'GREGORIAN';
+
+        $collectedTimezones = array();
+
+        $timezones = array();
+        $objects = array();
+
+        foreach($nodes as $node) {
+
+            $nodeData = $node->get();
+            $nodeComp = Sabre_VObject_Reader::read($nodeData);
+
+            foreach($nodeComp->children() as $child) {
+
+                switch($child->name) {
+                    case 'VEVENT' :
+                    case 'VTODO' :
+                    case 'VJOURNAL' :
+                        $objects[] = $child;
+                        break;
+
+                    // VTIMEZONE is special, because we need to filter out the duplicates
+                    case 'VTIMEZONE' :
+                        // Naively just checking tzid.
+                        if (in_array((string)$child->TZID, $collectedTimezones)) continue;
+
+                        $timezones[] = $child;
+                        $collectedTimezones[] = $child->TZID;
+                        break;
+
+
+                }
+
+
+            }
+
+
+        }
+
+        foreach($timezones as $tz) $calendar->add($tz);
+        foreach($objects as $obj) $calendar->add($obj);
+
+        return $calendar->serialize();
+
+    } 
+
+}
diff --git a/3rdparty/Sabre/CalDAV/ICalendarUtil.php b/3rdparty/Sabre/CalDAV/ICalendarUtil.php
new file mode 100644
index 0000000000000000000000000000000000000000..699abfdd6f5e0726701664a849405df4b422fe32
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/ICalendarUtil.php
@@ -0,0 +1,157 @@
+<?php
+
+/**
+ * This class contains several utilities related to the ICalendar (rfc2445) format
+ *
+ * This class is now deprecated, and won't be further maintained. Please use 
+ * the Sabre_VObject package for your ics parsing needs.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ * @deprecated Use Sabre_VObject instead.
+ */
+class Sabre_CalDAV_ICalendarUtil {
+
+    /**
+     * Validates an ICalendar object
+     *
+     * This method makes sure this ICalendar object is properly formatted.
+     * If we can't parse it, we'll throw exceptions.
+     *
+     * @param string $icalData 
+     * @param array $allowedComponents 
+     * @return bool 
+     */
+    static function validateICalendarObject($icalData, array $allowedComponents = null) {
+
+        $xcal = simplexml_load_string(self::toXCal($icalData));
+        if (!$xcal) throw new Sabre_CalDAV_Exception_InvalidICalendarObject('Invalid calendarobject format');
+
+        $xcal->registerXPathNameSpace('cal','urn:ietf:params:xml:ns:xcal');
+        
+        // Check if there's only 1 component
+        $components = array('vevent','vtodo','vjournal','vfreebusy');
+        $componentsFound = array();
+
+        foreach($components as $component) {
+            $test = $xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:' . $component);
+            if (is_array($test)) $componentsFound = array_merge($componentsFound, $test);
+        }
+        if (count($componentsFound)<1) {
+            throw new Sabre_CalDAV_Exception_InvalidICalendarObject('One VEVENT, VTODO, VJOURNAL or VFREEBUSY must be specified. 0 found.');
+        }
+        $component = $componentsFound[0];
+
+        if (is_null($allowedComponents)) return true;
+
+        // Check if the component is allowed
+        $name = $component->getName();
+        if (!in_array(strtoupper($name),$allowedComponents)) {
+            throw new Sabre_CalDAV_Exception_InvalidICalendarObject(strtoupper($name) . ' is not allowed in this calendar.');
+        }
+
+        if (count($xcal->xpath('/cal:iCalendar/cal:vcalendar/cal:method'))>0) {
+            throw new Sabre_CalDAV_Exception_InvalidICalendarObject('The METHOD property is not allowed in calendar objects');
+        }
+
+        return true;
+
+    }
+
+    /**
+     * Converts ICalendar data to XML.
+     *
+     * Properties are converted to lowercase xml elements. Parameters are;
+     * converted to attributes. BEGIN:VEVENT is converted to <vevent> and
+     * END:VEVENT </vevent> as well as other components.
+     *
+     * It's a very loose parser. If any line does not conform to the spec, it
+     * will simply be ignored. It will try to detect if \r\n or \n line endings
+     * are used.
+     *
+     * @todo Currently quoted attributes are not parsed correctly.
+     * @see http://tools.ietf.org/html/draft-royer-calsch-xcal-03
+     * @param string $icalData 
+     * @return string. 
+     */
+    static function toXCAL($icalData) {
+
+        // Detecting line endings
+        $lb="\r\n";
+        if (strpos($icalData,"\r\n")!==false) $lb = "\r\n";
+        elseif (strpos($icalData,"\n")!==false) $lb = "\n";
+
+        // Splitting up items per line
+        $lines = explode($lb,$icalData);
+
+        // Properties can be folded over 2 lines. In this case the second
+        // line will be preceeded by a space or tab.
+        $lines2 = array();
+        foreach($lines as $line) {
+
+            if (!$line) continue;
+            if ($line[0]===" " || $line[0]==="\t") {
+                $lines2[count($lines2)-1].=substr($line,1);
+                continue;
+            }
+
+            $lines2[]=$line;
+
+        }
+
+        $xml = '<?xml version="1.0"?>' . "\n";
+        $xml.= "<iCalendar xmlns=\"urn:ietf:params:xml:ns:xcal\">\n";
+
+        $spaces = 2;
+        foreach($lines2 as $line) {
+
+            $matches = array();
+            // This matches PROPERTYNAME;ATTRIBUTES:VALUE
+            if (!preg_match('/^([^:^;]*)(?:;([^:]*))?:(.*)$/',$line,$matches))
+                continue;
+
+            $propertyName = strtolower($matches[1]);
+            $attributes = $matches[2];
+            $value = $matches[3];
+
+            // If the line was in the format BEGIN:COMPONENT or END:COMPONENT, we need to special case it.
+            if ($propertyName === 'begin') {
+                $xml.=str_repeat(" ",$spaces);
+                $xml.='<' . strtolower($value) . ">\n";
+                $spaces+=2;
+                continue;
+            } elseif ($propertyName === 'end') {
+                $spaces-=2;
+                $xml.=str_repeat(" ",$spaces);
+                $xml.='</' . strtolower($value) . ">\n";
+                continue;
+            }
+
+            $xml.=str_repeat(" ",$spaces);
+            $xml.='<' . $propertyName;
+            if ($attributes) {
+                // There can be multiple attributes
+                $attributes = explode(';',$attributes);
+                foreach($attributes as $att) {
+  
+                    list($attName,$attValue) = explode('=',$att,2);
+                    $attName = strtolower($attName);
+                    if ($attName === 'language') $attName='xml:lang';
+                    $xml.=' ' . $attName . '="' . htmlspecialchars($attValue) . '"';
+
+                }
+            }
+
+            $xml.='>'. htmlspecialchars(trim($value)) . '</' . $propertyName . ">\n";
+          
+        }
+        $xml.="</iCalendar>";
+        return $xml;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/CalDAV/Plugin.php b/3rdparty/Sabre/CalDAV/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..640595e74babca337367fecbbed63e4336f391fa
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Plugin.php
@@ -0,0 +1,788 @@
+<?php
+
+/**
+ * CalDAV plugin
+ *
+ * This plugin provides functionality added by CalDAV (RFC 4791)
+ * It implements new reports, and the MKCALENDAR method.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * This is the official CalDAV namespace
+     */
+    const NS_CALDAV = 'urn:ietf:params:xml:ns:caldav';
+    
+    /**
+     * This is the namespace for the proprietary calendarserver extensions
+     */
+    const NS_CALENDARSERVER = 'http://calendarserver.org/ns/';
+
+    /**
+     * The following constants are used to differentiate
+     * the various filters for the calendar-query report
+     */
+    const FILTER_COMPFILTER   = 1;
+    const FILTER_TIMERANGE    = 3;
+    const FILTER_PROPFILTER   = 4;
+    const FILTER_PARAMFILTER  = 5;
+    const FILTER_TEXTMATCH    = 6;
+
+    /**
+     * The hardcoded root for calendar objects. It is unfortunate
+     * that we're stuck with it, but it will have to do for now
+     */
+    const CALENDAR_ROOT = 'calendars';
+
+    /**
+     * Reference to server object 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * Use this method to tell the server this plugin defines additional
+     * HTTP methods.
+     *
+     * This method is passed a uri. It should only return HTTP methods that are 
+     * available for the specified uri.
+     *
+     * @param string $uri
+     * @return array 
+     */
+    public function getHTTPMethods($uri) {
+
+        // The MKCALENDAR is only available on unmapped uri's, whose
+        // parents extend IExtendedCollection
+        list($parent, $name) = Sabre_DAV_URLUtil::splitPath($uri);
+
+        $node = $this->server->tree->getNodeForPath($parent);
+
+        if ($node instanceof Sabre_DAV_IExtendedCollection) {
+            try {
+                $node->getChild($name);
+            } catch (Sabre_DAV_Exception_FileNotFound $e) {
+                return array('MKCALENDAR');
+            }
+        }
+        return array();
+
+    }
+
+    /**
+     * Returns a list of features for the DAV: HTTP header. 
+     * 
+     * @return array 
+     */
+    public function getFeatures() {
+
+        return array('calendar-access', 'calendar-proxy');
+
+    }
+
+    /**
+     * Returns a plugin name.
+     * 
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
+     * 
+     * @return string 
+     */
+    public function getPluginName() {
+
+        return 'caldav';
+
+    }
+
+    /**
+     * Returns a list of reports this plugin supports.
+     *
+     * This will be used in the {DAV:}supported-report-set property.
+     * Note that you still need to subscribe to the 'report' event to actually 
+     * implement them 
+     * 
+     * @param string $uri
+     * @return array 
+     */
+    public function getSupportedReportSet($uri) {
+
+        $node = $this->server->tree->getNodeForPath($uri);
+        if ($node instanceof Sabre_CalDAV_Calendar || $node instanceof Sabre_CalDAV_CalendarObject) {
+            return array(
+                 '{' . self::NS_CALDAV . '}calendar-multiget',
+                 '{' . self::NS_CALDAV . '}calendar-query',
+            );
+        }
+        return array();
+
+    }
+
+    /**
+     * Initializes the plugin 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
+        //$server->subscribeEvent('unknownMethod',array($this,'unknownMethod2'),1000);
+        $server->subscribeEvent('report',array($this,'report'));
+        $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
+
+        $server->xmlNamespaces[self::NS_CALDAV] = 'cal';
+        $server->xmlNamespaces[self::NS_CALENDARSERVER] = 'cs';
+
+        $server->propertyMap['{' . self::NS_CALDAV . '}supported-calendar-component-set'] = 'Sabre_CalDAV_Property_SupportedCalendarComponentSet';
+
+        $server->resourceTypeMapping['Sabre_CalDAV_Calendar'] = '{urn:ietf:params:xml:ns:caldav}calendar';
+        $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read';
+        $server->resourceTypeMapping['Sabre_CalDAV_Principal_ProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write';
+
+        array_push($server->protectedProperties,
+
+            '{' . self::NS_CALDAV . '}supported-calendar-component-set',
+            '{' . self::NS_CALDAV . '}supported-calendar-data',
+            '{' . self::NS_CALDAV . '}max-resource-size',
+            '{' . self::NS_CALDAV . '}min-date-time',
+            '{' . self::NS_CALDAV . '}max-date-time',
+            '{' . self::NS_CALDAV . '}max-instances',
+            '{' . self::NS_CALDAV . '}max-attendees-per-instance',
+            '{' . self::NS_CALDAV . '}calendar-home-set',
+            '{' . self::NS_CALDAV . '}supported-collation-set',
+            '{' . self::NS_CALDAV . '}calendar-data',
+
+            // scheduling extension
+            '{' . self::NS_CALDAV . '}calendar-user-address-set',
+
+            // CalendarServer extensions
+            '{' . self::NS_CALENDARSERVER . '}getctag',
+            '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for',
+            '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for'
+
+        );
+    }
+
+    /**
+     * This function handles support for the MKCALENDAR method
+     * 
+     * @param string $method 
+     * @return bool 
+     */
+    public function unknownMethod($method, $uri) {
+
+        if ($method!=='MKCALENDAR') return;
+
+        $this->httpMkCalendar($uri);
+        // false is returned to stop the unknownMethod event
+        return false;
+
+    }
+
+    /**
+     * This functions handles REPORT requests specific to CalDAV 
+     * 
+     * @param string $reportName 
+     * @param DOMNode $dom 
+     * @return bool 
+     */
+    public function report($reportName,$dom) {
+
+        switch($reportName) { 
+            case '{'.self::NS_CALDAV.'}calendar-multiget' :
+                $this->calendarMultiGetReport($dom);
+                return false;
+            case '{'.self::NS_CALDAV.'}calendar-query' :
+                $this->calendarQueryReport($dom);
+                return false;
+
+        }
+
+
+    }
+
+    /**
+     * This function handles the MKCALENDAR HTTP method, which creates
+     * a new calendar.
+     * 
+     * @param string $uri
+     * @return void 
+     */
+    public function httpMkCalendar($uri) {
+
+        // Due to unforgivable bugs in iCal, we're completely disabling MKCALENDAR support
+        // for clients matching iCal in the user agent
+        //$ua = $this->server->httpRequest->getHeader('User-Agent');
+        //if (strpos($ua,'iCal/')!==false) {
+        //    throw new Sabre_DAV_Exception_Forbidden('iCal has major bugs in it\'s RFC3744 support. Therefore we are left with no other choice but disabling this feature.');
+        //}
+
+        $body = $this->server->httpRequest->getBody(true);
+        $properties = array();
+
+        if ($body) {
+
+            $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+            foreach($dom->firstChild->childNodes as $child) {
+
+                if (Sabre_DAV_XMLUtil::toClarkNotation($child)!=='{DAV:}set') continue;
+                foreach(Sabre_DAV_XMLUtil::parseProperties($child,$this->server->propertyMap) as $k=>$prop) {
+                    $properties[$k] = $prop;
+                }
+            
+            }
+        }
+
+        $resourceType = array('{DAV:}collection','{urn:ietf:params:xml:ns:caldav}calendar');
+
+        $this->server->createCollection($uri,$resourceType,$properties);
+
+        $this->server->httpResponse->sendStatus(201);
+        $this->server->httpResponse->setHeader('Content-Length',0);
+    }
+
+    /**
+     * beforeGetProperties
+     *
+     * This method handler is invoked before any after properties for a
+     * resource are fetched. This allows us to add in any CalDAV specific 
+     * properties. 
+     * 
+     * @param string $path
+     * @param Sabre_DAV_INode $node
+     * @param array $requestedProperties
+     * @param array $returnedProperties
+     * @return void
+     */
+    public function beforeGetProperties($path, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
+
+        if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+            // calendar-home-set property
+            $calHome = '{' . self::NS_CALDAV . '}calendar-home-set';
+            if (in_array($calHome,$requestedProperties)) {
+                $principalId = $node->getName(); 
+                $calendarHomePath = self::CALENDAR_ROOT . '/' . $principalId . '/';
+                unset($requestedProperties[$calHome]);
+                $returnedProperties[200][$calHome] = new Sabre_DAV_Property_Href($calendarHomePath);
+            }
+
+            // calendar-user-address-set property
+            $calProp = '{' . self::NS_CALDAV . '}calendar-user-address-set';
+            if (in_array($calProp,$requestedProperties)) {
+
+                $addresses = $node->getAlternateUriSet();
+                $addresses[] = $this->server->getBaseUri() . $node->getPrincipalUrl();
+                unset($requestedProperties[$calProp]);
+                $returnedProperties[200][$calProp] = new Sabre_DAV_Property_HrefList($addresses, false);
+
+            }
+
+            // These two properties are shortcuts for ical to easily find 
+            // other principals this principal has access to.
+            $propRead = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-read-for';
+            $propWrite = '{' . self::NS_CALENDARSERVER . '}calendar-proxy-write-for';
+            if (in_array($propRead,$requestedProperties) || in_array($propWrite,$requestedProperties)) {
+
+                $membership = $node->getGroupMembership();
+                $readList = array();
+                $writeList = array();
+
+                foreach($membership as $group) {
+
+                    $groupNode = $this->server->tree->getNodeForPath($group);
+
+                    // If the node is either ap proxy-read or proxy-write 
+                    // group, we grab the parent principal and add it to the 
+                    // list.
+                    if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyRead) {
+                        list($readList[]) = Sabre_DAV_URLUtil::splitPath($group);
+                    }
+                    if ($groupNode instanceof Sabre_CalDAV_Principal_ProxyWrite) {
+                        list($writeList[]) = Sabre_DAV_URLUtil::splitPath($group);
+                    }
+
+                }
+                if (in_array($propRead,$requestedProperties)) {
+                    unset($requestedProperties[$propRead]);
+                    $returnedProperties[200][$propRead] = new Sabre_DAV_Property_HrefList($readList);
+                }
+                if (in_array($propWrite,$requestedProperties)) {
+                    unset($requestedProperties[$propWrite]);
+                    $returnedProperties[200][$propWrite] = new Sabre_DAV_Property_HrefList($writeList);
+                }
+
+            }
+
+        } // instanceof IPrincipal
+
+
+        if ($node instanceof Sabre_CalDAV_CalendarObject) {
+            // The calendar-data property is not supposed to be a 'real' 
+            // property, but in large chunks of the spec it does act as such. 
+            // Therefore we simply expose it as a property.
+            $calDataProp = '{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}calendar-data';
+            if (in_array($calDataProp, $requestedProperties)) {
+                unset($requestedProperties[$calDataProp]);
+                $val = $node->get();
+                if (is_resource($val))
+                    $val = stream_get_contents($val);
+
+                // Taking out \r to not screw up the xml output
+                $returnedProperties[200][$calDataProp] = str_replace("\r","", $val);
+
+            }
+        }
+
+    }
+
+    /**
+     * This function handles the calendar-multiget REPORT.
+     *
+     * This report is used by the client to fetch the content of a series
+     * of urls. Effectively avoiding a lot of redundant requests.
+     * 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function calendarMultiGetReport($dom) {
+
+        $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+
+        $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
+        foreach($hrefElems as $elem) {
+            $uri = $this->server->calculateUri($elem->nodeValue);
+            list($objProps) = $this->server->getPropertiesForPath($uri,$properties);
+            $propertyList[]=$objProps;
+
+        }
+
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
+
+    }
+
+    /**
+     * This function handles the calendar-query REPORT
+     *
+     * This report is used by clients to request calendar objects based on
+     * complex conditions.
+     * 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function calendarQueryReport($dom) {
+
+        $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+
+        $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav','filter');
+        if ($filterNode->length!==1) {
+            throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element');
+        }
+        $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0));
+
+        $requestedCalendarData = true;
+
+        if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) {
+            // We always retrieve calendar-data, as we need it for filtering.
+            $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data';
+
+            // If calendar-data wasn't explicitly requested, we need to remove 
+            // it after processing.
+            $requestedCalendarData = false;
+        }
+
+        // These are the list of nodes that potentially match the requirement
+        $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(),$requestedProperties,$this->server->getHTTPDepth(0));
+
+        $verifiedNodes = array();
+
+        foreach($candidateNodes as $node) {
+
+            // If the node didn't have a calendar-data property, it must not be a calendar object
+            if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) continue;
+
+            if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'],$filters)) {
+                
+                if (!$requestedCalendarData) {
+                    unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']);
+                }
+                $verifiedNodes[] = $node;
+            } 
+
+        }
+
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes));
+
+    }
+
+
+    /**
+     * Verify if a list of filters applies to the calendar data object 
+     *
+     * The calendarData object must be a valid iCalendar blob. The list of 
+     * filters must be formatted as parsed by Sabre_CalDAV_Plugin::parseCalendarQueryFilters
+     *
+     * @param string $calendarData 
+     * @param array $filters 
+     * @return bool 
+     */
+    public function validateFilters($calendarData,$filters) {
+
+        // We are converting the calendar object to an XML structure
+        // This makes it far easier to parse
+        $xCalendarData = Sabre_CalDAV_ICalendarUtil::toXCal($calendarData);
+        $xml = simplexml_load_string($xCalendarData);
+        $xml->registerXPathNamespace('c','urn:ietf:params:xml:ns:xcal');
+
+        foreach($filters as $xpath=>$filter) {
+
+            // if-not-defined comes first
+            if (isset($filter['is-not-defined'])) {
+                if (!$xml->xpath($xpath))
+                    continue;
+                else
+                    return false;
+                
+            }
+
+            $elem = $xml->xpath($xpath);
+            
+            if (!$elem) return false;
+            $elem = $elem[0];
+
+            if (isset($filter['time-range'])) {
+
+                switch($elem->getName()) {
+                    case 'vevent' :
+                        $result = $this->validateTimeRangeFilterForEvent($xml,$xpath,$filter);
+                        if ($result===false) return false;
+                        break;
+                    case 'vtodo' :
+                        $result = $this->validateTimeRangeFilterForTodo($xml,$xpath,$filter);
+                        if ($result===false) return false;
+                        break;
+                    case 'vjournal' :
+                    case 'vfreebusy' :
+                    case 'valarm' :
+                        // TODO: not implemented
+                        break;
+
+                    /*
+
+                    case 'vjournal' :
+                        $result = $this->validateTimeRangeFilterForJournal($xml,$xpath,$filter);
+                        if ($result===false) return false;
+                        break;
+                    case 'vfreebusy' :
+                        $result = $this->validateTimeRangeFilterForFreeBusy($xml,$xpath,$filter);
+                        if ($result===false) return false;
+                        break;
+                    case 'valarm' :
+                        $result = $this->validateTimeRangeFilterForAlarm($xml,$xpath,$filter);
+                        if ($result===false) return false;
+                        break;
+
+                        */
+
+                }
+
+            } 
+
+            if (isset($filter['text-match'])) {
+                $currentString = (string)$elem;
+
+                $isMatching = Sabre_DAV_StringUtil::textMatch($currentString, $filter['text-match']['value'], $filter['text-match']['collation']);
+                if ($filter['text-match']['negate-condition'] && $isMatching) return false;
+                if (!$filter['text-match']['negate-condition'] && !$isMatching) return false;
+                
+            }
+
+        }
+        return true;
+        
+    }
+
+    /**
+     * Checks whether a time-range filter matches an event.
+     * 
+     * @param SimpleXMLElement $xml Event as xml object 
+     * @param string $currentXPath XPath to check 
+     * @param array $currentFilter Filter information 
+     * @return void
+     */
+    private function validateTimeRangeFilterForEvent(SimpleXMLElement $xml,$currentXPath,array $currentFilter) {
+
+        // Grabbing the DTSTART property
+        $xdtstart = $xml->xpath($currentXPath.'/c:dtstart');
+        if (!count($xdtstart)) {
+            throw new Sabre_DAV_Exception_BadRequest('DTSTART property missing from calendar object');
+        }
+
+        // The dtstart can be both a date, or datetime property
+        if ((string)$xdtstart[0]['value']==='DATE' || strlen((string)$xdtstart[0])===8) {
+            $isDateTime = false;
+        } else {
+            $isDateTime = true;
+        }
+
+        // Determining the timezone
+        if ($tzid = (string)$xdtstart[0]['tzid']) {
+            $tz = new DateTimeZone($tzid);
+        } else {
+            $tz = null;
+        }
+        if ($isDateTime) {
+            $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtstart[0],$tz);
+        } else {
+            $dtstart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtstart[0]);
+        }
+
+
+        // Grabbing the DTEND property
+        $xdtend = $xml->xpath($currentXPath.'/c:dtend');
+        $dtend = null;
+
+        if (count($xdtend)) {
+            // Determining the timezone
+            if ($tzid = (string)$xdtend[0]['tzid']) {
+                $tz = new DateTimeZone($tzid);
+            } else {
+                $tz = null;
+            }
+
+            // Since the VALUE prameter of both DTSTART and DTEND must be the same
+            // we can assume we don't need to check the VALUE paramter of DTEND.
+            if ($isDateTime) {
+                $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdtend[0],$tz);
+            } else {
+                $dtend = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdtend[0],$tz);
+            }
+
+        } 
+        
+        if (is_null($dtend)) {
+            // The DTEND property was not found. We will first see if the event has a duration
+            // property
+
+            $xduration = $xml->xpath($currentXPath.'/c:duration');
+            if (count($xduration)) {
+                $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]);
+
+                // Making sure that the duration is bigger than 0 seconds.
+                $tempDT = clone $dtstart;
+                $tempDT->modify($duration);
+                if ($tempDT > $dtstart) {
+
+                    // use DTEND = DTSTART + DURATION 
+                    $dtend = $tempDT;
+                } else {
+                    // use DTEND = DTSTART
+                    $dtend = $dtstart;
+                }
+
+            }
+        }
+
+        if (is_null($dtend)) {
+            if ($isDateTime) {
+                // DTEND = DTSTART
+                $dtend = $dtstart;
+            } else {
+                // DTEND = DTSTART + 1 DAY
+                $dtend = clone $dtstart;
+                $dtend->modify('+1 day');
+            }
+        }
+        // TODO: we need to properly parse RRULE's, but it's very difficult.
+        // For now, we're always returning events if they have an RRULE at all.
+        $rrule = $xml->xpath($currentXPath.'/c:rrule');
+        $hasRrule = (count($rrule))>0; 
+       
+        if (!is_null($currentFilter['time-range']['start']) && $currentFilter['time-range']['start'] >= $dtend)  return false;
+        if (!is_null($currentFilter['time-range']['end'])   && $currentFilter['time-range']['end']   <= $dtstart && !$hasRrule) return false;
+        return true;
+    
+    }
+
+    private function validateTimeRangeFilterForTodo(SimpleXMLElement $xml,$currentXPath,array $filter) {
+
+        // Gathering all relevant elements
+
+        $dtStart = null;
+        $duration = null;
+        $due = null;
+        $completed = null;
+        $created = null;
+
+        $xdt = $xml->xpath($currentXPath.'/c:dtstart');
+        if (count($xdt)) {
+            // The dtstart can be both a date, or datetime property
+            if ((string)$xdt[0]['value']==='DATE') {
+                $isDateTime = false;
+            } else {
+                $isDateTime = true;
+            }
+
+            // Determining the timezone
+            if ($tzid = (string)$xdt[0]['tzid']) {
+                $tz = new DateTimeZone($tzid);
+            } else {
+                $tz = null;
+            }
+            if ($isDateTime) {
+                $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz);
+            } else {
+                $dtStart = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]);
+            }
+        }
+
+        // Only need to grab duration if dtStart is set
+        if (!is_null($dtStart)) {
+
+            $xduration = $xml->xpath($currentXPath.'/c:duration');
+            if (count($xduration)) {
+                $duration = Sabre_CalDAV_XMLUtil::parseICalendarDuration((string)$xduration[0]);
+            }
+
+        }
+
+        if (!is_null($dtStart) && !is_null($duration)) {
+
+            // Comparision from RFC 4791:
+            // (start <= DTSTART+DURATION) AND ((end > DTSTART) OR (end >= DTSTART+DURATION))
+
+            $end = clone $dtStart;
+            $end->modify($duration);
+
+            if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $end) &&
+                (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $dtStart || $filter['time-range']['end'] >= $end) ) {
+                return true;
+            } else {
+                return false;
+            }
+
+        }
+
+        // Need to grab the DUE property
+        $xdt = $xml->xpath($currentXPath.'/c:due');
+        if (count($xdt)) {
+            // The due property can be both a date, or datetime property
+            if ((string)$xdt[0]['value']==='DATE') {
+                $isDateTime = false;
+            } else {
+                $isDateTime = true;
+            }
+            // Determining the timezone
+            if ($tzid = (string)$xdt[0]['tzid']) {
+                $tz = new DateTimeZone($tzid);
+            } else {
+                $tz = null;
+            }
+            if ($isDateTime) {
+                $due = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0],$tz);
+            } else {
+                $due = Sabre_CalDAV_XMLUtil::parseICalendarDate((string)$xdt[0]);
+            }
+        }
+
+        if (!is_null($dtStart) && !is_null($due)) {
+
+            // Comparision from RFC 4791:
+            // ((start < DUE) OR (start <= DTSTART)) AND ((end > DTSTART) OR (end >= DUE))
+            
+            if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due || $filter['time-range']['start'] < $dtstart) &&
+                (is_null($filter['time-range']['end'])   || $filter['time-range']['end'] >= $due) ) {
+                return true;
+            } else {
+                return false;
+            }
+
+        }
+
+        if (!is_null($dtStart)) {
+            
+            // Comparision from RFC 4791
+            // (start <= DTSTART)  AND (end > DTSTART)
+            if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $dtStart) &&
+                 (is_null($filter['time-range']['end'])   || $filter['time-range']['end'] > $dtStart) ) {
+                 return true;
+            } else {
+                return false;
+            }
+
+        }
+
+        if (!is_null($due)) {
+            
+            // Comparison from RFC 4791
+            // (start < DUE) AND (end >= DUE)
+            if ( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] < $due) &&
+                 (is_null($filter['time-range']['end'])   || $filter['time-range']['end'] >= $due) ) {
+                 return true;
+            } else {
+                return false;
+            }
+
+        }
+        // Need to grab the COMPLETED property
+        $xdt = $xml->xpath($currentXPath.'/c:completed');
+        if (count($xdt)) {
+            $completed = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]);
+        }
+        // Need to grab the CREATED property
+        $xdt = $xml->xpath($currentXPath.'/c:created');
+        if (count($xdt)) {
+            $created = Sabre_CalDAV_XMLUtil::parseICalendarDateTime((string)$xdt[0]);
+        }
+
+        if (!is_null($completed) && !is_null($created)) {
+            // Comparison from RFC 4791
+            // ((start <= CREATED) OR (start <= COMPLETED)) AND ((end >= CREATED) OR (end >= COMPLETED))
+            if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $created || $filter['time-range']['start'] <= $completed) &&
+                (is_null($filter['time-range']['end'])   || $filter['time-range']['end'] >= $created   || $filter['time-range']['end'] >= $completed)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        if (!is_null($completed)) {
+            // Comparison from RFC 4791
+            // (start <= COMPLETED) AND (end  >= COMPLETED)
+            if( (is_null($filter['time-range']['start']) || $filter['time-range']['start'] <= $completed) &&
+                (is_null($filter['time-range']['end'])   || $filter['time-range']['end'] >= $completed)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        if (!is_null($created)) {
+            // Comparison from RFC 4791
+            // (end > CREATED)
+            if( (is_null($filter['time-range']['end']) || $filter['time-range']['end'] > $created) ) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+
+        // Everything else is TRUE
+        return true;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/Collection.php b/3rdparty/Sabre/CalDAV/Principal/Collection.php
new file mode 100644
index 0000000000000000000000000000000000000000..13435b2448efc55a0f7b8439e6a8727174d2cfd7
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/Collection.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * Principal collection
+ *
+ * This is an alternative collection to the standard ACL principal collection.
+ * This collection adds support for the calendar-proxy-read and 
+ * calendar-proxy-write sub-principals, as defined by the caldav-proxy 
+ * specification.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_Collection extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * Returns a child object based on principal information 
+     * 
+     * @param array $principalInfo 
+     * @return Sabre_CalDAV_Principal_User 
+     */
+    public function getChildForPrincipal(array $principalInfo) {
+
+        return new Sabre_CalDAV_Principal_User($this->principalBackend, $principalInfo);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php
new file mode 100644
index 0000000000000000000000000000000000000000..f531d85d1ffffacb2bf00101c942e0bb2b0953f3
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/ProxyRead.php
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * ProxyRead principal
+ *
+ * This class represents a principal group, hosted under the main principal.
+ * This is needed to implement 'Calendar delegation' support. This class is 
+ * instantiated by Sabre_CalDAV_Principal_User.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_ProxyRead implements Sabre_DAVACL_IPrincipal {
+
+    /**
+     * Principal information from the parent principal. 
+     * 
+     * @var array 
+     */
+    protected $principalInfo;
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * Creates the object.
+     *
+     * Note that you MUST supply the parent principal information. 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param array $principalInfo 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
+
+        $this->principalInfo = $principalInfo;
+        $this->principalBackend = $principalBackend;
+
+    }
+
+    /**
+     * Returns this principals name.
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return 'calendar-proxy-read';
+
+    }
+
+    /**
+     * Returns the last modification time 
+     *
+     * @return null 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Deletes the current node
+     *
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void 
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+    }
+
+    /**
+     * Renames the node
+     * 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+    }
+
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    public function getAlternateUriSet() {
+
+        return array();
+
+    }
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    public function getPrincipalUrl() {
+
+        return $this->principalInfo['uri'] . '/' . $this->getName(); 
+
+    }
+
+    /**
+     * Returns the list of group members
+     * 
+     * If this principal is a group, this function should return
+     * all member principal uri's for the group. 
+     * 
+     * @return array
+     */
+    public function getGroupMemberSet() {
+
+        return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Returns the list of groups this principal is member of
+     * 
+     * If this principal is a member of a (list of) groups, this function
+     * should return a list of principal uri's for it's members. 
+     * 
+     * @return array 
+     */
+    public function getGroupMembership() {
+
+        return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Sets a list of group members
+     *
+     * If this principal is a group, this method sets all the group members.
+     * The list of members is always overwritten, never appended to.
+     * 
+     * This method should throw an exception if the members could not be set. 
+     * 
+     * @param array $principals 
+     * @return void 
+     */
+    public function setGroupMemberSet(array $principals) {
+
+        $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
+
+    }
+
+    /**
+     * Returns the displayname
+     *
+     * This should be a human readable name for the principal.
+     * If none is available, return the nodename. 
+     * 
+     * @return string 
+     */
+    public function getDisplayName() {
+
+        return $this->getName();
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d8face20607f51a3f81e449143bdace2708aff7
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/ProxyWrite.php
@@ -0,0 +1,178 @@
+<?php
+
+/**
+ * ProxyWrite principal
+ *
+ * This class represents a principal group, hosted under the main principal.
+ * This is needed to implement 'Calendar delegation' support. This class is 
+ * instantiated by Sabre_CalDAV_Principal_User.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_ProxyWrite implements Sabre_DAVACL_IPrincipal {
+
+    /**
+     * Parent principal information 
+     * 
+     * @var array 
+     */
+    protected $principalInfo;
+
+    /**
+     * Principal Backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * Creates the object
+     *
+     * Note that you MUST supply the parent principal information. 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param array $principalInfo 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalInfo) {
+
+        $this->principalInfo = $principalInfo;
+        $this->principalBackend = $principalBackend;
+
+    }
+
+    /**
+     * Returns this principals name.
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return 'calendar-proxy-write';
+
+    }
+
+    /**
+     * Returns the last modification time 
+     *
+     * @return null 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Deletes the current node
+     *
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void 
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+    }
+
+    /**
+     * Renames the node
+     * 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+    }
+
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    public function getAlternateUriSet() {
+
+        return array();
+
+    }
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    public function getPrincipalUrl() {
+
+        return $this->principalInfo['uri'] . '/' . $this->getName(); 
+
+    }
+
+    /**
+     * Returns the list of group members
+     * 
+     * If this principal is a group, this function should return
+     * all member principal uri's for the group. 
+     * 
+     * @return array
+     */
+    public function getGroupMemberSet() {
+
+        return $this->principalBackend->getGroupMemberSet($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Returns the list of groups this principal is member of
+     * 
+     * If this principal is a member of a (list of) groups, this function
+     * should return a list of principal uri's for it's members. 
+     * 
+     * @return array 
+     */
+    public function getGroupMembership() {
+
+        return $this->principalBackend->getGroupMembership($this->getPrincipalUrl()); 
+
+    }
+
+    /**
+     * Sets a list of group members
+     *
+     * If this principal is a group, this method sets all the group members.
+     * The list of members is always overwritten, never appended to.
+     * 
+     * This method should throw an exception if the members could not be set. 
+     * 
+     * @param array $principals 
+     * @return void 
+     */
+    public function setGroupMemberSet(array $principals) {
+
+        $this->principalBackend->setGroupMemberSet($this->getPrincipalUrl(), $principals);
+
+    }
+
+    /**
+     * Returns the displayname
+     *
+     * This should be a human readable name for the principal.
+     * If none is available, return the nodename. 
+     * 
+     * @return string 
+     */
+    public function getDisplayName() {
+
+        return $this->getName();
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Principal/User.php b/3rdparty/Sabre/CalDAV/Principal/User.php
new file mode 100644
index 0000000000000000000000000000000000000000..034629b89b3154006e4756d3ffe614df8ca850b6
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Principal/User.php
@@ -0,0 +1,122 @@
+<?php
+
+/**
+ * CalDAV principal 
+ *
+ * This is a standard user-principal for CalDAV. This principal is also a 
+ * collection and returns the caldav-proxy-read and caldav-proxy-write child 
+ * principals.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Principal_User extends Sabre_DAVACL_Principal implements Sabre_DAV_ICollection {
+
+    /**
+     * Creates a new file in the directory 
+     * 
+     * @param string $name Name of the file 
+     * @param resource $data Initial payload, passed as a readable stream resource. 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void
+     */
+    public function createFile($name, $data = null) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
+
+    }
+
+    /**
+     * Creates a new subdirectory 
+     * 
+     * @param string $name 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
+
+    }
+
+    /**
+     * Returns a specific child node, referenced by its name 
+     * 
+     * @param string $name 
+     * @return Sabre_DAV_INode 
+     */
+    public function getChild($name) {
+
+        if ($name === 'calendar-proxy-read')
+            return new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties);
+
+        if ($name === 'calendar-proxy-write')
+            return new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties);
+
+        throw new Sabre_DAV_Exception_FileNotFound('Node with name ' . $name . ' was not found');
+
+    }
+
+    /**
+     * Returns an array with all the child nodes 
+     * 
+     * @return Sabre_DAV_INode[] 
+     */
+    public function getChildren() {
+
+        return array(
+            new Sabre_CalDAV_Principal_ProxyRead($this->principalBackend, $this->principalProperties),
+            new Sabre_CalDAV_Principal_ProxyWrite($this->principalBackend, $this->principalProperties),
+        );
+
+    }
+
+    /**
+     * Checks if a child-node with the specified name exists 
+     * 
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        return $name === 'calendar-proxy-read' ||  $name === 'calendar-proxy-write';
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+        );
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
new file mode 100644
index 0000000000000000000000000000000000000000..1bbaca6b8a75b2c57aafbc6ddf1f461af95b4f9e
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarComponentSet.php
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * Supported component set property
+ *
+ * This property is a representation of the supported-calendar_component-set 
+ * property in the CalDAV namespace. It simply requires an array of components,
+ * such as VEVENT, VTODO
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Property_SupportedCalendarComponentSet extends Sabre_DAV_Property {
+
+    /**
+     * List of supported components, such as "VEVENT, VTODO" 
+     * 
+     * @var array 
+     */
+    private $components;
+
+    /**
+     * Creates the property 
+     * 
+     * @param array $components 
+     */
+    public function __construct(array $components) {
+
+       $this->components = $components; 
+
+    }
+
+    /**
+     * Returns the list of supported components 
+     * 
+     * @return array 
+     */
+    public function getValue() {
+
+        return $this->components;
+
+    }
+
+    /**
+     * Serializes the property in a DOMDocument 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+       $doc = $node->ownerDocument;
+       foreach($this->components as $component) {
+
+            $xcomp = $doc->createElement('cal:comp');
+            $xcomp->setAttribute('name',$component);
+            $node->appendChild($xcomp); 
+
+       }
+
+    }
+
+    /**
+     * Unserializes the DOMElement back into a Property class.
+     * 
+     * @param DOMElement $node 
+     * @return void
+     */
+    static function unserialize(DOMElement $node) {
+
+        $components = array();
+        foreach($node->childNodes as $childNode) {
+            if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)==='{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}comp') {
+                $components[] = $childNode->getAttribute('name');
+            }
+        }
+        return new self($components);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php
new file mode 100644
index 0000000000000000000000000000000000000000..5010ee6d5257ad3085b646e95157d3870506279a
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Property/SupportedCalendarData.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Supported-calendar-data property
+ *
+ * This property is a representation of the supported-calendar-data property
+ * in the CalDAV namespace. SabreDAV only has support for text/calendar;2.0
+ * so the value is currently hardcoded.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Property_SupportedCalendarData extends Sabre_DAV_Property {
+
+    /**
+     * Serializes the property in a DOMDocument 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+
+        $prefix = isset($server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV])?$server->xmlNamespaces[Sabre_CalDAV_Plugin::NS_CALDAV]:'cal';
+
+        $caldata = $doc->createElement($prefix . ':calendar-data');
+        $caldata->setAttribute('content-type','text/calendar');
+        $caldata->setAttribute('version','2.0');
+
+        $node->appendChild($caldata); 
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php
new file mode 100644
index 0000000000000000000000000000000000000000..e585e9db3d877b5dc8e1d67ff7961791ac98800d
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Property/SupportedCollationSet.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * supported-collation-set property
+ *
+ * This property is a representation of the supported-collation-set property
+ * in the CalDAV namespace. 
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Property_SupportedCollationSet extends Sabre_DAV_Property {
+
+    /**
+     * Serializes the property in a DOM document 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        
+        $prefix = $node->lookupPrefix('urn:ietf:params:xml:ns:caldav');
+        if (!$prefix) $prefix = 'cal';
+
+        $node->appendChild(
+            $doc->createElement($prefix . ':supported-collation','i;ascii-casemap')
+        );
+        $node->appendChild(
+            $doc->createElement($prefix . ':supported-collation','i;octet')
+        );
+        $node->appendChild(
+            $doc->createElement($prefix . ':supported-collation','i;unicode-casemap')
+        );
+
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Server.php b/3rdparty/Sabre/CalDAV/Server.php
new file mode 100644
index 0000000000000000000000000000000000000000..969d69c6279a262286d1453d960a8da8aefbb29d
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Server.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * CalDAV server
+ *
+ * This script is a convenience script. It quickly sets up a WebDAV server
+ * with caldav and ACL support, and it creates the root 'principals' and
+ * 'calendars' collections.
+ *
+ * Note that if you plan to do anything moderately complex, you are advised to 
+ * not subclass this server, but use Sabre_DAV_Server directly instead. This 
+ * class is nothing more than an 'easy setup'.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Server extends Sabre_DAV_Server {
+
+    /**
+     * The authentication realm
+     *
+     * Note that if this changes, the hashes in the auth backend must also 
+     * be recalculated. 
+     * 
+     * @var string
+     */
+    public $authRealm = 'SabreDAV';
+
+    /**
+     * Sets up the object. A PDO object must be passed to setup all the backends.
+     * 
+     * @param PDO $pdo 
+     */
+    public function __construct(PDO $pdo) {
+
+        /* Backends */
+        $authBackend = new Sabre_DAV_Auth_Backend_PDO($pdo);
+        $calendarBackend = new Sabre_CalDAV_Backend_PDO($pdo);
+        $principalBackend = new Sabre_DAVACL_PrincipalBackend_PDO($pdo);
+
+        /* Directory structure */
+        $tree = array(
+            new Sabre_CalDAV_Principal_Collection($principalBackend),
+            new Sabre_CalDAV_CalendarRootNode($principalBackend, $calendarBackend),
+        );
+
+        /* Initializing server */
+        parent::__construct($tree);
+
+        /* Server Plugins */
+        $authPlugin = new Sabre_DAV_Auth_Plugin($authBackend,$this->authRealm);
+        $this->addPlugin($authPlugin);
+
+        $aclPlugin = new Sabre_DAVACL_Plugin();
+        $this->addPlugin($aclPlugin);
+
+        $caldavPlugin = new Sabre_CalDAV_Plugin();
+        $this->addPlugin($caldavPlugin);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CalDAV/UserCalendars.php b/3rdparty/Sabre/CalDAV/UserCalendars.php
new file mode 100644
index 0000000000000000000000000000000000000000..f52d65e9a73f222e325ec7695374ae4a0f93ffcb
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/UserCalendars.php
@@ -0,0 +1,280 @@
+<?php
+
+/**
+ * The UserCalenders class contains all calendars associated to one user 
+ * 
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_UserCalendars implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend
+     */
+    protected $principalBackend;
+
+    /**
+     * CalDAV backend
+     * 
+     * @var Sabre_CalDAV_Backend_Abstract
+     */
+    protected $caldavBackend;
+
+    /**
+     * Principal information 
+     * 
+     * @var array 
+     */
+    protected $principalInfo;
+    
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend
+     * @param Sabre_CalDAV_Backend_Abstract $caldavBackend 
+     * @param mixed $userUri 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, Sabre_CalDAV_Backend_Abstract $caldavBackend, $userUri) {
+
+        $this->principalBackend = $principalBackend;
+        $this->caldavBackend = $caldavBackend;
+        $this->principalInfo = $principalBackend->getPrincipalByPath($userUri);
+       
+    }
+
+    /**
+     * Returns the name of this object 
+     * 
+     * @return string
+     */
+    public function getName() {
+      
+        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalInfo['uri']);
+        return $name; 
+
+    }
+
+    /**
+     * Updates the name of this object 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden();
+
+    }
+
+    /**
+     * Deletes this object 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_Forbidden();
+
+    }
+
+    /**
+     * Returns the last modification date 
+     * 
+     * @return int 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Creates a new file under this object.
+     *
+     * This is currently not allowed
+     * 
+     * @param string $filename 
+     * @param resource $data 
+     * @return void
+     */
+    public function createFile($filename, $data=null) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
+
+    }
+
+    /**
+     * Creates a new directory under this object.
+     *
+     * This is currently not allowed.
+     * 
+     * @param string $filename 
+     * @return void
+     */
+    public function createDirectory($filename) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
+
+    }
+
+    /**
+     * Returns a single calendar, by name 
+     * 
+     * @param string $name
+     * @todo needs optimizing
+     * @return Sabre_CalDAV_Calendar 
+     */
+    public function getChild($name) {
+
+        foreach($this->getChildren() as $child) {
+            if ($name==$child->getName())
+                return $child;
+
+        }
+        throw new Sabre_DAV_Exception_FileNotFound('Calendar with name \'' . $name . '\' could not be found');
+
+    }
+
+    /**
+     * Checks if a calendar exists.
+     * 
+     * @param string $name
+     * @todo needs optimizing
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        foreach($this->getChildren() as $child) {
+            if ($name==$child->getName())
+                return true; 
+
+        }
+        return false;
+
+    }
+
+    /**
+     * Returns a list of calendars
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        $calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
+        $objs = array();
+        foreach($calendars as $calendar) {
+            $objs[] = new Sabre_CalDAV_Calendar($this->principalBackend, $this->caldavBackend, $calendar);
+        }
+        return $objs;
+
+    }
+
+    /**
+     * Creates a new calendar
+     * 
+     * @param string $name 
+     * @param string $properties 
+     * @return void
+     */
+    public function createExtendedCollection($name, array $resourceType, array $properties) {
+
+        if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar',$resourceType) || count($resourceType)!==2) {
+            throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
+        }
+        $this->caldavBackend->createCalendar($this->principalInfo['uri'], $name, $properties);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->principalInfo['uri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalInfo['uri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->principalInfo['uri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->principalInfo['uri'] . '/calendar-proxy-write',
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalInfo['uri'] . '/calendar-proxy-read',
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+
+}
diff --git a/3rdparty/Sabre/CalDAV/Version.php b/3rdparty/Sabre/CalDAV/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ecc0cebb376dc335244eff37ef8e5b35982c1f5
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the Sabre_CalDAV version constants.
+ * 
+ * @package Sabre
+ * @subpackage CalDAV 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.5.0';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/CalDAV/XMLUtil.php b/3rdparty/Sabre/CalDAV/XMLUtil.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf349a36aae6f13418dd7f25fd1710bcc3975750
--- /dev/null
+++ b/3rdparty/Sabre/CalDAV/XMLUtil.php
@@ -0,0 +1,208 @@
+<?php
+
+/**
+ * XML utilities for CalDAV 
+ *
+ * This class contains a few static methods used for parsing certain CalDAV 
+ * requests.
+ *
+ * @package Sabre
+ * @subpackage CalDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CalDAV_XMLUtil {
+
+    /**
+     * This function parses the calendar-query report request body
+     *
+     * The body is quite complicated, so we're turning it into a PHP
+     * array.
+     *
+     * The resulting associative array has xpath expressions as keys.
+     * By default the xpath expressions should simply be checked for existance
+     * The xpath expressions can point to elements or attributes.
+     * 
+     * The array values can contain a number of items, which alters the query
+     * filter. 
+     *
+     * * time-range. Must also check if the todo or event falls within the
+     *               specified timerange. How this is interpreted depends on
+     *               the type of object (VTODO, VEVENT, VJOURNAL, etc)
+     * * is-not-defined
+     *               Instead of checking if the attribute or element exist,
+     *               we must check if it doesn't.
+     * * text-match
+     *               Checks if the value of the attribute or element matches
+     *               the specified value. This is actually another array with
+     *               the 'collation', 'value' and 'negate-condition' items.
+     *
+     * Refer to the CalDAV spec for more information.
+     * 
+     * @param DOMNode $domNode 
+     * @param string $basePath used for recursive calls.
+     * @param array $filters used for recursive calls.
+     * @return array 
+     */
+    static public function parseCalendarQueryFilters($domNode,$basePath = '/c:iCalendar', &$filters = array()) {
+
+        foreach($domNode->childNodes as $child) {
+
+            switch(Sabre_DAV_XMLUtil::toClarkNotation($child)) {
+
+                case '{urn:ietf:params:xml:ns:caldav}comp-filter' :
+                case '{urn:ietf:params:xml:ns:caldav}prop-filter' :
+                   
+                    $filterName = $basePath . '/' . 'c:' . strtolower($child->getAttribute('name'));
+                    $filters[$filterName] = array(); 
+
+                    self::parseCalendarQueryFilters($child, $filterName,$filters);
+                    break;
+
+                case '{urn:ietf:params:xml:ns:caldav}time-range' :
+               
+                    if ($start = $child->getAttribute('start')) {
+                        $start = self::parseICalendarDateTime($start);
+                    } else {
+                        $start = null;
+                    }
+                    if ($end = $child->getAttribute('end')) {
+                        $end = self::parseICalendarDateTime($end);
+                    } else {
+                        $end = null;
+                    }
+
+                    if (!is_null($start) && !is_null($end) && $end <= $start) {
+                        throw new Sabre_DAV_Exception_BadRequest('The end-date must be larger than the start-date in the time-range filter');
+                    }
+
+                    $filters[$basePath]['time-range'] = array(
+                        'start' => $start,
+                        'end'   => $end
+                    );
+                    break;
+
+                case '{urn:ietf:params:xml:ns:caldav}is-not-defined' :
+                    $filters[$basePath]['is-not-defined'] = true;
+                    break;
+
+                case '{urn:ietf:params:xml:ns:caldav}param-filter' :
+               
+                    $filterName = $basePath . '/@' . strtolower($child->getAttribute('name'));
+                    $filters[$filterName] = array();
+                    self::parseCalendarQueryFilters($child, $filterName, $filters);
+                    break;
+
+                case '{urn:ietf:params:xml:ns:caldav}text-match' :
+               
+                    $collation = $child->getAttribute('collation');
+                    if (!$collation) $collation = 'i;ascii-casemap';
+
+                    $filters[$basePath]['text-match'] = array(
+                        'collation' => ($collation == 'default'?'i;ascii-casemap':$collation),
+                        'negate-condition' => $child->getAttribute('negate-condition')==='yes',
+                        'value' => $child->nodeValue,
+                    );
+                    break;
+
+            }
+
+        }
+
+        return $filters;
+
+    }
+
+    /**
+     * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
+     *
+     * Specifying a reference timezone is optional. It will only be used
+     * if the non-UTC format is used. The argument is used as a reference, the 
+     * returned DateTime object will still be in the UTC timezone.
+     *
+     * @param string $dt 
+     * @param DateTimeZone $tz 
+     * @return DateTime 
+     */
+    static public function parseICalendarDateTime($dt,DateTimeZone $tz = null) {
+
+        // Format is YYYYMMDD + "T" + hhmmss 
+        $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/',$dt,$matches);
+
+        if (!$result) {
+            throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar datetime value is incorrect: ' . $dt);
+        }
+
+        if ($matches[7]==='Z' || is_null($tz)) {
+            $tz = new DateTimeZone('UTC');
+        } 
+        $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3] . ' ' . $matches[4] . ':' . $matches[5] .':' . $matches[6], $tz);
+
+        // Still resetting the timezone, to normalize everything to UTC
+        $date->setTimeZone(new DateTimeZone('UTC'));
+        return $date;
+
+    }
+
+    /**
+     * Parses an iCalendar (rfc5545) formatted datetime and returns a DateTime object
+     *
+     * @param string $date 
+     * @param DateTimeZone $tz 
+     * @return DateTime 
+     */
+    static public function parseICalendarDate($date) {
+
+        // Format is YYYYMMDD
+        $result = preg_match('/^([1-3][0-9]{3})([0-1][0-9])([0-3][0-9])$/',$date,$matches);
+
+        if (!$result) {
+            throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar date value is incorrect: ' . $date);
+        }
+
+        $date = new DateTime($matches[1] . '-' . $matches[2] . '-' . $matches[3], new DateTimeZone('UTC'));
+        return $date;
+
+    }
+   
+    /**
+     * Parses an iCalendar (RFC5545) formatted duration and returns a string suitable
+     * for strtotime or DateTime::modify.
+     *
+     * NOTE: When we require PHP 5.3 this can be replaced by the DateTimeInterval object, which
+     * supports ISO 8601 Intervals, which is a superset of ICalendar durations.
+     *
+     * For now though, we're just gonna live with this messy system
+     *
+     * @param string $duration
+     * @return string
+     */
+    static public function parseICalendarDuration($duration) {
+
+        $result = preg_match('/^(?P<plusminus>\+|-)?P((?P<week>\d+)W)?((?P<day>\d+)D)?(T((?P<hour>\d+)H)?((?P<minute>\d+)M)?((?P<second>\d+)S)?)?$/', $duration, $matches);
+        if (!$result) {
+            throw new Sabre_DAV_Exception_BadRequest('The supplied iCalendar duration value is incorrect: ' . $duration);
+        }
+       
+        $parts = array(
+            'week',
+            'day',
+            'hour',
+            'minute',
+            'second',
+        );
+
+        $newDur = '';
+        foreach($parts as $part) {
+            if (isset($matches[$part]) && $matches[$part]) {
+                $newDur.=' '.$matches[$part] . ' ' . $part . 's';
+            }
+        }
+
+        $newDur = ($matches['plusminus']==='-'?'-':'+') . trim($newDur);
+        return $newDur;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CardDAV/AddressBook.php b/3rdparty/Sabre/CardDAV/AddressBook.php
new file mode 100644
index 0000000000000000000000000000000000000000..04e4c227b86574104449a77d237893560698da51
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/AddressBook.php
@@ -0,0 +1,295 @@
+<?php
+
+/**
+ * UserAddressBook class
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * The AddressBook class represents a CardDAV addressbook, owned by a specific user
+ *
+ * The AddressBook can contain multiple vcards
+ */
+class Sabre_CardDAV_AddressBook extends Sabre_DAV_Collection implements Sabre_CardDAV_IAddressBook, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
+
+    /**
+     * This is an array with addressbook information 
+     * 
+     * @var array 
+     */
+    private $addressBookInfo;
+
+    /**
+     * CardDAV backend 
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    private $carddavBackend;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend 
+     * @param array $addressBookInfo 
+     * @return void
+     */
+    public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo) {
+
+        $this->carddavBackend = $carddavBackend;
+        $this->addressBookInfo = $addressBookInfo;
+
+    }
+
+    /**
+     * Returns the name of the addressbook 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->addressBookInfo['uri'];
+
+    }
+
+    /**
+     * Returns a card
+     *
+     * @param string $name 
+     * @return Sabre_DAV_Card
+     */
+    public function getChild($name) {
+
+        $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name);
+        if (!$obj) throw new Sabre_DAV_Exception_FileNotFound('Card not found');
+        return new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
+
+    }
+
+    /**
+     * Returns the full list of cards
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        $objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
+        $children = array();
+        foreach($objs as $obj) {
+            $children[] = new Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
+        }
+        return $children;
+
+    }
+
+    /**
+     * Creates a new directory
+     *
+     * We actually block this, as subdirectories are not allowed in addressbooks. 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating collections in addressbooks is not allowed');
+
+    }
+
+    /**
+     * Creates a new file
+     *
+     * The contents of the new file must be a valid VCARD
+     * 
+     * @param string $name 
+     * @param resource $vcardData 
+     * @return void
+     */
+    public function createFile($name,$vcardData = null) {
+
+        $vcardData = stream_get_contents($vcardData);
+
+        $this->carddavBackend->createCard($this->addressBookInfo['id'],$name,$vcardData);
+
+    }
+
+    /**
+     * Deletes the entire addressbook. 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        $this->carddavBackend->deleteAddressBook($this->addressBookInfo['id']);
+
+    }
+
+    /**
+     * Renames the addressbook
+     * 
+     * @param string $newName 
+     * @return void
+     */
+    public function setName($newName) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Renaming addressbooks is not yet supported');
+
+    }
+
+    /**
+     * Returns the last modification date as a unix timestamp.
+     * 
+     * @return void
+     */
+    public function getLastModified() {
+
+        return null;
+
+    }
+
+    /**
+     * Updates properties on this node,
+     *
+     * The properties array uses the propertyName in clark-notation as key,
+     * and the array value for the property value. In the case a property
+     * should be deleted, the property value will be null.
+     *
+     * This method must be atomic. If one property cannot be changed, the
+     * entire operation must fail.
+     *
+     * If the operation was successful, true can be returned.
+     * If the operation failed, false can be returned.
+     *
+     * Deletion of a non-existant property is always succesful.
+     *
+     * Lastly, it is optional to return detailed information about any
+     * failures. In this case an array should be returned with the following
+     * structure:
+     *
+     * array(
+     *   403 => array(
+     *      '{DAV:}displayname' => null,
+     *   ),
+     *   424 => array(
+     *      '{DAV:}owner' => null,
+     *   )
+     * )
+     *
+     * In this example it was forbidden to update {DAV:}displayname. 
+     * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+     * (424 Failed Dependency) because the request needs to be atomic.
+     *
+     * @param array $mutations 
+     * @return bool|array 
+     */
+    public function updateProperties($mutations) {
+
+        return $this->carddavBackend->updateAddressBook($this->addressBookInfo['id'], $mutations); 
+
+    }
+
+    /**
+     * Returns a list of properties for this nodes.
+     *
+     * The properties list is a list of propertynames the client requested,
+     * encoded in clark-notation {xmlnamespace}tagname
+     *
+     * If the array is empty, it means 'all properties' were requested.
+     *
+     * @param array $properties 
+     * @return void
+     */
+    public function getProperties($properties) {
+
+        $response = array();
+        foreach($properties as $propertyName) {
+
+            if (isset($this->addressBookInfo[$propertyName])) {
+
+                $response[$propertyName] = $this->addressBookInfo[$propertyName];
+
+            }
+
+        }
+
+        return $response;
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->addressBookInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+
+}
diff --git a/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
new file mode 100644
index 0000000000000000000000000000000000000000..08adc3b8157ecf72d8bb7508a8e3819384ce9c1b
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/AddressBookQueryParser.php
@@ -0,0 +1,211 @@
+<?php
+
+/**
+ * Parses the addressbook-query report request body.
+ *
+ * Whoever designed this format, and the CalDAV equavalent even more so, 
+ * has no feel for design.
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookQueryParser {
+
+    const TEST_ANYOF = 'anyof';
+    const TEST_ALLOF = 'allof';
+
+    /**
+     * List of requested properties the client wanted
+     * 
+     * @var array 
+     */
+    public $requestedProperties;
+
+    /**
+     * The number of results the client wants
+     *
+     * null means it wasn't specified, which in most cases means 'all results'.
+     * 
+     * @var int|null 
+     */
+    public $limit;
+
+    /**
+     * List of property filters.
+     *
+     * @var array 
+     */
+    public $filters;
+
+    /**
+     * Either TEST_ANYOF or TEST_ALLOF
+     * 
+     * @var string 
+     */
+    public $test;
+
+    /**
+     * DOM Document
+     * 
+     * @var DOMDocument 
+     */
+    protected $dom;
+
+    /**
+     * DOM XPath object 
+     * 
+     * @var DOMXPath 
+     */
+    protected $xpath;
+
+    /**
+     * Creates the parser
+     * 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function __construct(DOMDocument $dom) {
+
+        $this->dom = $dom;
+
+        $this->xpath = new DOMXPath($dom);
+        $this->xpath->registerNameSpace('card',Sabre_CardDAV_Plugin::NS_CARDDAV);
+
+    }
+
+    /**
+     * Parses the request. 
+     * 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function parse() {
+
+        $filterNode = null;
+        
+        $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
+        if (is_nan($limit)) $limit = null;
+
+        $filter = $this->xpath->query('/card:addressbook-query/card:filter');
+        if ($filter->length !== 1) {
+            throw new Sabre_DAV_Exception_BadRequest('Only one filter element is allowed');
+        }
+
+        $filter = $filter->item(0);
+        $test = $this->xpath->evaluate('string(@test)', $filter);
+        if (!$test) $test = self::TEST_ANYOF;
+        if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
+            throw new Sabre_DAV_Exception_BadRequest('The test attribute must either hold "anyof" or "allof"');
+        }
+
+        $propFilters = array();
+
+        $propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
+        for($ii=0; $ii < $propFilterNodes->length; $ii++) {
+
+            $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
+
+
+        }
+
+        $this->filters = $propFilters;
+        $this->limit = $limit;
+        $this->requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($this->dom->firstChild));
+        $this->test = $test;
+
+    }
+
+    /**
+     * Parses the prop-filter xml element
+     * 
+     * @param DOMElement $propFilterNode 
+     * @return array 
+     */
+    protected function parsePropFilterNode(DOMElement $propFilterNode) {
+
+        $propFilter = array();
+        $propFilter['name'] = $propFilterNode->getAttribute('name');
+        $propFilter['test'] = $propFilterNode->getAttribute('test');
+        if (!$propFilter['test']) $propFilter['test'] = 'anyof';
+
+        $propFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $propFilterNode)->length>0;
+
+        $paramFilterNodes = $this->xpath->query('card:param-filter', $propFilterNode);
+
+        $propFilter['param-filters'] = array();
+
+
+        for($ii=0;$ii<$paramFilterNodes->length;$ii++) {
+
+            $propFilter['param-filters'][] = $this->parseParamFilterNode($paramFilterNodes->item($ii));
+
+        }
+        $propFilter['text-matches'] = array();
+        $textMatchNodes = $this->xpath->query('card:text-match', $propFilterNode);
+
+        for($ii=0;$ii<$textMatchNodes->length;$ii++) {
+
+            $propFilter['text-matches'][] = $this->parseTextMatchNode($textMatchNodes->item($ii));
+
+        }
+
+        return $propFilter;
+
+    }
+
+    /**
+     * Parses the param-filter element 
+     * 
+     * @param DOMElement $paramFilterNode 
+     * @return array 
+     */
+    public function parseParamFilterNode(DOMElement $paramFilterNode) {
+        
+        $paramFilter = array();
+        $paramFilter['name'] = $paramFilterNode->getAttribute('name');
+        $paramFilter['is-not-defined'] = $this->xpath->query('card:is-not-defined', $paramFilterNode)->length>0;
+        $paramFilter['text-match'] = null;
+
+        $textMatch = $this->xpath->query('card:text-match', $paramFilterNode);
+        if ($textMatch->length>0) {
+            $paramFilter['text-match'] = $this->parseTextMatchNode($textMatch->item(0));
+        }
+
+        return $paramFilter; 
+
+    }
+
+    /**
+     * Text match
+     * 
+     * @param DOMElement $textMatchNode 
+     * @return void
+     */
+    public function parseTextMatchNode(DOMElement $textMatchNode) {
+
+        $matchType = $textMatchNode->getAttribute('match-type');
+        if (!$matchType) $matchType = 'contains';
+
+        if (!in_array($matchType, array('contains', 'equals', 'starts-with', 'ends-with'))) {
+            throw new Sabre_DAV_Exception_BadRequest('Unknown match-type: ' . $matchType);
+        }
+
+        $negateCondition = $textMatchNode->getAttribute('negate-condition');
+        $negateCondition = $negateCondition==='yes';
+        $collation = $textMatchNode->getAttribute('collation');
+        if (!$collation) $collation = 'i;unicode-casemap';
+
+        return array(
+            'negate-condition' => $negateCondition,
+            'collation' => $collation,
+            'match-type' => $matchType,
+            'value' => $textMatchNode->nodeValue
+        );
+          
+
+    } 
+
+}
diff --git a/3rdparty/Sabre/CardDAV/AddressBookRoot.php b/3rdparty/Sabre/CardDAV/AddressBookRoot.php
new file mode 100644
index 0000000000000000000000000000000000000000..1a80efba35e38e58251f0b13e815843f5e4d8f06
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/AddressBookRoot.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * AddressBook rootnode 
+ *
+ * This object lists a collection of users, which can contain addressbooks.
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_CardDAV_AddressBookRoot extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * Principal Backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend
+     */
+    protected $principalBackend;
+
+    /**
+     * CardDAV backend 
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    protected $carddavBackend;
+
+    /**
+     * Constructor 
+     *
+     * This constructor needs both a principal and a carddav backend.
+     *
+     * By default this class will show a list of addressbook collections for 
+     * principals in the 'principals' collection. If your main principals are 
+     * actually located in a different path, use the $principalPrefix argument 
+     * to override this.
+     *
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend
+     * @param string $principalPrefix 
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend,Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalPrefix = 'principals') {
+
+        $this->carddavBackend = $carddavBackend;
+        parent::__construct($principalBackend, $principalPrefix);
+
+    }
+
+    /**
+     * Returns the name of the node 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return Sabre_CardDAV_Plugin::ADDRESSBOOK_ROOT;
+
+    }
+
+    /**
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
+     * 
+     * @param array $principal 
+     * @return Sabre_DAV_INode 
+     */
+    public function getChildForPrincipal(array $principal) {
+
+        return new Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Backend/Abstract.php b/3rdparty/Sabre/CardDAV/Backend/Abstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..f6d10291ca61dd025f7c49f7c50210db57ab2463
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Backend/Abstract.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Abstract Backend class
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * This class serves as a base-class for addressbook backends
+ *
+ * Note that there are references to 'addressBookId' scattered throughout the 
+ * class. The value of the addressBookId is completely up to you, it can be any 
+ * arbitrary value you can use as an unique identifier.
+ */
+abstract class Sabre_CardDAV_Backend_Abstract {
+
+    /**
+     * Returns the list of addressbooks for a specific user.
+     *
+     * Every addressbook should have the following properties:
+     *   id - an arbitrary unique id
+     *   uri - the 'basename' part of the url
+     *   principaluri - Same as the passed parameter
+     *
+     * Any additional clark-notation property may be passed besides this. Some 
+     * common ones are :
+     *   {DAV:}displayname
+     *   {urn:ietf:params:xml:ns:carddav}addressbook-description
+     *   {http://calendarserver.org/ns/}getctag
+     * 
+     * @param string $principalUri 
+     * @return array 
+     */
+    public abstract function getAddressBooksForUser($principalUri); 
+
+    /**
+     * Updates an addressbook's properties
+     *
+     * See Sabre_DAV_IProperties for a description of the mutations array, as 
+     * well as the return value. 
+     *
+     * @param mixed $addressBookId
+     * @param array $mutations
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array
+     */
+    public abstract function updateAddressBook($addressBookId, array $mutations); 
+
+    /**
+     * Creates a new address book 
+     *
+     * @param string $principalUri 
+     * @param string $url Just the 'basename' of the url. 
+     * @param array $properties 
+     * @return void
+     */
+    abstract public function createAddressBook($principalUri, $url, array $properties); 
+
+    /**
+     * Deletes an entire addressbook and all its contents
+     *
+     * @param mixed $addressBookId 
+     * @return void
+     */
+    abstract public function deleteAddressBook($addressBookId); 
+
+    /**
+     * Returns all cards for a specific addressbook id. 
+     *
+     * This method should return the following properties for each card:
+     *   * carddata - raw vcard data
+     *   * uri - Some unique url
+     *   * lastmodified - A unix timestamp
+
+     * @param mixed $addressbookId 
+     * @return array 
+     */
+    public abstract function getCards($addressbookId); 
+
+    /**
+     * Returns a specfic card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return void
+     */
+    public abstract function getCard($addressBookId, $cardUri); 
+
+    /**
+     * Creates a new card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    abstract public function createCard($addressBookId, $cardUri, $cardData); 
+
+    /**
+     * Updates a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    abstract public function updateCard($addressBookId, $cardUri, $cardData); 
+
+    /**
+     * Deletes a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return bool 
+     */
+    abstract public function deleteCard($addressBookId, $cardUri); 
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Backend/PDO.php b/3rdparty/Sabre/CardDAV/Backend/PDO.php
new file mode 100644
index 0000000000000000000000000000000000000000..63a74745aaca8c18a8f9132f442b4cbc5e339fa0
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Backend/PDO.php
@@ -0,0 +1,277 @@
+<?php
+
+/**
+ * PDO CardDAV backend
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * This CardDAV backend uses PDO to store addressbooks
+ */
+class Sabre_CardDAV_Backend_PDO extends Sabre_CardDAV_Backend_Abstract {
+
+    /**
+     * PDO connection 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * The PDO table name used to store addressbooks
+     */
+    protected $addressBooksTableName;
+
+    /**
+     * The PDO table name used to store cards
+     */
+    protected $cardsTableName;
+
+    /**
+     * Sets up the object 
+     * 
+     * @param PDO $pdo 
+     */
+    public function __construct(PDO $pdo, $addressBooksTableName = 'addressbooks', $cardsTableName = 'cards') {
+
+        $this->pdo = $pdo;
+        $this->addressBooksTableName = $addressBooksTableName;
+        $this->cardsTableName = $cardsTableName; 
+
+    }
+
+    /**
+     * Returns the list of addressbooks for a specific user. 
+     * 
+     * @param string $principalUri 
+     * @return array 
+     */
+    public function getAddressBooksForUser($principalUri) {
+
+        $stmt = $this->pdo->prepare('SELECT id, uri, displayname, principaluri, description, ctag FROM `'.$this->addressBooksTableName.'` WHERE principaluri = ?');
+        $result = $stmt->execute(array($principalUri));
+
+        $addressBooks = array();
+
+        foreach($stmt->fetchAll() as $row) {
+
+            $addressBooks[] = array(
+                'id'  => $row['id'],
+                'uri' => $row['uri'],
+                'principaluri' => $row['principaluri'],
+                '{DAV:}displayname' => $row['displayname'],
+                '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
+                '{http://calendarserver.org/ns/}getctag' => $row['ctag'], 
+            );
+
+        }
+
+        return $addressBooks;
+
+    }
+
+
+    /**
+     * Updates an addressbook's properties
+     *
+     * See Sabre_DAV_IProperties for a description of the mutations array, as 
+     * well as the return value. 
+     *
+     * @param mixed $addressBookId
+     * @param array $mutations
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array
+     */
+    public function updateAddressBook($addressBookId, array $mutations) {
+        
+        $updates = array();
+
+        foreach($mutations as $property=>$newValue) {
+
+            switch($property) {
+                case '{DAV:}displayname' :
+                    $updates['displayname'] = $newValue;
+                    break;
+                case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+                    $updates['description'] = $newValue;
+                    break;
+                default :
+                    // If any unsupported values were being updated, we must 
+                    // let the entire request fail.
+                    return false;
+            }
+
+        }
+
+        // No values are being updated?
+        if (!$updates) {
+            return false;
+        }
+
+        $query = 'UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 ';
+        foreach($updates as $key=>$value) {
+            $query.=', `' . $key . '` = :' . $key . ' ';
+        }
+        $query.=' WHERE id = :addressbookid';
+
+        $stmt = $this->pdo->prepare($query);
+        $updates['addressbookid'] = $addressBookId;
+
+        $stmt->execute($updates);
+
+        return true;
+
+    }
+
+    /**
+     * Creates a new address book 
+     *
+     * @param string $principalUri 
+     * @param string $url Just the 'basename' of the url. 
+     * @param array $properties 
+     * @return void
+     */
+    public function createAddressBook($principalUri, $url, array $properties) {
+
+        $values = array(
+            'displayname' => null,
+            'description' => null,
+            'principaluri' => $principalUri,
+            'uri' => $url,
+        );
+
+        foreach($properties as $property=>$newValue) {
+
+            switch($property) {
+                case '{DAV:}displayname' :
+                    $values['displayname'] = $newValue;
+                    break;
+                case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+                    $values['description'] = $newValue;
+                    break;
+                default :
+                    throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
+            }
+
+        }
+
+        $query = 'INSERT INTO `' . $this->addressBooksTableName . '` (uri, displayname, description, principaluri, ctag) VALUES (:uri, :displayname, :description, :principaluri, 1)';
+        $stmt = $this->pdo->prepare($query);
+        $stmt->execute($values);
+
+    }
+
+    /**
+     * Deletes an entire addressbook and all its contents
+     *
+     * @param int $addressBookId 
+     * @return void
+     */
+    public function deleteAddressBook($addressBookId) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `' . $this->cardsTableName . '` WHERE addressbookid = ?');
+        $stmt->execute(array($addressBookId));
+
+        $stmt = $this->pdo->prepare('DELETE FROM `' . $this->addressBooksTableName . '` WHERE id = ?');
+        $stmt->execute(array($addressBookId));
+
+    }
+
+    /**
+     * Returns all cards for a specific addressbook id. 
+     * 
+     * @param mixed $addressbookId 
+     * @return array 
+     */
+    public function getCards($addressbookId) {
+
+        $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM `' . $this->cardsTableName . '` WHERE addressbookid = ?');
+        $stmt->execute(array($addressbookId));
+
+        return $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+    
+    }
+    /**
+     * Returns a specfic card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return array 
+     */
+    public function getCard($addressBookId, $cardUri) {
+
+        $stmt = $this->pdo->prepare('SELECT id, carddata, uri, lastmodified FROM `' . $this->cardsTableName . '` WHERE addressbookid = ? AND uri = ? LIMIT 1');
+        $stmt->execute(array($addressBookId, $cardUri));
+
+        $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+        return (count($result)>0?$result[0]:false);
+
+    }
+
+    /**
+     * Creates a new card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    public function createCard($addressBookId, $cardUri, $cardData) {
+
+        $stmt = $this->pdo->prepare('INSERT INTO `' . $this->cardsTableName . '` (carddata, uri, lastmodified, addressbookid) VALUES (?, ?, ?, ?)');
+
+        $result = $stmt->execute(array($cardData, $cardUri, time(), $addressBookId));
+
+        $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt2->execute(array($addressBookId));
+
+        return $result;
+
+    }
+
+    /**
+     * Updates a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @param string $cardData 
+     * @return bool 
+     */
+    public function updateCard($addressBookId, $cardUri, $cardData) {
+
+        $stmt = $this->pdo->prepare('UPDATE `' . $this->cardsTableName . '` SET carddata = ?, lastmodified = ? WHERE uri = ? AND addressbookid =?');
+        $result = $stmt->execute(array($cardData, time(), $cardUri, $addressBookId));
+
+        $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt2->execute(array($addressBookId));
+
+        return $stmt->rowCount()===1;
+
+    }
+
+    /**
+     * Deletes a card
+     * 
+     * @param mixed $addressBookId 
+     * @param string $cardUri 
+     * @return bool 
+     */
+    public function deleteCard($addressBookId, $cardUri) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `' . $this->cardsTableName . '` WHERE addressbookid = ? AND uri = ?');
+        $stmt->execute(array($addressBookId, $cardUri));
+
+        $stmt2 = $this->pdo->prepare('UPDATE `' . $this->addressBooksTableName . '` SET ctag = ctag + 1 WHERE id = ?');
+        $stmt2->execute(array($addressBookId));
+
+        return $stmt->rowCount()===1;
+
+    }
+}
diff --git a/3rdparty/Sabre/CardDAV/Card.php b/3rdparty/Sabre/CardDAV/Card.php
new file mode 100644
index 0000000000000000000000000000000000000000..52d8b79d7ddbc27914c432e79bbbf42089ce4f14
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Card.php
@@ -0,0 +1,221 @@
+<?php
+
+/**
+ * Card class
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ *
+ /
+/**
+ * The Card object represents a single Card from an addressbook
+ */ 
+class Sabre_CardDAV_Card extends Sabre_DAV_File implements Sabre_CardDAV_ICard, Sabre_DAVACL_IACL {
+
+    /**
+     * CardDAV backend
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    private $carddavBackend;
+
+    /**
+     * Array with information about this Card
+     * 
+     * @var array 
+     */
+    private $cardData;
+
+    /**
+     * Array with information about the containing addressbook 
+     * 
+     * @var array 
+     */
+    private $addressBookInfo;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend 
+     * @param array $addressBookInfo
+     * @param array $cardData
+     */
+    public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend,array $addressBookInfo,array $cardData) {
+
+        $this->carddavBackend = $carddavBackend;
+        $this->addressBookInfo = $addressBookInfo;
+        $this->cardData = $cardData;
+
+    }
+
+    /**
+     * Returns the uri for this object 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->cardData['uri'];
+
+    }
+
+    /**
+     * Returns the VCard-formatted object 
+     * 
+     * @return string 
+     */
+    public function get() {
+
+        $cardData = $this->cardData['carddata'];
+        $s = fopen('php://temp','r+');
+        fwrite($s, $cardData);
+        rewind($s);
+        return $s;
+
+    }
+
+    /**
+     * Updates the VCard-formatted object 
+     * 
+     * @param string $cardData 
+     * @return void 
+     */
+    public function put($cardData) {
+
+        if (is_resource($cardData))
+            $cardData = stream_get_contents($cardData);
+
+        $this->carddavBackend->updateCard($this->addressBookInfo['id'],$this->cardData['uri'],$cardData);
+        $this->cardData['carddata'] = $cardData;
+
+    }
+
+    /**
+     * Deletes the card
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        $this->carddavBackend->deleteCard($this->addressBookInfo['id'],$this->cardData['uri']);
+
+    }
+
+    /**
+     * Returns the mime content-type 
+     * 
+     * @return string 
+     */
+    public function getContentType() {
+
+        return 'text/x-vcard';
+
+    }
+
+    /**
+     * Returns an ETag for this object 
+     * 
+     * @return string 
+     */
+    public function getETag() {
+
+        return '"' . md5($this->cardData['carddata']) . '"';
+
+    }
+
+    /**
+     * Returns the last modification date as a unix timestamp
+     * 
+     * @return time 
+     */
+    public function getLastModified() {
+
+        return isset($this->cardData['lastmodified'])?$this->cardData['lastmodified']:null;
+
+    }
+
+    /**
+     * Returns the size of this object in bytes 
+     * 
+     * @return int
+     */
+    public function getSize() {
+
+        return strlen($this->cardData['carddata']);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->addressBookInfo['principaluri'];
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->addressBookInfo['principaluri'],
+                'protected' => true,
+            ),
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/CardDAV/IAddressBook.php b/3rdparty/Sabre/CardDAV/IAddressBook.php
new file mode 100644
index 0000000000000000000000000000000000000000..a0dffb30aea5c11cda8c04731c1068cc3c776089
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/IAddressBook.php
@@ -0,0 +1,18 @@
+<?php
+
+/**
+ * AddressBook interface
+ *
+ * Implement this interface to allow a node to be recognized as an addressbook.
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_IAddressBook extends Sabre_DAV_ICollection {
+
+ 
+
+}
diff --git a/3rdparty/Sabre/CardDAV/ICard.php b/3rdparty/Sabre/CardDAV/ICard.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f9bb097b5685e6ef99363850232569c4b5c69a0
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/ICard.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * Card interface 
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ *
+ /
+/**
+ * Extend the ICard interface to allow your custom nodes to be picked up as 
+ * 'Cards'. 
+ */ 
+interface Sabre_CardDAV_ICard extends Sabre_DAV_IFile { 
+
+}
+
diff --git a/3rdparty/Sabre/CardDAV/IDirectory.php b/3rdparty/Sabre/CardDAV/IDirectory.php
new file mode 100644
index 0000000000000000000000000000000000000000..e0d0797d285bb8e1dae530513c51d6d47e78856e
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/IDirectory.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * IDirectory interface
+ *
+ * Implement this interface to have an addressbook marked as a 'directory'. A 
+ * directory is an (often) global addressbook.
+ *
+ * A full description can be found in the IETF draft:
+ *   - draft-daboo-carddav-directory-gateway
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_CardDAV_IDirectory extends Sabre_CardDAV_IAddressBook {
+
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Plugin.php b/3rdparty/Sabre/CardDAV/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..a96f9aaebb6872be472ab4f4861f1cc611d8213f
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Plugin.php
@@ -0,0 +1,465 @@
+<?php
+
+/**
+ * CardDAV plugin 
+ *
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+
+/**
+ * The CardDAV plugin adds CardDAV functionality to the WebDAV server
+ */
+class Sabre_CardDAV_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Url to the addressbooks
+     */
+    const ADDRESSBOOK_ROOT = 'addressbooks';
+
+    /**
+     * xml namespace for CardDAV elements
+     */
+    const NS_CARDDAV = 'urn:ietf:params:xml:ns:carddav';
+
+    /**
+     * Add urls to this property to have them automatically exposed as 
+     * 'directories' to the user.
+     * 
+     * @var array
+     */
+    public $directories = array();
+
+    /**
+     * Server class 
+     *
+     * @var Sabre_DAV_Server 
+     */
+    protected $server;
+
+    /**
+     * Initializes the plugin 
+     *
+     * @param Sabre_DAV_Server $server 
+     * @return void 
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        /* Events */
+        $server->subscribeEvent('beforeGetProperties', array($this, 'beforeGetProperties'));
+        $server->subscribeEvent('report', array($this,'report'));
+
+        /* Namespaces */
+        $server->xmlNamespaces[self::NS_CARDDAV] = 'card';
+
+        /* Mapping Interfaces to {DAV:}resourcetype values */
+        $server->resourceTypeMapping['Sabre_CardDAV_IAddressBook'] = '{' . self::NS_CARDDAV . '}addressbook';
+        $server->resourceTypeMapping['Sabre_CardDAV_IDirectory'] = '{' . self::NS_CARDDAV . '}directory';
+        
+        /* Adding properties that may never be changed */
+        $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}supported-address-data';
+        $server->protectedProperties[] = '{' . self::NS_CARDDAV . '}max-resource-size';
+
+
+        $this->server = $server;
+
+    }
+
+    /**
+     * Returns a list of supported features.
+     *
+     * This is used in the DAV: header in the OPTIONS and PROPFIND requests. 
+     *
+     * @return array
+     */
+    public function getFeatures() {
+
+        return array('addressbook');
+
+    }
+
+    /**
+     * Returns a list of reports this plugin supports.
+     *
+     * This will be used in the {DAV:}supported-report-set property.
+     * Note that you still need to subscribe to the 'report' event to actually 
+     * implement them 
+     *
+     * @param string $uri
+     * @return array 
+     */
+    public function getSupportedReportSet($uri) {
+
+        $node = $this->server->tree->getNodeForPath($uri);
+        if ($node instanceof Sabre_CardDAV_AddressBook || $node instanceof Sabre_CardDAV_ICard) {
+            return array(
+                 '{' . self::NS_CARDDAV . '}addressbook-multiget',
+            );
+        }
+        return array();
+
+    }
+
+
+    /**
+     * Adds all CardDAV-specific properties 
+     *
+     * @param string $path
+     * @param Sabre_DAV_INode $node 
+     * @param array $requestedProperties
+     * @param array $returnedProperties 
+     * @return void
+     */
+    public function beforeGetProperties($path, Sabre_DAV_INode $node, array &$requestedProperties, array &$returnedProperties) { 
+
+        if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+            // calendar-home-set property
+            $addHome = '{' . self::NS_CARDDAV . '}addressbook-home-set';
+            if (in_array($addHome,$requestedProperties)) {
+                $principalId = $node->getName(); 
+                $addressbookHomePath = self::ADDRESSBOOK_ROOT . '/' . $principalId . '/';
+                unset($requestedProperties[array_search($addHome, $requestedProperties)]);
+                $returnedProperties[200][$addHome] = new Sabre_DAV_Property_Href($addressbookHomePath);
+            }
+
+            $directories = '{' . self::NS_CARDDAV . '}directory-gateway';
+            if ($this->directories && in_array($directories, $requestedProperties)) {
+                unset($requestedProperties[array_search($directories, $requestedProperties)]);
+                $returnedProperties[200][$directories] = new Sabre_DAV_Property_HrefList($this->directories);
+            }
+
+        }
+
+        if ($node instanceof Sabre_CardDAV_ICard) {
+
+            // The address-data property is not supposed to be a 'real' 
+            // property, but in large chunks of the spec it does act as such. 
+            // Therefore we simply expose it as a property.
+            $addressDataProp = '{' . self::NS_CARDDAV . '}address-data';
+            if (in_array($addressDataProp, $requestedProperties)) {
+                unset($requestedProperties[$addressDataProp]);
+                $val = $node->get();
+                if (is_resource($val))
+                    $val = stream_get_contents($val);
+
+                // Taking out \r to not screw up the xml output
+                $returnedProperties[200][$addressDataProp] = str_replace("\r","", $val);
+
+            }
+        }
+
+    }
+
+    /**
+     * This functions handles REPORT requests specific to CardDAV 
+     *
+     * @param string $reportName 
+     * @param DOMNode $dom
+     * @return bool 
+     */
+    public function report($reportName,$dom) {
+
+        switch($reportName) { 
+            case '{'.self::NS_CARDDAV.'}addressbook-multiget' :
+                $this->addressbookMultiGetReport($dom);
+                return false;
+            case '{'.self::NS_CARDDAV.'}addressbook-query' :
+                $this->addressBookQueryReport($dom);
+                return false; 
+            default :
+                return;
+
+        }
+
+
+    }
+
+    /**
+     * This function handles the addressbook-multiget REPORT.
+     *
+     * This report is used by the client to fetch the content of a series
+     * of urls. Effectively avoiding a lot of redundant requests.
+     *
+     * @param DOMNode $dom
+     * @return void
+     */
+    public function addressbookMultiGetReport($dom) {
+
+        $properties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild));
+
+        $hrefElems = $dom->getElementsByTagNameNS('urn:DAV','href');
+        $propertyList = array();
+
+        foreach($hrefElems as $elem) {
+
+            $uri = $this->server->calculateUri($elem->nodeValue);
+            list($propertyList[]) = $this->server->getPropertiesForPath($uri,$properties);
+
+        }
+
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList));
+
+    }
+
+    /**
+     * This function handles the addressbook-query REPORT
+     *
+     * This report is used by the client to filter an addressbook based on a
+     * complex query.
+     *
+     * @param DOMNode $dom
+     * @return void
+     */
+    protected function addressbookQueryReport($dom) {
+
+        $query = new Sabre_CardDAV_AddressBookQueryParser($dom);
+        $query->parse();
+
+        $depth = $this->server->getHTTPDepth(0);
+
+        if ($depth==0) {
+            $candidateNodes = array(
+                $this->server->tree->getNodeForPath($this->server->getRequestUri())
+            );
+        } else {
+            $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri());
+        }
+
+        $validNodes = array();
+        foreach($candidateNodes as $node) {
+
+            if (!$node instanceof Sabre_CardDAV_ICard)
+                continue;
+
+            $blob = $node->get();
+            if (is_resource($blob)) {
+                $blob = stream_get_contents($blob);
+            }
+
+            if (!$this->validateFilters($blob, $query->filters, $query->test)) {
+                continue;
+            }
+
+            $validNodes[] = $node;
+
+            if ($query->limit && $query->limit <= count($validNodes)) {
+                // We hit the maximum number of items, we can stop now.
+                break;
+            }
+
+        }
+
+        $result = array();
+        foreach($validNodes as $validNode) {
+            if ($depth==0) { 
+                $href = $this->server->getRequestUri();
+            } else {
+                $href = $this->server->getRequestUri() . '/' . $validNode->getName();
+            }
+
+            list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0);
+
+        }
+ 
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result));
+
+    }
+
+    /**
+     * Validates if a vcard makes it throught a list of filters.
+     * 
+     * @param string $vcardData 
+     * @param array $filters 
+     * @param string $test anyof or allof (which means OR or AND) 
+     * @return bool 
+     */
+    public function validateFilters($vcardData, array $filters, $test) {
+
+        $vcard = Sabre_VObject_Reader::read($vcardData);
+        
+        $success = true;
+
+        foreach($filters as $filter) {
+
+            $isDefined = isset($vcard->{$filter['name']});
+            if ($filter['is-not-defined']) {
+                if ($isDefined) {
+                    $success = false;
+                } else {
+                    $success = true;
+                }
+            } elseif ((!$filter['param-filters'] && !$filter['text-matches']) || !$isDefined) {
+
+                // We only need to check for existence
+                $success = $isDefined;
+                
+            } else {
+
+                $vProperties = $vcard->select($filter['name']); 
+
+                $results = array();
+                if ($filter['param-filters']) {
+                    $results[] = $this->validateParamFilters($vProperties, $filter['param-filters'], $filter['test']);
+                }
+                if ($filter['text-matches']) {
+                    $texts = array();
+                    foreach($vProperties as $vProperty)
+                        $texts[] = $vProperty->value;
+
+                    $results[] = $this->validateTextMatches($texts, $filter['text-matches'], $filter['test']);
+                }
+
+                if (count($results)===1) {
+                    $success = $results[0];
+                } else {
+                    if ($filter['test'] === 'anyof') {
+                        $success = $results[0] || $results[1];
+                    } else {
+                        $success = $results[0] && $results[1];
+                    }
+                }
+
+            } // else
+
+            // There are two conditions where we can already determine wether 
+            // or not this filter succeeds.
+            if ($test==='anyof' && $success) {
+                return true;
+            }
+            if ($test==='allof' && !$success) {
+                return false;
+            }
+
+        } // foreach
+
+        // If we got all the way here, it means we haven't been able to 
+        // determine early if the test failed or not.
+        //
+        // This implies for 'anyof' that the test failed, and for 'allof' that 
+        // we succeeded. Sounds weird, but makes sense.
+        return $test==='allof';
+
+    }
+
+    /**
+     * Validates if a param-filter can be applied to a specific property. 
+     * 
+     * @todo currently we're only validating the first parameter of the passed 
+     *       property. Any subsequence parameters with the same name are
+     *       ignored.
+     * @param Sabre_VObject_Property $vProperty 
+     * @param array $filters 
+     * @param string $test 
+     * @return bool 
+     */
+    protected function validateParamFilters(array $vProperties, array $filters, $test) {
+
+        $success = false;
+        foreach($filters as $filter) {
+
+            $isDefined = false;
+            foreach($vProperties as $vProperty) {
+                $isDefined = isset($vProperty[$filter['name']]);
+                if ($isDefined) break;
+            }
+
+            if ($filter['is-not-defined']) {
+                if ($isDefined) {
+                    $success = false;
+                } else {
+                    $success = true;
+                }
+
+            // If there's no text-match, we can just check for existence 
+            } elseif (!$filter['text-match'] || !$isDefined) {
+
+                $success = $isDefined;
+                
+            } else {
+
+                $texts = array();
+                $success = false;
+                foreach($vProperties as $vProperty) {
+                    // If we got all the way here, we'll need to validate the 
+                    // text-match filter.
+                    $success = Sabre_DAV_StringUtil::textMatch($vProperty[$filter['name']]->value, $filter['text-match']['value'], $filter['text-match']['collation'], $filter['text-match']['match-type']);
+                    if ($success) break;
+                }
+                if ($filter['text-match']['negate-condition']) {
+                    $success = !$success;
+                }
+
+            } // else
+
+            // There are two conditions where we can already determine wether 
+            // or not this filter succeeds.
+            if ($test==='anyof' && $success) {
+                return true;
+            }
+            if ($test==='allof' && !$success) {
+                return false;
+            }
+
+        } 
+
+        // If we got all the way here, it means we haven't been able to 
+        // determine early if the test failed or not.
+        //
+        // This implies for 'anyof' that the test failed, and for 'allof' that 
+        // we succeeded. Sounds weird, but makes sense.
+        return $test==='allof';
+
+    }
+
+    /**
+     * Validates if a text-filter can be applied to a specific property. 
+     * 
+     * @param array $texts
+     * @param array $filters 
+     * @param string $test 
+     * @return bool 
+     */
+    protected function validateTextMatches(array $texts, array $filters, $test) {
+
+        foreach($filters as $filter) {
+
+            $success = false;
+            foreach($texts as $haystack) {
+                $success = Sabre_DAV_StringUtil::textMatch($haystack, $filter['value'], $filter['collation'], $filter['match-type']);
+
+                // Breaking on the first match
+                if ($success) break;
+            }
+            if ($filter['negate-condition']) {
+                $success = !$success;
+            }
+            
+            if ($success && $test==='anyof')
+                return true;
+
+            if (!$success && $test=='allof')
+                return false;
+
+
+        }
+
+        // If we got all the way here, it means we haven't been able to 
+        // determine early if the test failed or not.
+        //
+        // This implies for 'anyof' that the test failed, and for 'allof' that 
+        // we succeeded. Sounds weird, but makes sense.
+        return $test==='allof';
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/CardDAV/UserAddressBooks.php b/3rdparty/Sabre/CardDAV/UserAddressBooks.php
new file mode 100644
index 0000000000000000000000000000000000000000..564ecd701f0ce9b461a95d705664e538e77bed95
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/UserAddressBooks.php
@@ -0,0 +1,242 @@
+<?php
+
+/**
+ * UserAddressBooks class
+ * 
+ * @package Sabre
+ * @subpackage CardDAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * The UserAddressBooks collection contains a list of addressbooks associated with a user
+ */
+class Sabre_CardDAV_UserAddressBooks extends Sabre_DAV_Collection implements Sabre_DAV_IExtendedCollection, Sabre_DAVACL_IACL {
+
+    /**
+     * Principal uri
+     * 
+     * @var array 
+     */
+    protected $principalUri;
+
+    /**
+     * carddavBackend 
+     * 
+     * @var Sabre_CardDAV_Backend_Abstract 
+     */
+    protected $carddavBackend;
+
+    /**
+     * Constructor 
+     * 
+     * @param Sabre_CardDAV_Backend_Abstract $carddavBackend 
+     * @param string $principalUri 
+     */
+    public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, $principalUri) {
+
+        $this->carddavBackend = $carddavBackend;
+        $this->principalUri = $principalUri;
+       
+    }
+
+    /**
+     * Returns the name of this object 
+     * 
+     * @return string
+     */
+    public function getName() {
+      
+        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalUri);
+        return $name; 
+
+    }
+
+    /**
+     * Updates the name of this object 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed();
+
+    }
+
+    /**
+     * Deletes this object 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed();
+
+    }
+
+    /**
+     * Returns the last modification date 
+     * 
+     * @return int 
+     */
+    public function getLastModified() {
+
+        return null; 
+
+    }
+
+    /**
+     * Creates a new file under this object.
+     *
+     * This is currently not allowed
+     * 
+     * @param string $filename 
+     * @param resource $data 
+     * @return void
+     */
+    public function createFile($filename, $data=null) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new files in this collection is not supported');
+
+    }
+
+    /**
+     * Creates a new directory under this object.
+     *
+     * This is currently not allowed.
+     * 
+     * @param string $filename 
+     * @return void
+     */
+    public function createDirectory($filename) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Creating new collections in this collection is not supported');
+
+    }
+
+    /**
+     * Returns a single calendar, by name 
+     * 
+     * @param string $name
+     * @todo needs optimizing
+     * @return Sabre_CardDAV_AddressBook
+     */
+    public function getChild($name) {
+
+        foreach($this->getChildren() as $child) {
+            if ($name==$child->getName())
+                return $child;
+
+        }
+        throw new Sabre_DAV_Exception_FileNotFound('Addressbook with name \'' . $name . '\' could not be found');
+
+    }
+
+    /**
+     * Returns a list of addressbooks 
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        $addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri);
+        $objs = array();
+        foreach($addressbooks as $addressbook) {
+            $objs[] = new Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook);
+        }
+        return $objs;
+
+    }
+
+    /**
+     * Creates a new addressbook 
+     * 
+     * @param string $name
+     * @param array $resourceType 
+     * @param array $properties 
+     * @return void
+     */
+    public function createExtendedCollection($name, array $resourceType, array $properties) {
+
+        if (!in_array('{'.Sabre_CardDAV_Plugin::NS_CARDDAV.'}addressbook',$resourceType) || count($resourceType)!==2) {
+            throw new Sabre_DAV_Exception_InvalidResourceType('Unknown resourceType for this collection');
+        }
+        $this->carddavBackend->createAddressBook($this->principalUri, $name, $properties);
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->principalUri;
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalUri,
+                'protected' => true,
+            ),
+            array(
+                'privilege' => '{DAV:}write',
+                'principal' => $this->principalUri,
+                'protected' => true,
+            ),
+
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Changing ACL is not yet supported');
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/CardDAV/Version.php b/3rdparty/Sabre/CardDAV/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..8961027fc89a0b0d95e64e8720243ae59a275190
--- /dev/null
+++ b/3rdparty/Sabre/CardDAV/Version.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Version Class
+ * 
+ * @package Sabre
+ * @subpackage CardDAV 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * This class contains the Sabre_CardDAV version information
+ */
+class Sabre_CardDAV_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '0.2';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'alpha';
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php
new file mode 100644
index 0000000000000000000000000000000000000000..11bab8c7af78b024f568a753af54af8c0be93444
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractBasic.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * HTTP Basic authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Basic
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the validateUserPass method.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author James David Low (http://jameslow.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Auth_Backend_AbstractBasic implements Sabre_DAV_Auth_IBackend {
+
+    /**
+     * This variable holds the currently logged in username.
+     *
+     * @var string|null
+     */
+    protected $currentUser;
+
+    /**
+     * Validates a username and password
+     *
+     * This method should return true or false depending on if login
+     * succeeded.
+     *
+     * @return bool
+     */
+    abstract protected function validateUserPass($username, $password);
+
+    /**
+     * Returns information about the currently logged in username.
+     *
+     * If nobody is currently logged in, this method should return null.
+     *
+     * @return string|null
+     */
+    public function getCurrentUser() {
+        return $this->currentUser;
+    }
+
+
+    /**
+     * Authenticates the user based on the current request.
+     *
+     * If authentication is succesful, true must be returned.
+     * If authentication fails, an exception must be thrown.
+     *
+     * @throws Sabre_DAV_Exception_NotAuthenticated
+     * @return bool
+     */
+    public function authenticate(Sabre_DAV_Server $server,$realm) {
+
+        $auth = new Sabre_HTTP_BasicAuth();
+        $auth->setHTTPRequest($server->httpRequest);
+        $auth->setHTTPResponse($server->httpResponse);
+        $auth->setRealm($realm);
+        $userpass = $auth->getUserPass();
+        if (!$userpass) {
+            $auth->requireLogin();
+            throw new Sabre_DAV_Exception_NotAuthenticated('No basic authentication headers were found');
+        }
+
+        // Authenticates the user
+        if (!$this->validateUserPass($userpass[0],$userpass[1])) {
+            $auth->requireLogin();
+            throw new Sabre_DAV_Exception_NotAuthenticated('Username or password does not match');
+        }
+        $this->currentUser = $userpass[0];
+        return true;
+    }
+
+
+} 
+
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php
new file mode 100644
index 0000000000000000000000000000000000000000..5bdc72753ece623b8bb5c9634c42d7494ba7ccf5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/AbstractDigest.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * HTTP Digest authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Digest
+ * Most of the digest logic is handled, implementors just need to worry about 
+ * the getDigestHash method 
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Auth_Backend_AbstractDigest implements Sabre_DAV_Auth_IBackend {
+
+    /**
+     * This variable holds the currently logged in username.
+     * 
+     * @var array|null
+     */
+    protected $currentUser;
+
+    /**
+     * Returns a users digest hash based on the username and realm.
+     *
+     * If the user was not known, null must be returned. 
+     * 
+     * @param string $realm
+     * @param string $username 
+     * @return string|null 
+     */
+    abstract public function getDigestHash($realm,$username);
+
+    /**
+     * Authenticates the user based on the current request.
+     *
+     * If authentication is succesful, true must be returned.
+     * If authentication fails, an exception must be thrown.
+     *
+     * @throws Sabre_DAV_Exception_NotAuthenticated
+     * @return bool 
+     */
+    public function authenticate(Sabre_DAV_Server $server,$realm) {
+
+        $digest = new Sabre_HTTP_DigestAuth();
+
+        // Hooking up request and response objects
+        $digest->setHTTPRequest($server->httpRequest);
+        $digest->setHTTPResponse($server->httpResponse);
+
+        $digest->setRealm($realm);
+        $digest->init();
+
+        $username = $digest->getUsername();
+
+        // No username was given
+        if (!$username) {
+            $digest->requireLogin();
+            throw new Sabre_DAV_Exception_NotAuthenticated('No digest authentication headers were found');
+        }
+
+        $hash = $this->getDigestHash($realm, $username);
+        // If this was false, the user account didn't exist
+        if ($hash===false || is_null($hash)) {
+            $digest->requireLogin();
+            throw new Sabre_DAV_Exception_NotAuthenticated('The supplied username was not on file');
+        }
+        if (!is_string($hash)) {
+            throw new Sabre_DAV_Exception('The returned value from getDigestHash must be a string or null');
+        }
+
+        // If this was false, the password or part of the hash was incorrect.
+        if (!$digest->validateA1($hash)) {
+            $digest->requireLogin();
+            throw new Sabre_DAV_Exception_NotAuthenticated('Incorrect username');
+        }
+
+        $this->currentUser = $username;
+        return true;
+
+    }
+
+    /**
+     * Returns the currently logged in username. 
+     * 
+     * @return string|null 
+     */
+    public function getCurrentUser() {
+
+        return $this->currentUser;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/Apache.php b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php
new file mode 100644
index 0000000000000000000000000000000000000000..6bcd76bdcb0ca5c81f2a7d9ad167c03ed1a92054
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/Apache.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * Apache authenticator
+ *
+ * This authentication backend assumes that authentication has been
+ * conifgured in apache, rather than within SabreDAV.
+ *
+ * Make sure apache is properly configured for this to work.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Backend_Apache implements Sabre_DAV_Auth_IBackend {
+
+    /**
+     * Current apache user 
+     * 
+     * @var string 
+     */
+    protected $remoteUser;
+    
+    /**
+     * Authenticates the user based on the current request.
+     *
+     * If authentication is succesful, true must be returned.
+     * If authentication fails, an exception must be thrown.
+     *
+     * @return bool 
+     */
+    public function authenticate(Sabre_DAV_Server $server,$realm) {
+
+        $remoteUser = $server->httpRequest->getRawServerValue('REMOTE_USER');
+        if (is_null($remoteUser)) {
+            throw new Sabre_DAV_Exception('We did not receive the $_SERVER[REMOTE_USER] property. This means that apache might have been misconfigured');
+        }
+
+        $this->remoteUser = $remoteUser;
+        return true;
+
+    }
+
+    /**
+     * Returns information about the currently logged in user.
+     *
+     * If nobody is currently logged in, this method should return null.
+     * 
+     * @return array|null
+     */
+    public function getCurrentUser() {
+
+        return $this->remoteUser;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/File.php b/3rdparty/Sabre/DAV/Auth/Backend/File.php
new file mode 100644
index 0000000000000000000000000000000000000000..db1f04c477250bec0aab04cb7adbac38af7939e5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/File.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * This is an authentication backend that uses a file to manage passwords.
+ *
+ * The backend file must conform to Apache's htdigest format
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Backend_File extends Sabre_DAV_Auth_Backend_AbstractDigest {
+
+    /**
+     * List of users 
+     * 
+     * @var array
+     */
+    protected $users = array();
+
+    /**
+     * Creates the backend object. 
+     *
+     * If the filename argument is passed in, it will parse out the specified file fist.
+     * 
+     * @param string $filename 
+     * @return void
+     */
+    public function __construct($filename=null) {
+
+        if (!is_null($filename))
+            $this->loadFile($filename);
+
+    }
+
+    /**
+     * Loads an htdigest-formatted file. This method can be called multiple times if
+     * more than 1 file is used.
+     * 
+     * @param string $filename 
+     * @return void
+     */
+    public function loadFile($filename) {
+
+        foreach(file($filename,FILE_IGNORE_NEW_LINES) as $line) {
+
+            if (substr_count($line, ":") !== 2) 
+                throw new Sabre_DAV_Exception('Malformed htdigest file. Every line should contain 2 colons');
+            
+            list($username,$realm,$A1) = explode(':',$line);
+
+            if (!preg_match('/^[a-zA-Z0-9]{32}$/', $A1))
+                throw new Sabre_DAV_Exception('Malformed htdigest file. Invalid md5 hash');
+                
+            $this->users[$realm . ':' . $username] = $A1;
+
+        }
+
+    }
+
+    /**
+     * Returns a users' information
+     * 
+     * @param string $realm 
+     * @param string $username 
+     * @return string 
+     */
+    public function getDigestHash($realm, $username) {
+
+        return isset($this->users[$realm . ':' . $username])?$this->users[$realm . ':' . $username]:false;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/Backend/PDO.php b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php
new file mode 100644
index 0000000000000000000000000000000000000000..0301503601e2f853c6d40d3c9252b0f08bc6ab35
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Backend/PDO.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * This is an authentication backend that uses a file to manage passwords.
+ *
+ * The backend file must conform to Apache's htdigest format
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Backend_PDO extends Sabre_DAV_Auth_Backend_AbstractDigest {
+
+    /**
+     * Reference to PDO connection 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * PDO table name we'll be using  
+     * 
+     * @var string
+     */
+    protected $tableName;
+
+
+    /**
+     * Creates the backend object. 
+     *
+     * If the filename argument is passed in, it will parse out the specified file fist.
+     * 
+     * @param string $filename
+     * @param string $tableName The PDO table name to use 
+     * @return void
+     */
+    public function __construct(PDO $pdo, $tableName = 'users') {
+
+        $this->pdo = $pdo;
+        $this->tableName = $tableName;
+
+    }
+
+    /**
+     * Returns the digest hash for a user. 
+     * 
+     * @param string $realm 
+     * @param string $username 
+     * @return string|null 
+     */
+    public function getDigestHash($realm,$username) {
+
+        $stmt = $this->pdo->prepare('SELECT username, digesta1 FROM `'.$this->tableName.'` WHERE username = ?');
+        $stmt->execute(array($username));
+        $result = $stmt->fetchAll();
+
+        if (!count($result)) return;
+
+        return $result[0]['digesta1'];
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Auth/IBackend.php b/3rdparty/Sabre/DAV/Auth/IBackend.php
new file mode 100644
index 0000000000000000000000000000000000000000..1f67af4c2d90eabc8eacfb862b15c17592a28595
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/IBackend.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * This is the base class for any authentication object.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_Auth_IBackend {
+
+    /**
+     * Authenticates the user based on the current request.
+     *
+     * If authentication is succesful, true must be returned.
+     * If authentication fails, an exception must be thrown.
+     *
+     * @return bool 
+     */
+    function authenticate(Sabre_DAV_Server $server,$realm); 
+
+    /**
+     * Returns information about the currently logged in username.
+     *
+     * If nobody is currently logged in, this method should return null.
+     * 
+     * @return string|null
+     */
+    function getCurrentUser();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Auth/Plugin.php b/3rdparty/Sabre/DAV/Auth/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..f3718fcf469d53c7039523c98de6f697fd3512bf
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Auth/Plugin.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * This plugin provides Authentication for a WebDAV server.
+ * 
+ * It relies on a Backend object, which provides user information.
+ *
+ * Additionally, it provides support for:
+ *  * {DAV:}current-user-principal property from RFC5397
+ *  * {DAV:}principal-collection-set property from RFC3744
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Auth_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Reference to main server object 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * Authentication backend
+     * 
+     * @var Sabre_DAV_Auth_Backend_Abstract 
+     */
+    private $authBackend;
+
+    /**
+     * The authentication realm. 
+     * 
+     * @var string 
+     */
+    private $realm;
+
+    /**
+     * __construct 
+     * 
+     * @param Sabre_DAV_Auth_Backend_Abstract $authBackend 
+     * @param string $realm 
+     * @return void
+     */
+    public function __construct(Sabre_DAV_Auth_IBackend $authBackend, $realm) {
+
+        $this->authBackend = $authBackend;
+        $this->realm = $realm;
+
+    }
+
+    /**
+     * Initializes the plugin. This function is automatically called by the server  
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),10);
+
+    }
+
+    /**
+     * Returns a plugin name.
+     * 
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
+     * 
+     * @return string 
+     */
+    public function getPluginName() {
+
+        return 'auth';
+
+    }
+
+    /**
+     * Returns the current users' principal uri.
+     * 
+     * If nobody is logged in, this will return null. 
+     * 
+     * @return string|null 
+     */
+    public function getCurrentUser() {
+
+        $userInfo = $this->authBackend->getCurrentUser();
+        if (!$userInfo) return null;
+
+        return $userInfo;
+
+    }
+
+    /**
+     * This method is called before any HTTP method and forces users to be authenticated
+     * 
+     * @param string $method
+     * @throws Sabre_DAV_Exception_NotAuthenticated
+     * @return bool 
+     */
+    public function beforeMethod($method, $uri) {
+
+        $this->authBackend->authenticate($this->server,$this->realm);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/GuessContentType.php b/3rdparty/Sabre/DAV/Browser/GuessContentType.php
new file mode 100644
index 0000000000000000000000000000000000000000..8b55ec3ad9f4d5ac957b21889caa3fa767af0d34
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/GuessContentType.php
@@ -0,0 +1,97 @@
+<?php
+
+/**
+ * GuessContentType plugin
+ *
+ * A lot of the built-in File objects just return application/octet-stream
+ * as a content-type by default. This is a problem for some clients, because
+ * they expect a correct contenttype.
+ *
+ * There's really no accurate, fast and portable way to determine the contenttype
+ * so this extension does what the rest of the world does, and guesses it based
+ * on the file extension.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Browser_GuessContentType extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * List of recognized file extensions
+     *
+     * Feel free to add more
+     *
+     * @var array
+     */
+    public $extensionMap = array(
+
+        // images
+        'jpg' => 'image/jpeg',
+        'gif' => 'image/gif',
+        'png' => 'image/png',
+
+        // groupware
+        'ics' => 'text/calendar',
+        'vcf' => 'text/x-vcard',
+
+        // text
+        'txt' => 'text/plain',
+
+    );
+
+    /**
+     * Initializes the plugin 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        // Using a relatively low priority (200) to allow other extensions
+        // to set the content-type first.
+        $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'),200);
+
+    }
+
+    /**
+     * Handler for teh afterGetProperties event 
+     * 
+     * @param string $path 
+     * @param array $properties 
+     * @return void
+     */
+    public function afterGetProperties($path, &$properties) {
+
+        if (array_key_exists('{DAV:}getcontenttype', $properties[404])) {
+           
+            list(, $fileName) = Sabre_DAV_URLUtil::splitPath($path);
+            $contentType = $this->getContentType($fileName);
+
+            if ($contentType) {
+                $properties[200]['{DAV:}getcontenttype'] = $contentType;
+                unset($properties[404]['{DAV:}getcontenttype']);
+            }
+
+        }
+
+    }
+
+    /**
+     * Simple method to return the contenttype
+     * 
+     * @param string $fileName 
+     * @return string 
+     */
+    protected function getContentType($fileName) {
+
+        // Just grabbing the extension
+        $extension = substr($fileName,strrpos($fileName,'.')+1);
+        if (isset($this->extensionMap[$extension]))
+            return $this->extensionMap[$extension];
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php
new file mode 100644
index 0000000000000000000000000000000000000000..a66b57a3a90ac89ba9038cf0caa085f194c536fd
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/MapGetToPropFind.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * This is a simple plugin that will map any GET request for non-files to 
+ * PROPFIND allprops-requests.
+ *
+ * This should allow easy debugging of PROPFIND
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Browser_MapGetToPropFind extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * reference to server class 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    protected $server;
+
+    /**
+     * Initializes the plugin and subscribes to events 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
+    }
+
+    /**
+     * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request 
+     * 
+     * @param string $method 
+     * @return bool 
+     */
+    public function httpGetInterceptor($method, $uri) {
+
+        if ($method!='GET') return true;
+       
+        $node = $this->server->tree->getNodeForPath($uri);
+        if ($node instanceof Sabre_DAV_IFile) return;
+
+        $this->server->invokeMethod('PROPFIND',$uri);
+        return false;
+        
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Browser/Plugin.php b/3rdparty/Sabre/DAV/Browser/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e0ca24cff25176fcb96d021e013c13f272f653a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Browser/Plugin.php
@@ -0,0 +1,275 @@
+<?php
+
+/**
+ * Browser Plugin
+ *
+ * This plugin provides a html representation, so that a WebDAV server may be accessed
+ * using a browser.
+ *
+ * The class intercepts GET requests to collection resources and generates a simple 
+ * html index. 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Browser_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * reference to server class 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    protected $server;
+
+    /**
+     * enableEditing
+     * 
+     * @var bool 
+     */
+    protected $enablePost = true;
+
+    /**
+     * Creates the object.
+     *
+     * By default it will allow file creation and uploads.
+     * Specify the first argument as false to disable this
+     * 
+     * @param bool $enablePost 
+     * @return void
+     */
+    public function __construct($enablePost=true) {
+
+        $this->enablePost = $enablePost; 
+
+    }
+
+    /**
+     * Initializes the plugin and subscribes to events 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod',array($this,'httpGetInterceptor'));
+        if ($this->enablePost) $this->server->subscribeEvent('unknownMethod',array($this,'httpPOSTHandler'));
+    }
+
+    /**
+     * This method intercepts GET requests to collections and returns the html 
+     * 
+     * @param string $method 
+     * @return bool 
+     */
+    public function httpGetInterceptor($method, $uri) {
+
+        if ($method!='GET') return true;
+        
+        $node = $this->server->tree->getNodeForPath($uri);
+        if ($node instanceof Sabre_DAV_IFile) return true;
+
+        $this->server->httpResponse->sendStatus(200);
+        $this->server->httpResponse->setHeader('Content-Type','text/html; charset=utf-8');
+
+        $this->server->httpResponse->sendBody(
+            $this->generateDirectoryIndex($uri)
+        );
+
+        return false;
+        
+    }
+
+    /**
+     * Handles POST requests for tree operations
+     * 
+     * This method is not yet used.
+     * 
+     * @param string $method 
+     * @return bool
+     */
+    public function httpPOSTHandler($method, $uri) {
+
+        if ($method!='POST') return true;
+        if (isset($_POST['sabreAction'])) switch($_POST['sabreAction']) {
+
+            case 'mkcol' :
+                if (isset($_POST['name']) && trim($_POST['name'])) {
+                    // Using basename() because we won't allow slashes
+                    list(, $folderName) = Sabre_DAV_URLUtil::splitPath(trim($_POST['name']));
+                    $this->server->createDirectory($uri . '/' . $folderName);
+                }
+                break;
+            case 'put' :
+                if ($_FILES) $file = current($_FILES);
+                else break;
+                $newName = trim($file['name']);
+                list(, $newName) = Sabre_DAV_URLUtil::splitPath(trim($file['name']));
+                if (isset($_POST['name']) && trim($_POST['name']))
+                    $newName = trim($_POST['name']);
+
+                // Making sure we only have a 'basename' component
+                list(, $newName) = Sabre_DAV_URLUtil::splitPath($newName);
+                    
+               
+                if (is_uploaded_file($file['tmp_name'])) {
+                    $parent = $this->server->tree->getNodeForPath(trim($uri,'/'));
+                    $parent->createFile($newName,fopen($file['tmp_name'],'r'));
+                }
+
+        }
+        $this->server->httpResponse->setHeader('Location',$this->server->httpRequest->getUri());
+        return false;
+
+    }
+
+    /**
+     * Escapes a string for html. 
+     * 
+     * @param string $value 
+     * @return void
+     */
+    public function escapeHTML($value) {
+
+        return htmlspecialchars($value,ENT_QUOTES,'UTF-8');
+
+    }
+
+    /**
+     * Generates the html directory index for a given url 
+     *
+     * @param string $path 
+     * @return string 
+     */
+    public function generateDirectoryIndex($path) {
+
+        $html = "<html>
+<head>
+  <title>Index for " . $this->escapeHTML($path) . "/ - SabreDAV " . Sabre_DAV_Version::VERSION . "</title>
+  <style type=\"text/css\"> body { Font-family: arial}</style>
+</head>
+<body>
+  <h1>Index for " . $this->escapeHTML($path) . "/</h1>
+  <table>
+    <tr><th>Name</th><th>Type</th><th>Size</th><th>Last modified</th></tr>
+    <tr><td colspan=\"4\"><hr /></td></tr>";
+    
+    $files = $this->server->getPropertiesForPath($path,array(
+        '{DAV:}displayname',
+        '{DAV:}resourcetype',
+        '{DAV:}getcontenttype',
+        '{DAV:}getcontentlength',
+        '{DAV:}getlastmodified',
+    ),1);
+
+
+    if ($path) {
+
+        list($parentUri) = Sabre_DAV_URLUtil::splitPath($path);
+        $fullPath = Sabre_DAV_URLUtil::encodePath($this->server->getBaseUri() . $parentUri);
+
+        $html.= "<tr>
+<td><a href=\"{$fullPath}\">..</a></td>
+<td>[parent]</td>
+<td></td>
+<td></td>
+</tr>";
+
+    }
+
+    foreach($files as $k=>$file) {
+
+        // This is the current directory, we can skip it
+        if (rtrim($file['href'],'/')==$path) continue;
+
+        list(, $name) = Sabre_DAV_URLUtil::splitPath($file['href']);
+
+        $type = null;
+
+        if (isset($file[200]['{DAV:}resourcetype'])) {
+            $type = $file[200]['{DAV:}resourcetype']->getValue();
+
+            // resourcetype can have multiple values
+            if (!is_array($type)) $type = array($type);
+
+            foreach($type as $k=>$v) { 
+
+                // Some name mapping is preferred 
+                switch($v) {
+                    case '{DAV:}collection' :
+                        $type[$k] = 'Collection';
+                        break;
+                    case '{DAV:}principal' :
+                        $type[$k] = 'Principal';
+                        break;
+                    case '{urn:ietf:params:xml:ns:carddav}addressbook' :
+                        $type[$k] = 'Addressbook';
+                        break;
+                    case '{urn:ietf:params:xml:ns:caldav}calendar' :
+                        $type[$k] = 'Calendar';
+                        break;
+                }
+
+            }
+            $type = implode(', ', $type);
+        }
+
+        // If no resourcetype was found, we attempt to use
+        // the contenttype property
+        if (!$type && isset($file[200]['{DAV:}getcontenttype'])) {
+            $type = $file[200]['{DAV:}getcontenttype'];
+        }
+        if (!$type) $type = 'Unknown';
+
+        $size = isset($file[200]['{DAV:}getcontentlength'])?(int)$file[200]['{DAV:}getcontentlength']:'';
+        $lastmodified = isset($file[200]['{DAV:}getlastmodified'])?$file[200]['{DAV:}getlastmodified']->getTime()->format(DateTime::ATOM):'';
+
+        $fullPath = Sabre_DAV_URLUtil::encodePath('/' . trim($this->server->getBaseUri() . ($path?$path . '/':'') . $name,'/'));
+
+        $displayName = isset($file[200]['{DAV:}displayname'])?$file[200]['{DAV:}displayname']:$name;
+
+        $name = $this->escapeHTML($name);
+        $displayName = $this->escapeHTML($displayName);
+        $type = $this->escapeHTML($type);
+
+        $html.= "<tr>
+<td><a href=\"{$fullPath}\">{$displayName}</a></td>
+<td>{$type}</td>
+<td>{$size}</td>
+<td>{$lastmodified}</td>
+</tr>";
+
+    }
+
+  $html.= "<tr><td colspan=\"4\"><hr /></td></tr>";
+
+  if ($this->enablePost) {
+      $html.= '<tr><td><form method="post" action="">
+            <h3>Create new folder</h3>
+            <input type="hidden" name="sabreAction" value="mkcol" />
+            Name: <input type="text" name="name" /><br />
+            <input type="submit" value="create" />
+            </form>
+            <form method="post" action="" enctype="multipart/form-data">
+            <h3>Upload file</h3>
+            <input type="hidden" name="sabreAction" value="put" />
+            Name (optional): <input type="text" name="name" /><br />
+            File: <input type="file" name="file" /><br />
+            <input type="submit" value="upload" />
+            </form>
+       </td></tr>';
+  }
+
+  $html.= "</table>
+  <address>Generated by SabreDAV " . Sabre_DAV_Version::VERSION ."-". Sabre_DAV_Version::STABILITY . " (c)2007-2011 <a href=\"http://code.google.com/p/sabredav/\">http://code.google.com/p/sabredav/</a></address>
+</body>
+</html>";
+
+        return $html; 
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Client.php b/3rdparty/Sabre/DAV/Client.php
new file mode 100644
index 0000000000000000000000000000000000000000..fc6a6fff083e4ea2747d2fdb3d843c4570757c75
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Client.php
@@ -0,0 +1,431 @@
+<?php
+
+/**
+ * SabreDAV DAV client
+ *
+ * This client wraps around Curl to provide a convenient API to a WebDAV 
+ * server.
+ *
+ * NOTE: This class is experimental, it's api will likely change in the future.
+ * 
+ * @package Sabre
+ * @subpackage DAVClient
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Client {
+
+    public $propertyMap = array();
+
+    protected $baseUri;
+    protected $userName;
+    protected $password;
+    protected $proxy;
+
+    /**
+     * Constructor
+     *
+     * Settings are provided through the 'settings' argument. The following 
+     * settings are supported:
+     *
+     *   * baseUri
+     *   * userName (optional)
+     *   * password (optional)
+     *   * proxy (optional)
+     * 
+     * @param array $settings 
+     */
+    public function __construct(array $settings) {
+
+        if (!isset($settings['baseUri'])) {
+            throw new InvalidArgumentException('A baseUri must be provided');
+        }
+        
+        $validSettings = array(
+            'baseUri',
+            'userName',
+            'password',
+            'proxy'
+        );
+
+        foreach($validSettings as $validSetting) {
+            if (isset($settings[$validSetting])) {
+                $this->$validSetting = $settings[$validSetting];
+            }
+        }
+
+        $this->propertyMap['{DAV:}resourcetype'] = 'Sabre_DAV_Property_ResourceType';
+
+    }
+
+    /**
+     * Does a PROPFIND request
+     *
+     * The list of requested properties must be specified as an array, in clark 
+     * notation. 
+     *
+     * The returned array will contain a list of filenames as keys, and 
+     * properties as values.
+     *
+     * The properties array will contain the list of properties. Only properties 
+     * that are actually returned from the server (without error) will be 
+     * returned, anything else is discarded.
+     *
+     * Depth should be either 0 or 1. A depth of 1 will cause a request to be 
+     * made to the server to also return all child resources.
+     *
+     * @param string $url 
+     * @param array $properties 
+     * @param int $depth 
+     * @return array 
+     */
+    public function propFind($url, array $properties, $depth = 0) {
+
+        $body = '<?xml version="1.0"?>' . "\n";
+        $body.= '<d:propfind xmlns:d="DAV:">' . "\n";
+        $body.= '  <d:prop>' . "\n";
+
+        foreach($properties as $property) {
+
+            list(
+                $namespace,
+                $elementName
+            ) = Sabre_DAV_XMLUtil::parseClarkNotation($property);
+
+            if ($namespace === 'DAV:') {
+                $body.='    <d:' . $elementName . ' />' . "\n";
+            } else {
+                $body.="    <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
+            }
+
+        }
+
+        $body.= '  </d:prop>' . "\n";
+        $body.= '</d:propfind>';
+
+        $response = $this->request('PROPFIND', $url, $body, array(
+            'Depth' => $depth,
+            'Content-Type' => 'application/xml'
+        ));
+
+        $result = $this->parseMultiStatus($response['body']);
+
+        // If depth was 0, we only return the top item
+        if ($depth===0) {
+            reset($result);
+            $result = current($result);
+            return $result[200];
+        }
+
+        $newResult = array();
+        foreach($result as $href => $statusList) {
+
+            $newResult[$href] = $statusList[200];
+
+        }
+
+        return $newResult;
+
+    }
+
+    /**
+     * Updates a list of properties on the server
+     *
+     * The list of properties must have clark-notation properties for the keys, 
+     * and the actual (string) value for the value. If the value is null, an 
+     * attempt is made to delete the property. 
+     *
+     * @todo Must be building the request using the DOM, and does not yet 
+     *       support complex properties. 
+     * @param string $url 
+     * @param array $properties 
+     * @return void
+     */
+    public function propPatch($url, array $properties) {
+
+        $body = '<?xml version="1.0"?>' . "\n";
+        $body.= '<d:propertyupdate xmlns:d="DAV:">' . "\n";
+
+        foreach($properties as $propName => $propValue) {
+
+            list(
+                $namespace,
+                $elementName
+            ) = Sabre_DAV_XMLUtil::parseClarkNotation($propName);
+
+            if ($propValue === null) {
+
+                $body.="<d:remove><d:prop>\n";
+
+                if ($namespace === 'DAV:') {
+                    $body.='    <d:' . $elementName . ' />' . "\n";
+                } else {
+                    $body.="    <x:" . $elementName . " xmlns:x=\"" . $namespace . "\"/>\n";
+                }
+
+                $body.="</d:prop></d:remove>\n";
+
+            } else {
+
+                $body.="<d:set><d:prop>\n";
+                if ($namespace === 'DAV:') {
+                    $body.='    <d:' . $elementName . '>';
+                } else {
+                    $body.="    <x:" . $elementName . " xmlns:x=\"" . $namespace . "\">";
+                }
+                // Shitty.. i know
+                $body.=htmlspecialchars($propValue, ENT_NOQUOTES, 'UTF-8'); 
+                if ($namespace === 'DAV:') {
+                    $body.='</d:' . $elementName . '>' . "\n";
+                } else {
+                    $body.="</x:" . $elementName . ">\n";
+                }
+                $body.="</d:prop></d:set>\n";
+
+            }
+
+        }
+
+        $body.= '</d:propertyupdate>';
+
+        $response = $this->request('PROPPATCH', $url, $body, array(
+            'Content-Type' => 'application/xml'
+        ));
+
+    }
+
+    /**
+     * Performs an HTTP options request
+     *
+     * This method returns all the features from the 'DAV:' header as an array. 
+     * If there was no DAV header, or no contents this method will return an 
+     * empty array. 
+     * 
+     * @return array 
+     */
+    public function options() {
+
+        $result = $this->request('OPTIONS');
+        if (!isset($result['headers']['dav'])) {
+            return array();
+        }
+
+        $features = explode(',', $result['headers']['dav']);
+        foreach($features as &$v) {
+            $v = trim($v);
+        }
+        return $features;
+
+    }
+
+    /**
+     * Performs an actual HTTP request, and returns the result.
+     *
+     * If the specified url is relative, it will be expanded based on the base 
+     * url.
+     *
+     * The returned array contains 3 keys:
+     *   * body - the response body
+     *   * httpCode - a HTTP code (200, 404, etc)
+     *   * headers - a list of response http headers. The header names have 
+     *     been lowercased.
+     *
+     * @param string $method 
+     * @param string $url 
+     * @param string $body 
+     * @param array $headers 
+     * @return array 
+     */
+    public function request($method, $url = '', $body = null, $headers = array()) {
+
+        $url = $this->getAbsoluteUrl($url);
+
+        $curlSettings = array(
+            CURLOPT_RETURNTRANSFER => true,
+            CURLOPT_CUSTOMREQUEST => $method,
+            CURLOPT_POSTFIELDS => $body,
+            // Return headers as part of the response
+            CURLOPT_HEADER => true
+        );
+
+        // Adding HTTP headers
+        $nHeaders = array(); 
+        foreach($headers as $key=>$value) {
+
+            $nHeaders[] = $key . ': ' . $value;
+
+        }
+        $curlSettings[CURLOPT_HTTPHEADER] = $nHeaders;
+
+        if ($this->proxy) {
+            $curlSettings[CURLOPT_PROXY] = $this->proxy;
+        }
+
+        if ($this->userName) {
+            $curlSettings[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC | CURLAUTH_DIGEST;
+            $curlSettings[CURLOPT_USERPWD] = $this->userName . ':' . $this->password;
+        }
+
+        list(
+            $response,
+            $curlInfo,
+            $curlErrNo,
+            $curlError
+        ) = $this->curlRequest($url, $curlSettings);
+
+        $headerBlob = substr($response, 0, $curlInfo['header_size']);
+        $response = substr($response, $curlInfo['header_size']);
+
+        // In the case of 100 Continue, or redirects we'll have multiple lists 
+        // of headers for each separate HTTP response. We can easily split this 
+        // because they are separated by \r\n\r\n
+        $headerBlob = explode("\r\n\r\n", trim($headerBlob, "\r\n"));
+        
+        // We only care about the last set of headers
+        $headerBlob = $headerBlob[count($headerBlob)-1];
+
+        // Splitting headers
+        $headerBlob = explode("\r\n", $headerBlob);
+        
+        $headers = array();
+        foreach($headerBlob as $header) {
+            $parts = explode(':', $header, 2);
+            if (count($parts)==2) {
+                $headers[strtolower(trim($parts[0]))] = trim($parts[1]);
+            }
+        }
+
+        $response = array(
+            'body' => $response,
+            'statusCode' => $curlInfo['http_code'],
+            'headers' => $headers
+        );
+
+        if ($curlErrNo) {
+            throw new Sabre_DAV_Exception('[CURL] Error while making request: ' . $curlError . ' (error code: ' . $curlErrNo . ')');
+        } 
+
+        if ($response['statusCode']>=400) {
+            throw new Sabre_DAV_Exception('HTTP error response. (errorcode ' . $response['statusCode'] . ')');
+        }
+
+        return $response;
+
+    }
+
+    /**
+     * Wrapper for all curl functions.
+     *
+     * The only reason this was split out in a separate method, is so it 
+     * becomes easier to unittest. 
+     *
+     * @param string $url
+     * @param array $settings 
+     * @return  
+     */
+    protected function curlRequest($url, $settings) {
+
+        $curl = curl_init($url);
+        curl_setopt_array($curl, $settings);
+
+        return array(
+            curl_exec($curl),
+            curl_getinfo($curl),
+            curl_errno($curl),
+            curl_error($curl)
+        );
+
+    }
+
+    /**
+     * Returns the full url based on the given url (which may be relative). All 
+     * urls are expanded based on the base url as given by the server. 
+     * 
+     * @param string $url 
+     * @return string 
+     */
+    protected function getAbsoluteUrl($url) {
+
+        // If the url starts with http:// or https://, the url is already absolute. 
+        if (preg_match('/^http(s?):\/\//', $url)) {
+            return $url;
+        }
+
+        // If the url starts with a slash, we must calculate the url based off 
+        // the root of the base url.
+        if (strpos($url,'/') === 0) {
+            $parts = parse_url($this->baseUri);
+            return $parts['scheme'] . '://' . $parts['host'] . (isset($parts['port'])?':' . $parts['port']:'') . $url;
+        }
+
+        // Otherwise...
+        return $this->baseUri . $url;
+
+    }
+
+    /**
+     * Parses a WebDAV multistatus response body
+     * 
+     * This method returns an array with the following structure
+     *
+     * array(
+     *   'url/to/resource' => array(
+     *     '200' => array(
+     *        '{DAV:}property1' => 'value1',
+     *        '{DAV:}property2' => 'value2',
+     *     ),
+     *     '404' => array(
+     *        '{DAV:}property1' => null,
+     *        '{DAV:}property2' => null,
+     *     ),
+     *   )
+     *   'url/to/resource2' => array(
+     *      .. etc ..
+     *   )
+     * )
+     *
+     *
+     * @param string $body xml body
+     * @return array 
+     */
+    public function parseMultiStatus($body) {
+
+        $body = Sabre_DAV_XMLUtil::convertDAVNamespace($body);
+
+        $responseXML = simplexml_load_string($body, null, LIBXML_NOBLANKS | LIBXML_NOCDATA);
+        if ($responseXML===false) {
+            throw new InvalidArgumentException('The passed data is not valid XML');
+        }
+         
+        $responseXML->registerXPathNamespace('d','DAV:');
+
+        $propResult = array();
+
+        foreach($responseXML->xpath('d:response') as $response) {
+
+            $response->registerXPathNamespace('d','DAV:');
+            $href = $response->xpath('d:href');
+            $href = (string)$href[0];
+
+            $properties = array();
+
+            foreach($response->xpath('d:propstat') as $propStat) {
+
+                $propStat->registerXPathNamespace('d','DAV:');
+                $status = $propStat->xpath('d:status');
+                list($httpVersion, $statusCode, $message) = explode(' ', (string)$status[0],3);
+
+                $properties[$statusCode] = Sabre_DAV_XMLUtil::parseProperties(dom_import_simplexml($propStat), $this->propertyMap); 
+
+            }
+
+            $propResult[$href] = $properties;
+
+        }
+
+        return $propResult;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Collection.php b/3rdparty/Sabre/DAV/Collection.php
new file mode 100644
index 0000000000000000000000000000000000000000..9da04c12792fc1324e8b7a502079441a4decc714
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Collection.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * Collection class
+ *
+ * This is a helper class, that should aid in getting collections classes setup.
+ * Most of its methods are implemented, and throw permission denied exceptions 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Collection extends Sabre_DAV_Node implements Sabre_DAV_ICollection {
+
+    /**
+     * Returns a child object, by its name.
+     *
+     * This method makes use of the getChildren method to grab all the child nodes, and compares the name. 
+     * Generally its wise to override this, as this can usually be optimized
+     * 
+     * @param string $name
+     * @throws Sabre_DAV_Exception_FileNotFound
+     * @return Sabre_DAV_INode 
+     */
+    public function getChild($name) {
+
+        foreach($this->getChildren() as $child) {
+
+            if ($child->getName()==$name) return $child;
+
+        }
+        throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name);
+
+    }
+
+    /**
+     * Checks is a child-node exists.
+     *
+     * It is generally a good idea to try and override this. Usually it can be optimized.
+     * 
+     * @param string $name 
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        try {
+
+            $this->getChild($name);
+            return true;
+
+        } catch(Sabre_DAV_Exception_FileNotFound $e) {
+
+            return false;
+
+        }
+
+    }
+
+    /**
+     * Creates a new file in the directory 
+     * 
+     * @param string $name Name of the file 
+     * @param resource $data Initial payload, passed as a readable stream resource. 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void
+     */
+    public function createFile($name, $data = null) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to create file (filename ' . $name . ')');
+
+    }
+
+    /**
+     * Creates a new subdirectory 
+     * 
+     * @param string $name 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to create directory');
+
+    }
+
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Directory.php b/3rdparty/Sabre/DAV/Directory.php
new file mode 100644
index 0000000000000000000000000000000000000000..86af4827b3edf5555142e6e875b28f060fe7f338
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Directory.php
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * Directory class
+ *
+ * This class is now deprecated in favor of the Sabre_DAV_Collection class.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @deprecated Use Sabre_DAV_Collection instead
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Directory extends Sabre_DAV_Collection {
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception.php b/3rdparty/Sabre/DAV/Exception.php
new file mode 100644
index 0000000000000000000000000000000000000000..61f8b87c0a62f15de6cd09e90383093094f0b799
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * SabreDAV base exception
+ *
+ * This is SabreDAV's base exception file, use this to implement your own exception.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * Main Exception class. 
+ *
+ * This class defines a getHTTPCode method, which should return the appropriate HTTP code for the Exception occured.
+ * The default for this is 500.
+ *
+ * This class also allows you to generate custom xml data for your exceptions. This will be displayed
+ * in the 'error' element in the failing response.
+ */
+class Sabre_DAV_Exception extends Exception { 
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() { 
+
+        return 500;
+
+    }
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+    
+
+    }
+
+    /**
+     * This method allows the exception to return any extra HTTP response headers.
+     *
+     * The headers must be returned as an array.
+     * 
+     * @return array 
+     */
+    public function getHTTPHeaders(Sabre_DAV_Server $server) {
+
+        return array();
+
+    } 
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/BadRequest.php b/3rdparty/Sabre/DAV/Exception/BadRequest.php
new file mode 100644
index 0000000000000000000000000000000000000000..7025bb1031745b8a94b16afc4e83fa81b11de872
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/BadRequest.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * BadRequest
+ *
+ * The BadRequest is thrown when the user submitted an invalid HTTP request
+ * BadRequest 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_BadRequest extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 400; 
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/Conflict.php b/3rdparty/Sabre/DAV/Exception/Conflict.php
new file mode 100644
index 0000000000000000000000000000000000000000..7eaa08178ae2f16f2417a13ef1dd6833037b871a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/Conflict.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * Conflict
+ *
+ * A 409 Conflict is thrown when a user tried to make a directory over an existing
+ * file or in a parent directory that doesn't exist.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_Conflict extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 409;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/ConflictingLock.php b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php
new file mode 100644
index 0000000000000000000000000000000000000000..279f63dfde783a5c0fd6955f486f4d8832dfa530
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/ConflictingLock.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * ConflictingLock 
+ *
+ * Similar to Exception_Locked, this exception thrown when a LOCK request 
+ * was made, on a resource which was already locked
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_ConflictingLock extends Sabre_DAV_Exception_Locked {
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        if ($this->lock) {
+            $error = $errorNode->ownerDocument->createElementNS('DAV:','d:no-conflicting-lock');
+            $errorNode->appendChild($error);
+            if (!is_object($this->lock)) var_dump($this->lock);
+            $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/FileNotFound.php b/3rdparty/Sabre/DAV/Exception/FileNotFound.php
new file mode 100644
index 0000000000000000000000000000000000000000..b20e4a2fb3ff7b1bed6024700969ad415d5185a5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/FileNotFound.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * FileNotFound
+ *
+ * This Exception is thrown when a Node couldn't be found. It returns HTTP error code 404
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_FileNotFound extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 404;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/Forbidden.php b/3rdparty/Sabre/DAV/Exception/Forbidden.php
new file mode 100644
index 0000000000000000000000000000000000000000..167f3c2760a8b38704d19fa340fba7327882d383
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/Forbidden.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * Forbidden
+ *
+ * This exception is thrown whenever a user tries to do an operation he's not allowed to
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_Forbidden extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 403;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..15007cdd352524fd362c754a75bdc7b1ad84ce2e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/InsufficientStorage.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * InsufficientStorage 
+ *
+ * This Exception can be thrown, when for example a harddisk is full or a quota is exceeded
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_InsufficientStorage extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 507;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php
new file mode 100644
index 0000000000000000000000000000000000000000..f06810a25ef58ba2c2b67d71f6accce60d814a7a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/InvalidResourceType.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * InvalidResourceType 
+ *
+ * This exception is thrown when the user tried to create a new collection, with
+ * a special resourcetype value that was not recognized by the server.
+ *
+ * See RFC5689 section 3.3
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_InvalidResourceType extends Sabre_DAV_Exception_Forbidden { 
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+        $error = $errorNode->ownerDocument->createElementNS('DAV:','d:valid-resourcetype');
+        $errorNode->appendChild($error);
+
+    }
+    
+}
diff --git a/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
new file mode 100644
index 0000000000000000000000000000000000000000..47032cffc75de30033010bfd3a632c5de0ee6b01
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/LockTokenMatchesRequestUri.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * LockTokenMatchesRequestUri 
+ *
+ * This exception is thrown by UNLOCK if a supplied lock-token is invalid 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_LockTokenMatchesRequestUri extends Sabre_DAV_Exception_Conflict {
+
+    /**
+     * Creates the exception
+     */
+    public function __construct() {
+
+        $this->message = 'The locktoken supplied does not match any locks on this entity';
+
+    }
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+        $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-matches-request-uri');
+        $errorNode->appendChild($error);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/Locked.php b/3rdparty/Sabre/DAV/Exception/Locked.php
new file mode 100644
index 0000000000000000000000000000000000000000..b4bb2e0378cc55ed509258da4a5af79b3f853efe
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/Locked.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * Locked 
+ *
+ * The 423 is thrown when a client tried to access a resource that was locked, without supplying a valid lock token
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_Locked extends Sabre_DAV_Exception {
+
+    /**
+     * Lock information 
+     * 
+     * @var Sabre_DAV_Locks_LockInfo 
+     */
+    protected $lock;
+
+    /**
+     * Creates the exception
+     * 
+     * A LockInfo object should be passed if the user should be informed
+     * which lock actually has the file locked.
+     * 
+     * @param Sabre_DAV_Locks_LockInfo $lock 
+     */
+    public function __construct(Sabre_DAV_Locks_LockInfo $lock = null) {
+
+        $this->lock = $lock;
+
+    }
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 423;
+
+    }
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        if ($this->lock) {
+            $error = $errorNode->ownerDocument->createElementNS('DAV:','d:lock-token-submitted');
+            $errorNode->appendChild($error);
+            if (!is_object($this->lock)) var_dump($this->lock);
+            $error->appendChild($errorNode->ownerDocument->createElementNS('DAV:','d:href',$this->lock->uri));
+        }
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php
new file mode 100644
index 0000000000000000000000000000000000000000..02c145ffeb635de6320a707ed191963ae8da28e9
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/MethodNotAllowed.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * MethodNotAllowed
+ *
+ * The 405 is thrown when a client tried to create a directory on an already existing directory
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_MethodNotAllowed extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 405;
+
+    }
+
+    /**
+     * This method allows the exception to return any extra HTTP response headers.
+     *
+     * The headers must be returned as an array.
+     * 
+     * @return array 
+     */
+    public function getHTTPHeaders(Sabre_DAV_Server $server) {
+
+        $methods = $server->getAllowedMethods($server->getRequestUri());
+
+        return array(
+            'Allow' => strtoupper(implode(', ',$methods)),
+        );
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php
new file mode 100644
index 0000000000000000000000000000000000000000..1faffddfa00d58f171ed2ac3a6de00145438110b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/NotAuthenticated.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * NotAuthenticated
+ *
+ * This exception is thrown when the client did not provide valid
+ * authentication credentials.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_NotAuthenticated extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+        
+        return 401;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/NotImplemented.php b/3rdparty/Sabre/DAV/Exception/NotImplemented.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd7f609b09df436604e39bf82528854ad349bbd3
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/NotImplemented.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * NotImplemented
+ *
+ * This exception is thrown when the client tried to call an unsupported HTTP method or other feature
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_NotImplemented extends Sabre_DAV_Exception {
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+        
+        return 501;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php
new file mode 100644
index 0000000000000000000000000000000000000000..ebcb9f5b9ac858e0a77907bd8785d4e4f0b582f0
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/PreconditionFailed.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * PreconditionFailed 
+ *
+ * This exception is normally thrown when a client submitted a conditional request, 
+ * like for example an If, If-None-Match or If-Match header, which caused the HTTP 
+ * request to not execute (the condition of the header failed)
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_PreconditionFailed extends Sabre_DAV_Exception {
+
+    /**
+     * When this exception is thrown, the header-name might be set.
+     * 
+     * This allows the exception-catching code to determine which HTTP header
+     * caused the exception.
+     * 
+     * @var string 
+     */
+    public $header = null;
+
+    /**
+     * Create the exception 
+     * 
+     * @param string $message 
+     * @param string $header 
+     */
+    public function __construct($message, $header=null) {
+
+        parent::__construct($message);
+        $this->header = $header;
+
+    } 
+
+    /**
+     * Returns the HTTP statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 412; 
+
+    }
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        if ($this->header) {
+            $prop = $errorNode->ownerDocument->createElement('s:header');
+            $prop->nodeValue = $this->header;
+            $errorNode->appendChild($prop);
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php
new file mode 100644
index 0000000000000000000000000000000000000000..e4ed601b16cd8d37d3663379a17de1a5bd29171a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/ReportNotImplemented.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * ReportNotImplemented
+ *
+ * This exception is thrown when the client requested an unknown report through the REPORT method
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_ReportNotImplemented extends Sabre_DAV_Exception_NotImplemented {
+
+    /**
+     * This method allows the exception to include additonal information into the WebDAV error response 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+
+        $error = $errorNode->ownerDocument->createElementNS('DAV:','d:supported-report');
+        $errorNode->appendChild($error);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
new file mode 100644
index 0000000000000000000000000000000000000000..37abbd729d11724077faf53b3240cf153d99b0be
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/RequestedRangeNotSatisfiable.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * RequestedRangeNotSatisfiable 
+ *
+ * This exception is normally thrown when the user 
+ * request a range that is out of the entity bounds.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_RequestedRangeNotSatisfiable extends Sabre_DAV_Exception {
+
+    /**
+     * returns the http statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 416;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c37d8997cf0a63ac09aa20091160b09bd87c092
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Exception/UnsupportedMediaType.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * UnSupportedMediaType
+ *
+ * The 415 Unsupported Media Type status code is generally sent back when the client 
+ * tried to call an HTTP method, with a body the server didn't understand
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Exception_UnsupportedMediaType extends Sabre_DAV_Exception { 
+
+    /**
+     * returns the http statuscode for this exception 
+     *
+     * @return int
+     */
+    public function getHTTPCode() {
+
+        return 415;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/FS/Directory.php b/3rdparty/Sabre/DAV/FS/Directory.php
new file mode 100644
index 0000000000000000000000000000000000000000..ebd6a6c505e2bd10140d963bdbb1d6829b65f9f2
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FS/Directory.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Directory class 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FS_Directory extends Sabre_DAV_FS_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+    /**
+     * Creates a new file in the directory 
+     * 
+     * data is a readable stream resource
+     *
+     * @param string $name Name of the file 
+     * @param resource $data Initial payload 
+     * @return void
+     */
+    public function createFile($name, $data = null) {
+
+        $newPath = $this->path . '/' . $name;
+        file_put_contents($newPath,$data);
+
+    }
+
+    /**
+     * Creates a new subdirectory 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        $newPath = $this->path . '/' . $name;
+        mkdir($newPath);
+
+    }
+
+    /**
+     * Returns a specific child node, referenced by its name 
+     * 
+     * @param string $name 
+     * @throws Sabre_DAV_Exception_FileNotFound
+     * @return Sabre_DAV_INode 
+     */
+    public function getChild($name) {
+
+        $path = $this->path . '/' . $name;
+
+        if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File with name ' . $path . ' could not be located');
+
+        if (is_dir($path)) {
+
+            return new Sabre_DAV_FS_Directory($path);
+
+        } else {
+
+            return new Sabre_DAV_FS_File($path);
+
+        }
+
+    }
+
+    /**
+     * Returns an array with all the child nodes 
+     * 
+     * @return Sabre_DAV_INode[] 
+     */
+    public function getChildren() {
+
+        $nodes = array();
+        foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node);
+        return $nodes;
+
+    }
+
+    /**
+     * Checks if a child exists. 
+     * 
+     * @param string $name 
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        $path = $this->path . '/' . $name;
+        return file_exists($path);
+
+    }
+
+    /**
+     * Deletes all files in this directory, and then itself 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        foreach($this->getChildren() as $child) $child->delete();
+        rmdir($this->path);
+
+    }
+
+    /**
+     * Returns available diskspace information 
+     * 
+     * @return array 
+     */
+    public function getQuotaInfo() {
+
+        return array(
+            disk_total_space($this->path)-disk_free_space($this->path),
+            disk_free_space($this->path)
+            ); 
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FS/File.php b/3rdparty/Sabre/DAV/FS/File.php
new file mode 100644
index 0000000000000000000000000000000000000000..262187d7e8ab8c1abeb60d8b9584920d2b05fe55
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FS/File.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * File class 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FS_File extends Sabre_DAV_FS_Node implements Sabre_DAV_IFile {
+
+    /**
+     * Updates the data 
+     * 
+     * @param resource $data 
+     * @return void 
+     */
+    public function put($data) {
+
+        file_put_contents($this->path,$data);
+
+    }
+
+    /**
+     * Returns the data 
+     * 
+     * @return string 
+     */
+    public function get() {
+
+        return fopen($this->path,'r');
+
+    }
+
+    /**
+     * Delete the current file
+     *
+     * @return void 
+     */
+    public function delete() {
+
+        unlink($this->path);
+
+    }
+
+    /**
+     * Returns the size of the node, in bytes 
+     * 
+     * @return int 
+     */
+    public function getSize() {
+        
+        return filesize($this->path);
+
+    }
+
+    /**
+     * Returns the ETag for a file
+     *
+     * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+     * The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
+     *
+     * Return null if the ETag can not effectively be determined
+     * 
+     * @return mixed
+     */
+    public function getETag() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns the mime-type for a file
+     *
+     * If null is returned, we'll assume application/octet-stream
+     *
+     * @return mixed 
+     */ 
+    public function getContentType() {
+
+        return null;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FS/Node.php b/3rdparty/Sabre/DAV/FS/Node.php
new file mode 100644
index 0000000000000000000000000000000000000000..b8d7bcfe8461c11dc4448098889dacf29939aa4a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FS/Node.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * Base node-class 
+ *
+ * The node class implements the method used by both the File and the Directory classes 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_FS_Node implements Sabre_DAV_INode {
+
+    /**
+     * The path to the current node
+     * 
+     * @var string 
+     */
+    protected $path; 
+
+    /**
+     * Sets up the node, expects a full path name 
+     * 
+     * @param string $path 
+     * @return void
+     */
+    public function __construct($path) {
+
+        $this->path = $path;
+
+    }
+
+
+
+    /**
+     * Returns the name of the node 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        list(, $name)  = Sabre_DAV_URLUtil::splitPath($this->path);
+        return $name;
+
+    }
+
+    /**
+     * Renames the node
+     *
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+        list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+
+        $newPath = $parentPath . '/' . $newName;
+        rename($this->path,$newPath);
+        
+        $this->path = $newPath;
+
+    }
+
+
+
+    /**
+     * Returns the last modification time, as a unix timestamp 
+     * 
+     * @return int 
+     */
+    public function getLastModified() {
+
+        return filemtime($this->path);
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FSExt/Directory.php b/3rdparty/Sabre/DAV/FSExt/Directory.php
new file mode 100644
index 0000000000000000000000000000000000000000..c43d4385ac7b75cb20d19857501cebef51039aa9
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FSExt/Directory.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * Directory class 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FSExt_Directory extends Sabre_DAV_FSExt_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+    /**
+     * Creates a new file in the directory 
+     * 
+     * @param string $name Name of the file 
+     * @param resource $data Initial payload 
+     * @return void
+     */
+    public function createFile($name, $data = null) {
+
+        // We're not allowing dots
+        if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+        $newPath = $this->path . '/' . $name;
+        file_put_contents($newPath,$data);
+
+    }
+
+    /**
+     * Creates a new subdirectory 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function createDirectory($name) {
+
+        // We're not allowing dots
+        if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+        $newPath = $this->path . '/' . $name;
+        mkdir($newPath);
+
+    }
+
+    /**
+     * Returns a specific child node, referenced by its name 
+     * 
+     * @param string $name 
+     * @throws Sabre_DAV_Exception_FileNotFound
+     * @return Sabre_DAV_INode 
+     */
+    public function getChild($name) {
+
+        $path = $this->path . '/' . $name;
+
+        if (!file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File could not be located');
+        if ($name=='.' || $name=='..') throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..'); 
+
+        if (is_dir($path)) {
+
+            return new Sabre_DAV_FSExt_Directory($path);
+
+        } else {
+
+            return new Sabre_DAV_FSExt_File($path);
+
+        }
+
+    }
+
+    /**
+     * Checks if a child exists. 
+     * 
+     * @param string $name 
+     * @return bool 
+     */
+    public function childExists($name) {
+
+        if ($name=='.' || $name=='..')
+            throw new Sabre_DAV_Exception_Forbidden('Permission denied to . and ..');
+
+        $path = $this->path . '/' . $name;
+        return file_exists($path);
+
+    }
+
+    /**
+     * Returns an array with all the child nodes 
+     * 
+     * @return Sabre_DAV_INode[] 
+     */
+    public function getChildren() {
+
+        $nodes = array();
+        foreach(scandir($this->path) as $node) if($node!='.' && $node!='..' && $node!='.sabredav') $nodes[] = $this->getChild($node);
+        return $nodes;
+
+    }
+
+    /**
+     * Deletes all files in this directory, and then itself 
+     * 
+     * @return void
+     */
+    public function delete() {
+
+        // Deleting all children
+        foreach($this->getChildren() as $child) $child->delete();
+
+        // Removing resource info, if its still around
+        if (file_exists($this->path . '/.sabredav')) unlink($this->path . '/.sabredav');
+
+        // Removing the directory itself
+        rmdir($this->path);
+
+        return parent::delete();
+
+    }
+
+    /**
+     * Returns available diskspace information 
+     * 
+     * @return array 
+     */
+    public function getQuotaInfo() {
+
+        return array(
+            disk_total_space($this->path)-disk_free_space($this->path),
+            disk_free_space($this->path)
+            ); 
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FSExt/File.php b/3rdparty/Sabre/DAV/FSExt/File.php
new file mode 100644
index 0000000000000000000000000000000000000000..7a8e7a11f21308dd0d285670030cebd925fdd1e2
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FSExt/File.php
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * File class 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_FSExt_File extends Sabre_DAV_FSExt_Node implements Sabre_DAV_IFile {
+
+    /**
+     * Updates the data 
+     *
+     * data is a readable stream resource.
+     *
+     * @param resource $data 
+     * @return void 
+     */
+    public function put($data) {
+
+        file_put_contents($this->path,$data);
+
+    }
+
+    /**
+     * Returns the data
+     *
+     * @return string 
+     */
+    public function get() {
+
+        return fopen($this->path,'r');
+
+    }
+
+    /**
+     * Delete the current file
+     *
+     * @return void 
+     */
+    public function delete() {
+
+        unlink($this->path);
+        return parent::delete();
+
+    }
+
+    /**
+     * Returns the ETag for a file
+     *
+     * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+     * The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
+     *
+     * Return null if the ETag can not effectively be determined
+     */
+    public function getETag() {
+
+        return '"' . md5_file($this->path). '"';
+
+    }
+
+    /**
+     * Returns the mime-type for a file
+     *
+     * If null is returned, we'll assume application/octet-stream
+     */ 
+    public function getContentType() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns the size of the file, in bytes 
+     * 
+     * @return int 
+     */
+    public function getSize() {
+
+        return filesize($this->path);
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/FSExt/Node.php b/3rdparty/Sabre/DAV/FSExt/Node.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e36222bfd325a6bbe164bea5670e120fb83caf1
--- /dev/null
+++ b/3rdparty/Sabre/DAV/FSExt/Node.php
@@ -0,0 +1,276 @@
+<?php
+
+/**
+ * Base node-class 
+ *
+ * The node class implements the method used by both the File and the Directory classes 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_FSExt_Node extends Sabre_DAV_FS_Node implements Sabre_DAV_ILockable, Sabre_DAV_IProperties {
+
+    /**
+     * Returns all the locks on this node
+     * 
+     * @return array 
+     */
+    function getLocks() {
+
+        $resourceData = $this->getResourceData();
+        $locks = $resourceData['locks'];
+        foreach($locks as $k=>$lock) {
+            if (time() > $lock->timeout + $lock->created) unset($locks[$k]); 
+        }
+        return $locks;
+
+    }
+
+    /**
+     * Locks this node 
+     * 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return void
+     */
+    function lock(Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        // We're making the lock timeout 30 minutes
+        $lockInfo->timeout = 1800;
+        $lockInfo->created = time();
+
+        $resourceData = $this->getResourceData();
+        if (!isset($resourceData['locks'])) $resourceData['locks'] = array();
+        $current = null;
+        foreach($resourceData['locks'] as $k=>$lock) {
+            if ($lock->token === $lockInfo->token) $current = $k;
+        }
+        if (!is_null($current)) $resourceData['locks'][$current] = $lockInfo;
+        else $resourceData['locks'][] = $lockInfo;
+
+        $this->putResourceData($resourceData);
+
+    }
+
+    /**
+     * Removes a lock from this node
+     * 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    function unlock(Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        //throw new Sabre_DAV_Exception('bla');
+        $resourceData = $this->getResourceData();
+        foreach($resourceData['locks'] as $k=>$lock) {
+
+            if ($lock->token === $lockInfo->token) {
+
+                unset($resourceData['locks'][$k]);
+                $this->putResourceData($resourceData);
+                return true;
+
+            }
+        }
+        return false;
+
+    }
+
+    /**
+     * Updates properties on this node,
+     *
+     * @param array $mutations
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array 
+     */
+    public function updateProperties($properties) {
+
+        $resourceData = $this->getResourceData();
+        
+        $result = array();
+
+        foreach($properties as $propertyName=>$propertyValue) {
+
+            // If it was null, we need to delete the property
+            if (is_null($propertyValue)) {
+                if (isset($resourceData['properties'][$propertyName])) {
+                    unset($resourceData['properties'][$propertyName]);
+                }
+            } else {
+                $resourceData['properties'][$propertyName] = $propertyValue;
+            }
+               
+        }
+
+        $this->putResourceData($resourceData);
+        return true; 
+    }
+
+    /**
+     * Returns a list of properties for this nodes.;
+     *
+     * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author
+     * If the array is empty, all properties should be returned
+     *
+     * @param array $properties 
+     * @return void
+     */
+    function getProperties($properties) {
+
+        $resourceData = $this->getResourceData();
+
+        // if the array was empty, we need to return everything
+        if (!$properties) return $resourceData['properties'];
+
+        $props = array();
+        foreach($properties as $property) {
+            if (isset($resourceData['properties'][$property])) $props[$property] = $resourceData['properties'][$property];
+        }
+
+        return $props;
+
+    }
+
+    /**
+     * Returns the path to the resource file 
+     * 
+     * @return string 
+     */
+    protected function getResourceInfoPath() {
+
+        list($parentDir) = Sabre_DAV_URLUtil::splitPath($this->path);
+        return $parentDir . '/.sabredav';
+
+    }
+
+    /**
+     * Returns all the stored resource information 
+     * 
+     * @return array 
+     */
+    protected function getResourceData() {
+
+        $path = $this->getResourceInfoPath();
+        if (!file_exists($path)) return array('locks'=>array(), 'properties' => array());
+
+        // opening up the file, and creating a shared lock
+        $handle = fopen($path,'r');
+        flock($handle,LOCK_SH);
+        $data = '';
+
+        // Reading data until the eof
+        while(!feof($handle)) {
+            $data.=fread($handle,8192);
+        }
+
+        // We're all good
+        fclose($handle);
+
+        // Unserializing and checking if the resource file contains data for this file
+        $data = unserialize($data);
+        if (!isset($data[$this->getName()])) {
+            return array('locks'=>array(), 'properties' => array());
+        }
+
+        $data = $data[$this->getName()];
+        if (!isset($data['locks'])) $data['locks'] = array();
+        if (!isset($data['properties'])) $data['properties'] = array();
+        return $data;
+
+    }
+
+    /**
+     * Updates the resource information 
+     * 
+     * @param array $newData 
+     * @return void
+     */
+    protected function putResourceData(array $newData) {
+
+        $path = $this->getResourceInfoPath();
+
+        // opening up the file, and creating a shared lock
+        $handle = fopen($path,'a+');
+        flock($handle,LOCK_EX);
+        $data = '';
+
+        rewind($handle);
+
+        // Reading data until the eof
+        while(!feof($handle)) {
+            $data.=fread($handle,8192);
+        }
+
+        // Unserializing and checking if the resource file contains data for this file
+        $data = unserialize($data);
+        $data[$this->getName()] = $newData;
+        ftruncate($handle,0);
+        rewind($handle);
+
+        fwrite($handle,serialize($data));
+        fclose($handle);
+
+    }
+
+    /**
+     * Renames the node
+     *
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+        list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+        $newPath = $parentPath . '/' . $newName;
+
+        // We're deleting the existing resourcedata, and recreating it
+        // for the new path.
+        $resourceData = $this->getResourceData();
+        $this->deleteResourceData();
+
+        rename($this->path,$newPath);
+        $this->path = $newPath;
+        $this->putResourceData($resourceData);
+
+
+    }
+
+    public function deleteResourceData() {
+
+        // When we're deleting this node, we also need to delete any resource information
+        $path = $this->getResourceInfoPath();
+        if (!file_exists($path)) return true;
+
+        // opening up the file, and creating a shared lock
+        $handle = fopen($path,'a+');
+        flock($handle,LOCK_EX);
+        $data = '';
+
+        rewind($handle);
+
+        // Reading data until the eof
+        while(!feof($handle)) {
+            $data.=fread($handle,8192);
+        }
+
+        // Unserializing and checking if the resource file contains data for this file
+        $data = unserialize($data);
+        if (isset($data[$this->getName()])) unset($data[$this->getName()]);
+        ftruncate($handle,0);
+        rewind($handle);
+        fwrite($handle,serialize($data));
+        fclose($handle);
+
+    }
+
+    public function delete() {
+
+        return $this->deleteResourceData();
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/File.php b/3rdparty/Sabre/DAV/File.php
new file mode 100644
index 0000000000000000000000000000000000000000..b74bd9525b34cc8452466b260ca8be4c9fc9e16e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/File.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * File class
+ *
+ * This is a helper class, that should aid in getting file classes setup.
+ * Most of its methods are implemented, and throw permission denied exceptions 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_File extends Sabre_DAV_Node implements Sabre_DAV_IFile {
+
+    /**
+     * Updates the data
+     *
+     * data is a readable stream resource.
+     * 
+     * @param resource $data 
+     * @return void 
+     */
+    public function put($data) { 
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to change data');
+
+    }
+
+    /**
+     * Returns the data 
+     *
+     * This method may either return a string or a readable stream resource
+     *
+     * @return mixed 
+     */
+    public function get() { 
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to read this file');
+
+    }
+    
+    /**
+     * Returns the size of the file, in bytes. 
+     * 
+     * @return int 
+     */
+    public function getSize() {
+
+        return 0;
+
+    }
+
+    /**
+     * Returns the ETag for a file
+     *
+     * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+     * The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
+     *
+     * Return null if the ETag can not effectively be determined
+     */
+    public function getETag() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns the mime-type for a file
+     *
+     * If null is returned, we'll assume application/octet-stream
+     */ 
+    public function getContentType() {
+
+        return null;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ICollection.php b/3rdparty/Sabre/DAV/ICollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..0667d88899d2d307c025cc247503506f0d9311e5
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ICollection.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * The ICollection Interface
+ *
+ * This interface should be implemented by each class that represents a collection 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_ICollection extends Sabre_DAV_INode {
+
+    /**
+     * Creates a new file in the directory 
+     * 
+     * data is a readable stream resource
+     *
+     * @param string $name Name of the file 
+     * @param resource $data Initial payload 
+     * @return void
+     */
+    function createFile($name, $data = null);
+
+    /**
+     * Creates a new subdirectory 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    function createDirectory($name);
+
+    /**
+     * Returns a specific child node, referenced by its name 
+     * 
+     * @param string $name 
+     * @return Sabre_DAV_INode 
+     */
+    function getChild($name);
+
+    /**
+     * Returns an array with all the child nodes 
+     * 
+     * @return Sabre_DAV_INode[] 
+     */
+    function getChildren();
+
+    /**
+     * Checks if a child-node with the specified name exists 
+     * 
+     * @return bool 
+     */
+    function childExists($name);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IExtendedCollection.php b/3rdparty/Sabre/DAV/IExtendedCollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..b8db1ab2f26917a03d5266e22ab157efc3e132bc
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IExtendedCollection.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * The IExtendedCollection interface.
+ *
+ * This interface can be used to create special-type of collection-resources
+ * as defined by RFC 5689.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IExtendedCollection extends Sabre_DAV_ICollection {
+
+    /**
+     * Creates a new collection
+     *
+     * @param string $name 
+     * @param array $resourceType
+     * @param array $properties
+     * @return void
+     */
+    function createExtendedCollection($name, array $resourceType, array $properties);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IFile.php b/3rdparty/Sabre/DAV/IFile.php
new file mode 100644
index 0000000000000000000000000000000000000000..446ec86187b3a42f9770683de660b95ff87f3cfb
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IFile.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * This interface represents a file or leaf in the tree.
+ *
+ * The nature of a file is, as you might be aware of, that it doesn't contain sub-nodes and has contents
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IFile extends Sabre_DAV_INode {
+
+    /**
+     * Updates the data 
+     * 
+     * The data argument is a readable stream resource.
+     *
+     * @param resource $data 
+     * @return void 
+     */
+    function put($data);
+
+    /**
+     * Returns the data 
+     * 
+     * This method may either return a string or a readable stream resource
+     *
+     * @return mixed 
+     */
+    function get();
+
+    /**
+     * Returns the mime-type for a file
+     *
+     * If null is returned, we'll assume application/octet-stream
+     * 
+     * @return void
+     */
+    function getContentType();
+
+    /**
+     * Returns the ETag for a file
+     *
+     * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+     *
+     * Return null if the ETag can not effectively be determined
+     * 
+     * @return void
+     */
+    function getETag();
+
+    /**
+     * Returns the size of the node, in bytes 
+     * 
+     * @return int 
+     */
+    function getSize();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ILockable.php b/3rdparty/Sabre/DAV/ILockable.php
new file mode 100644
index 0000000000000000000000000000000000000000..f9fb3a702511eccfd4684c22c606237e1255570b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ILockable.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * Implement this class to support locking 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_ILockable extends Sabre_DAV_INode {
+
+    /**
+     * Returns an array with locks currently on the node 
+     * 
+     * @return Sabre_DAV_Locks_LockInfo[] 
+     */
+    function getLocks();
+
+    /**
+     * Creates a new lock on the file.  
+     * 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo The lock information 
+     * @return void
+     */
+    function lock(Sabre_DAV_Locks_LockInfo $lockInfo);
+
+    /**
+     * Unlocks a file 
+     * 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo The lock information 
+     * @return void 
+     */
+    function unlock(Sabre_DAV_Locks_LockInfo $lockInfo);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/INode.php b/3rdparty/Sabre/DAV/INode.php
new file mode 100644
index 0000000000000000000000000000000000000000..c0b96bf53775370a3374259b51196a0b26e48b1b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/INode.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * The INode interface is the base interface, and the parent class of both ICollection and IFile
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_INode {
+
+    /**
+     * Deleted the current node
+     *
+     * @return void 
+     */
+    function delete();
+
+    /**
+     * Returns the name of the node 
+     * 
+     * @return string 
+     */
+    function getName();
+
+    /**
+     * Renames the node
+     *
+     * @param string $name The new name
+     * @return void
+     */
+    function setName($name);
+
+    /**
+     * Returns the last modification time, as a unix timestamp 
+     * 
+     * @return int 
+     */
+    function getLastModified();
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IProperties.php b/3rdparty/Sabre/DAV/IProperties.php
new file mode 100644
index 0000000000000000000000000000000000000000..af17cad24affd88f1fe8633b25dae128b6b6460f
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IProperties.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+ * IProperties interface
+ *
+ * Implement this interface to support custom WebDAV properties requested and sent from clients.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IProperties extends Sabre_DAV_INode {
+
+    /**
+     * Updates properties on this node,
+     *
+     * The properties array uses the propertyName in clark-notation as key,
+     * and the array value for the property value. In the case a property
+     * should be deleted, the property value will be null.
+     *
+     * This method must be atomic. If one property cannot be changed, the
+     * entire operation must fail.
+     *
+     * If the operation was successful, true can be returned.
+     * If the operation failed, false can be returned.
+     *
+     * Deletion of a non-existant property is always succesful.
+     *
+     * Lastly, it is optional to return detailed information about any
+     * failures. In this case an array should be returned with the following
+     * structure:
+     *
+     * array(
+     *   403 => array(
+     *      '{DAV:}displayname' => null,
+     *   ),
+     *   424 => array(
+     *      '{DAV:}owner' => null,
+     *   )
+     * )
+     *
+     * In this example it was forbidden to update {DAV:}displayname. 
+     * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+     * (424 Failed Dependency) because the request needs to be atomic.
+     *
+     * @param array $mutations 
+     * @return bool|array 
+     */
+    function updateProperties($mutations);
+
+    /**
+     * Returns a list of properties for this nodes.
+     *
+     * The properties list is a list of propertynames the client requested,
+     * encoded in clark-notation {xmlnamespace}tagname
+     *
+     * If the array is empty, it means 'all properties' were requested.
+     *
+     * @param array $properties 
+     * @return void
+     */
+    function getProperties($properties);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/IQuota.php b/3rdparty/Sabre/DAV/IQuota.php
new file mode 100644
index 0000000000000000000000000000000000000000..8ff1a4597f85d43610439216f9c8ecc440429a0c
--- /dev/null
+++ b/3rdparty/Sabre/DAV/IQuota.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * IQuota interface
+ *
+ * Implement this interface to add the ability to return quota information. The ObjectTree
+ * will check for quota information on any given node. If the information is not available it will 
+ * attempt to fetch the information from the root node.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_IQuota extends Sabre_DAV_ICollection {
+
+    /**
+     * Returns the quota information
+     *
+     * This method MUST return an array with 2 values, the first being the total used space,
+     * the second the available space (in bytes)
+     */
+    function getQuotaInfo(); 
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..b09f93ddac79e41d1aa9497d7739a8a20d5d15d9
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/Abstract.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This is an alternative approach to doing this on a per-node basis
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Locks_Backend_Abstract {
+
+    /**
+     * Returns a list of Sabre_DAV_Locks_LockInfo objects  
+     * 
+     * This method should return all the locks for a particular uri, including
+     * locks that might be set on a parent uri.
+     *
+     * If returnChildLocks is set to true, this method should also look for
+     * any locks in the subtree of the uri for locks.
+     *
+     * @param string $uri 
+     * @param bool $returnChildLocks
+     * @return array 
+     */
+    abstract function getLocks($uri, $returnChildLocks);
+
+    /**
+     * Locks a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    abstract function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo);
+
+    /**
+     * Removes a lock from a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    abstract function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo);
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/FS.php b/3rdparty/Sabre/DAV/Locks/Backend/FS.php
new file mode 100644
index 0000000000000000000000000000000000000000..8653f55b1c6f9a116733392332975ff7c5a97e48
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/FS.php
@@ -0,0 +1,189 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This Lock Manager is now deprecated. It has a bug that allows parent 
+ * collections to be deletes when children deeper in the tree are locked. 
+ *
+ * You are recommended to use either the PDO or the File backend instead.
+ *
+ * This Lock Manager stores all its data in the filesystem.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @deprecated
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Backend_FS extends Sabre_DAV_Locks_Backend_Abstract {
+
+    /**
+     * The default data directory 
+     * 
+     * @var string 
+     */
+    private $dataDir;
+
+    public function __construct($dataDir) {
+
+        $this->dataDir = $dataDir;
+
+    }
+
+    protected function getFileNameForUri($uri) {
+
+        return $this->dataDir . '/sabredav_' . md5($uri) . '.locks';
+
+    }
+
+
+    /**
+     * Returns a list of Sabre_DAV_Locks_LockInfo objects  
+     * 
+     * This method should return all the locks for a particular uri, including
+     * locks that might be set on a parent uri.
+     *
+     * If returnChildLocks is set to true, this method should also look for
+     * any locks in the subtree of the uri for locks.
+     *
+     * @param string $uri 
+     * @param bool $returnChildLocks
+     * @return array 
+     */
+    public function getLocks($uri, $returnChildLocks) {
+
+        $lockList = array();
+        $currentPath = '';
+
+        foreach(explode('/',$uri) as $uriPart) {
+
+            // weird algorithm that can probably be improved, but we're traversing the path top down 
+            if ($currentPath) $currentPath.='/'; 
+            $currentPath.=$uriPart;
+
+            $uriLocks = $this->getData($currentPath);
+
+            foreach($uriLocks as $uriLock) {
+
+                // Unless we're on the leaf of the uri-tree we should ingore locks with depth 0
+                if($uri==$currentPath || $uriLock->depth!=0) {
+                    $uriLock->uri = $currentPath;
+                    $lockList[] = $uriLock;
+                }
+
+            }
+
+        }
+
+        // Checking if we can remove any of these locks
+        foreach($lockList as $k=>$lock) {
+            if (time() > $lock->timeout + $lock->created) unset($lockList[$k]); 
+        }
+        return $lockList;
+
+    }
+
+    /**
+     * Locks a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        // We're making the lock timeout 30 minutes
+        $lockInfo->timeout = 1800;
+        $lockInfo->created = time();
+
+        $locks = $this->getLocks($uri,false);
+        foreach($locks as $k=>$lock) {
+            if ($lock->token == $lockInfo->token) unset($locks[$k]);
+        }
+        $locks[] = $lockInfo;
+        $this->putData($uri,$locks);
+        return true;
+
+    }
+
+    /**
+     * Removes a lock from a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        $locks = $this->getLocks($uri,false);
+        foreach($locks as $k=>$lock) {
+
+            if ($lock->token == $lockInfo->token) {
+
+                unset($locks[$k]);
+                $this->putData($uri,$locks);
+                return true;
+
+            }
+        }
+        return false;
+
+    }
+
+    /**
+     * Returns the stored data for a uri
+     *
+     * @param string $uri
+     * @return array 
+     */
+    protected function getData($uri) {
+
+        $path = $this->getFilenameForUri($uri);
+        if (!file_exists($path)) return array();
+
+        // opening up the file, and creating a shared lock
+        $handle = fopen($path,'r');
+        flock($handle,LOCK_SH);
+        $data = '';
+
+        // Reading data until the eof
+        while(!feof($handle)) {
+            $data.=fread($handle,8192);
+        }
+
+        // We're all good
+        fclose($handle);
+
+        // Unserializing and checking if the resource file contains data for this file
+        $data = unserialize($data);
+        if (!$data) return array();
+        return $data;
+
+    }
+
+    /**
+     * Updates the lock information
+     *
+     * @param string $uri
+     * @param array $newData 
+     * @return void
+     */
+    protected function putData($uri,array $newData) {
+
+        $path = $this->getFileNameForUri($uri);
+
+        // opening up the file, and creating a shared lock
+        $handle = fopen($path,'a+');
+        flock($handle,LOCK_EX);
+        ftruncate($handle,0);
+        rewind($handle);
+
+        fwrite($handle,serialize($newData));
+        fclose($handle);
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/File.php b/3rdparty/Sabre/DAV/Locks/Backend/File.php
new file mode 100644
index 0000000000000000000000000000000000000000..f65b20c4306728f93cd9c00968db46d6008f816c
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/File.php
@@ -0,0 +1,175 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This Lock Manager stores all its data in a single file. 
+ *
+ * Note that this is not nearly as robust as a database, you are encouraged
+ * to use the PDO backend instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Backend_File extends Sabre_DAV_Locks_Backend_Abstract {
+
+    /**
+     * The storage file
+     * 
+     * @var string 
+     */
+    private $locksFile;
+
+    /**
+     * Constructor
+     *
+     * @param string $locksFile path to file 
+     */
+    public function __construct($locksFile) {
+
+        $this->locksFile = $locksFile;
+
+    }
+
+    /**
+     * Returns a list of Sabre_DAV_Locks_LockInfo objects  
+     * 
+     * This method should return all the locks for a particular uri, including
+     * locks that might be set on a parent uri.
+     *
+     * If returnChildLocks is set to true, this method should also look for
+     * any locks in the subtree of the uri for locks.
+     *
+     * @param string $uri 
+     * @param bool $returnChildLocks
+     * @return array 
+     */
+    public function getLocks($uri, $returnChildLocks) {
+
+        $newLocks = array();
+        $currentPath = '';
+
+        $locks = $this->getData();
+        foreach($locks as $lock) {
+
+            if ($lock->uri === $uri ||
+                //deep locks on parents
+                ($lock->depth!=0 && strpos($uri, $lock->uri . '/')===0) ||
+
+                // locks on children
+                ($returnChildLocks && (strpos($lock->uri, $uri . '/')===0)) ) {
+
+                $newLocks[] = $lock;
+
+            }
+
+        }
+
+        // Checking if we can remove any of these locks
+        foreach($newLocks as $k=>$lock) {
+            if (time() > $lock->timeout + $lock->created) unset($newLocks[$k]); 
+        }
+        return $newLocks;
+
+    }
+
+    /**
+     * Locks a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        // We're making the lock timeout 30 minutes
+        $lockInfo->timeout = 1800;
+        $lockInfo->created = time();
+        $lockInfo->uri = $uri;
+
+        $locks = $this->getLocks($uri,false);
+        foreach($locks as $k=>$lock) {
+            if ($lock->token == $lockInfo->token) unset($locks[$k]);
+        }
+        $locks[] = $lockInfo;
+        $this->putData($locks);
+        return true;
+
+    }
+
+    /**
+     * Removes a lock from a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        $locks = $this->getLocks($uri,false);
+        foreach($locks as $k=>$lock) {
+
+            if ($lock->token == $lockInfo->token) {
+
+                unset($locks[$k]);
+                $this->putData($locks);
+                return true;
+
+            }
+        }
+        return false;
+
+    }
+
+    /**
+     * Loads the lockdata from the filesystem.
+     *
+     * @return array 
+     */
+    protected function getData() {
+
+        if (!file_exists($this->locksFile)) return array();
+
+        // opening up the file, and creating a shared lock
+        $handle = fopen($this->locksFile,'r');
+        flock($handle,LOCK_SH);
+
+        // Reading data until the eof
+        $data = stream_get_contents($handle);
+
+        // We're all good
+        fclose($handle);
+
+        // Unserializing and checking if the resource file contains data for this file
+        $data = unserialize($data);
+        if (!$data) return array();
+        return $data;
+
+    }
+
+    /**
+     * Saves the lockdata
+     *
+     * @param array $newData 
+     * @return void
+     */
+    protected function putData(array $newData) {
+
+        // opening up the file, and creating an exclusive lock
+        $handle = fopen($this->locksFile,'a+');
+        flock($handle,LOCK_EX);
+
+        // We can only truncate and rewind once the lock is acquired.
+        ftruncate($handle,0);
+        rewind($handle);
+
+        fwrite($handle,serialize($newData));
+        fclose($handle);
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Backend/PDO.php b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3923af19d3ea6d5eae4235711b175ef24672d71
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Backend/PDO.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This Lock Manager stores all its data in a database. You must pass a PDO
+ * connection object in the constructor.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Backend_PDO extends Sabre_DAV_Locks_Backend_Abstract {
+
+    /**
+     * The PDO connection object 
+     * 
+     * @var pdo 
+     */
+    private $pdo;
+
+    /**
+     * The PDO tablename this backend uses. 
+     * 
+     * @var string
+     */
+    protected $tableName;
+
+    /**
+     * Constructor 
+     * 
+     * @param PDO $pdo
+     * @param string $tableName 
+     */
+    public function __construct(PDO $pdo, $tableName = 'locks') {
+
+        $this->pdo = $pdo;
+        $this->tableName = $tableName;
+
+    }
+
+    /**
+     * Returns a list of Sabre_DAV_Locks_LockInfo objects  
+     * 
+     * This method should return all the locks for a particular uri, including
+     * locks that might be set on a parent uri.
+     *
+     * If returnChildLocks is set to true, this method should also look for
+     * any locks in the subtree of the uri for locks.
+     *
+     * @param string $uri 
+     * @param bool $returnChildLocks
+     * @return array 
+     */
+    public function getLocks($uri, $returnChildLocks) {
+
+        // NOTE: the following 10 lines or so could be easily replaced by 
+        // pure sql. MySQL's non-standard string concatination prevents us
+        // from doing this though.
+        $query = 'SELECT owner, token, timeout, created, scope, depth, uri FROM `'.$this->tableName.'` WHERE ((created + timeout) > CAST(? AS UNSIGNED INTEGER)) AND ((uri = ?)';
+        $params = array(time(),$uri);
+
+        // We need to check locks for every part in the uri.
+        $uriParts = explode('/',$uri);
+
+        // We already covered the last part of the uri
+        array_pop($uriParts);
+
+        $currentPath='';
+
+        foreach($uriParts as $part) {
+
+            if ($currentPath) $currentPath.='/';
+            $currentPath.=$part;
+
+            $query.=' OR (depth!=0 AND uri = ?)';
+            $params[] = $currentPath;
+
+        }
+
+        if ($returnChildLocks) {
+
+            $query.=' OR (uri LIKE ?)';
+            $params[] = $uri . '/%';
+
+        }
+        $query.=')';
+
+        $stmt = $this->pdo->prepare($query);
+        $stmt->execute($params);
+        $result = $stmt->fetchAll();
+
+        $lockList = array();
+        foreach($result as $row) {
+
+            $lockInfo = new Sabre_DAV_Locks_LockInfo();
+            $lockInfo->owner = $row['owner'];
+            $lockInfo->token = $row['token'];
+            $lockInfo->timeout = $row['timeout'];
+            $lockInfo->created = $row['created'];
+            $lockInfo->scope = $row['scope'];
+            $lockInfo->depth = $row['depth'];
+            $lockInfo->uri   = $row['uri'];
+            $lockList[] = $lockInfo;
+
+        }
+
+        return $lockList;
+
+    }
+
+    /**
+     * Locks a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        // We're making the lock timeout 30 minutes
+        $lockInfo->timeout = 30*60;
+        $lockInfo->created = time();
+        $lockInfo->uri = $uri;
+
+        $locks = $this->getLocks($uri,false);
+        $exists = false;
+        foreach($locks as $k=>$lock) {
+            if ($lock->token == $lockInfo->token) $exists = true;
+        }
+        
+        if ($exists) {
+            $stmt = $this->pdo->prepare('UPDATE `'.$this->tableName.'` SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE token = ?');
+            $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+        } else {
+            $stmt = $this->pdo->prepare('INSERT INTO `'.$this->tableName.'` (owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?)');
+            $stmt->execute(array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+        }
+
+        return true;
+
+    }
+
+
+
+    /**
+     * Removes a lock from a uri 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return bool 
+     */
+    public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->tableName.'` WHERE uri = ? AND token = ?');
+        $stmt->execute(array($uri,$lockInfo->token));
+
+        return $stmt->rowCount()===1;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/LockInfo.php b/3rdparty/Sabre/DAV/Locks/LockInfo.php
new file mode 100644
index 0000000000000000000000000000000000000000..6a064466f4015303ca802b44b05d2617e33ce0ad
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/LockInfo.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * LockInfo class
+ *
+ * An object of the LockInfo class holds all the information relevant to a
+ * single lock.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_LockInfo {
+
+    /**
+     * A shared lock
+     */
+    const SHARED = 1;
+
+    /**
+     * An exclusive lock
+     */
+    const EXCLUSIVE = 2;
+
+    /**
+     * A never expiring timeout
+     */
+    const TIMEOUT_INFINITE = -1;
+
+    /**
+     * The owner of the lock 
+     * 
+     * @var string 
+     */
+    public $owner;
+
+    /**
+     * The locktoken 
+     * 
+     * @var string 
+     */
+    public $token;
+
+    /**
+     * How long till the lock is expiring 
+     * 
+     * @var int 
+     */
+    public $timeout;
+
+    /**
+     * UNIX Timestamp of when this lock was created 
+     * 
+     * @var int 
+     */
+    public $created;
+
+    /**
+     * Exclusive or shared lock 
+     * 
+     * @var int 
+     */
+    public $scope = self::EXCLUSIVE;
+
+    /**
+     * Depth of lock, can be 0 or Sabre_DAV_Server::DEPTH_INFINITY
+     */
+    public $depth = 0;
+
+    /**
+     * The uri this lock locks
+     *
+     * TODO: This value is not always set 
+     * @var mixed
+     */
+    public $uri;
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Locks/Plugin.php b/3rdparty/Sabre/DAV/Locks/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..461e2847e0ae8e562827294eb4d550b19b3b4530
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Locks/Plugin.php
@@ -0,0 +1,680 @@
+<?php
+
+/**
+ * Locking plugin
+ *
+ * This plugin provides locking support to a WebDAV server.
+ * The easiest way to get started, is by hooking it up as such:
+ *
+ * $lockBackend = new Sabre_DAV_Locks_Backend_File('./mylockdb');
+ * $lockPlugin = new Sabre_DAV_Locks_Plugin($lockBackend);
+ * $server->addPlugin($lockPlugin);
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Locks_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * locksBackend 
+     * 
+     * @var Sabre_DAV_Locks_Backend_Abstract 
+     */
+    private $locksBackend;
+
+    /**
+     * server
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * __construct 
+     * 
+     * @param Sabre_DAV_Locks_Backend_Abstract $locksBackend 
+     * @return void
+     */
+    public function __construct(Sabre_DAV_Locks_Backend_Abstract $locksBackend = null) {
+
+        $this->locksBackend = $locksBackend;        
+
+    }
+
+    /**
+     * Initializes the plugin
+     *
+     * This method is automatically called by the Server class after addPlugin.
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $server->subscribeEvent('unknownMethod',array($this,'unknownMethod'));
+        $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'),50);
+        $server->subscribeEvent('afterGetProperties',array($this,'afterGetProperties'));
+
+    }
+
+    /**
+     * Returns a plugin name.
+     * 
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
+     * 
+     * @return string 
+     */
+    public function getPluginName() {
+
+        return 'locks';
+
+    }
+
+    /**
+     * This method is called by the Server if the user used an HTTP method 
+     * the server didn't recognize.
+     *
+     * This plugin intercepts the LOCK and UNLOCK methods.
+     * 
+     * @param string $method 
+     * @return bool 
+     */
+    public function unknownMethod($method, $uri) {
+
+        switch($method) { 
+
+            case 'LOCK'   : $this->httpLock($uri); return false; 
+            case 'UNLOCK' : $this->httpUnlock($uri); return false; 
+
+        }
+
+    }
+
+    /**
+     * This method is called after most properties have been found
+     * it allows us to add in any Lock-related properties
+     * 
+     * @param string $path 
+     * @param array $properties 
+     * @return bool 
+     */
+    public function afterGetProperties($path,&$newProperties) {
+
+        foreach($newProperties[404] as $propName=>$discard) {
+
+            $node = null;
+
+            switch($propName) {
+
+                case '{DAV:}supportedlock' :
+                    $val = false;
+                    if ($this->locksBackend) $val = true;
+                    else {
+                        if (!$node) $node = $this->server->tree->getNodeForPath($path);
+                        if ($node instanceof Sabre_DAV_ILockable) $val = true;
+                    }
+                    $newProperties[200][$propName] = new Sabre_DAV_Property_SupportedLock($val);
+                    unset($newProperties[404][$propName]);
+                    break;
+
+                case '{DAV:}lockdiscovery' :
+                    $newProperties[200][$propName] = new Sabre_DAV_Property_LockDiscovery($this->getLocks($path));
+                    unset($newProperties[404][$propName]);
+                    break;
+
+            }
+
+
+        }
+        return true;
+
+    }
+
+
+    /**
+     * This method is called before the logic for any HTTP method is
+     * handled.
+     *
+     * This plugin uses that feature to intercept access to locked resources.
+     * 
+     * @param string $method
+     * @param string $uri
+     * @return bool 
+     */
+    public function beforeMethod($method, $uri) {
+
+        switch($method) {
+
+            case 'DELETE' :
+                $lastLock = null;
+                if (!$this->validateLock($uri,$lastLock, true))
+                    throw new Sabre_DAV_Exception_Locked($lastLock);
+                break;
+            case 'MKCOL' :
+            case 'PROPPATCH' :
+            case 'PUT' :
+                $lastLock = null;
+                if (!$this->validateLock($uri,$lastLock))
+                    throw new Sabre_DAV_Exception_Locked($lastLock);
+                break;
+            case 'MOVE' :
+                $lastLock = null;
+                if (!$this->validateLock(array(
+                      $uri,
+                      $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
+                    ),$lastLock, true))
+                        throw new Sabre_DAV_Exception_Locked($lastLock);
+                break;
+            case 'COPY' :
+                $lastLock = null;
+                if (!$this->validateLock(
+                      $this->server->calculateUri($this->server->httpRequest->getHeader('Destination')),
+                      $lastLock, true))
+                        throw new Sabre_DAV_Exception_Locked($lastLock);
+                break;
+        }
+
+        return true;
+
+    }
+
+    /**
+     * Use this method to tell the server this plugin defines additional
+     * HTTP methods.
+     *
+     * This method is passed a uri. It should only return HTTP methods that are 
+     * available for the specified uri.
+     *
+     * @param string $uri
+     * @return array 
+     */
+    public function getHTTPMethods($uri) {
+
+        if ($this->locksBackend ||
+            $this->server->tree->getNodeForPath($uri) instanceof Sabre_DAV_ILocks) {
+            return array('LOCK','UNLOCK');
+        }
+        return array();
+
+    }
+
+    /**
+     * Returns a list of features for the HTTP OPTIONS Dav: header.
+     *
+     * In this case this is only the number 2. The 2 in the Dav: header
+     * indicates the server supports locks.
+     * 
+     * @return array 
+     */
+    public function getFeatures() {
+
+        return array(2);
+
+    }
+
+    /**
+     * Returns all lock information on a particular uri 
+     * 
+     * This function should return an array with Sabre_DAV_Locks_LockInfo objects. If there are no locks on a file, return an empty array.
+     *
+     * Additionally there is also the possibility of locks on parent nodes, so we'll need to traverse every part of the tree 
+     * If the $returnChildLocks argument is set to true, we'll also traverse all the children of the object
+     * for any possible locks and return those as well.
+     *
+     * @param string $uri 
+     * @param bool $returnChildLocks
+     * @return array 
+     */
+    public function getLocks($uri, $returnChildLocks = false) {
+
+        $lockList = array();
+        $currentPath = '';
+        foreach(explode('/',$uri) as $uriPart) {
+
+            $uriLocks = array();
+            if ($currentPath) $currentPath.='/'; 
+            $currentPath.=$uriPart;
+
+            try {
+
+                $node = $this->server->tree->getNodeForPath($currentPath);
+                if ($node instanceof Sabre_DAV_ILockable) $uriLocks = $node->getLocks();
+
+            } catch (Sabre_DAV_Exception_FileNotFound $e){
+                // In case the node didn't exist, this could be a lock-null request
+            }
+
+            foreach($uriLocks as $uriLock) {
+
+                // Unless we're on the leaf of the uri-tree we should ignore locks with depth 0
+                if($uri==$currentPath || $uriLock->depth!=0) {
+                    $uriLock->uri = $currentPath;
+                    $lockList[] = $uriLock;
+                }
+
+            }
+
+        }
+        if ($this->locksBackend) 
+            $lockList = array_merge($lockList,$this->locksBackend->getLocks($uri, $returnChildLocks));
+
+        return $lockList;
+
+    }
+
+    /**
+     * Locks an uri
+     *
+     * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock
+     * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type 
+     * of lock (shared or exclusive) and the owner of the lock
+     *
+     * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock
+     *
+     * Additionally, a lock can be requested for a non-existant file. In these case we're obligated to create an empty file as per RFC4918:S7.3
+     * 
+     * @param string $uri
+     * @return void
+     */
+    protected function httpLock($uri) {
+
+        $lastLock = null;
+        if (!$this->validateLock($uri,$lastLock)) {
+
+            // If the existing lock was an exclusive lock, we need to fail
+            if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) {
+                //var_dump($lastLock);
+                throw new Sabre_DAV_Exception_ConflictingLock($lastLock);
+            }
+
+        }
+
+        if ($body = $this->server->httpRequest->getBody(true)) {
+            // This is a new lock request
+            $lockInfo = $this->parseLockRequest($body);
+            $lockInfo->depth = $this->server->getHTTPDepth(); 
+            $lockInfo->uri = $uri;
+            if($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) throw new Sabre_DAV_Exception_ConflictingLock($lastLock);
+
+        } elseif ($lastLock) {
+
+            // This must have been a lock refresh
+            $lockInfo = $lastLock;
+
+            // The resource could have been locked through another uri. 
+            if ($uri!=$lockInfo->uri) $uri = $lockInfo->uri;
+
+        } else {
+            
+            // There was neither a lock refresh nor a new lock request
+            throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests');
+
+        }
+
+        if ($timeout = $this->getTimeoutHeader()) $lockInfo->timeout = $timeout;
+
+        $newFile = false;
+
+        // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first
+        try {
+            $node = $this->server->tree->getNodeForPath($uri);
+            
+            // We need to call the beforeWriteContent event for RFC3744
+            $this->server->broadcastEvent('beforeWriteContent',array($uri));
+
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+            
+            // It didn't, lets create it
+            $this->server->createFile($uri,fopen('php://memory','r')); 
+            $newFile = true; 
+
+        }
+
+        $this->lockNode($uri,$lockInfo);
+
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->setHeader('Lock-Token','<opaquelocktoken:' . $lockInfo->token . '>');
+        $this->server->httpResponse->sendStatus($newFile?201:200);
+        $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo));
+
+    }
+
+    /**
+     * Unlocks a uri
+     *
+     * This WebDAV method allows you to remove a lock from a node. The client should provide a valid locktoken through the Lock-token http header
+     * The server should return 204 (No content) on success
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpUnlock($uri) {
+
+        $lockToken = $this->server->httpRequest->getHeader('Lock-Token');
+
+        // If the locktoken header is not supplied, we need to throw a bad request exception
+        if (!$lockToken) throw new Sabre_DAV_Exception_BadRequest('No lock token was supplied');
+
+        $locks = $this->getLocks($uri);
+
+        // Windows sometimes forgets to include < and > in the Lock-Token 
+        // header
+        if ($lockToken[0]!=='<') $lockToken = '<' . $lockToken . '>';
+
+        foreach($locks as $lock) {
+
+            if ('<opaquelocktoken:' . $lock->token . '>' == $lockToken) {
+
+                $this->server->broadcastEvent('beforeUnlock',array($uri, $lock));
+                $this->unlockNode($uri,$lock);
+                $this->server->httpResponse->setHeader('Content-Length','0');
+                $this->server->httpResponse->sendStatus(204);
+                return;
+
+            }
+
+        }
+
+        // If we got here, it means the locktoken was invalid
+        throw new Sabre_DAV_Exception_LockTokenMatchesRequestUri();
+
+    }
+
+    /**
+     * Locks a uri
+     *
+     * All the locking information is supplied in the lockInfo object. The object has a suggested timeout, but this can be safely ignored
+     * It is important that if the existing timeout is ignored, the property is overwritten, as this needs to be sent back to the client
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return void
+     */
+    public function lockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        if (!$this->server->broadcastEvent('beforeLock',array($uri,$lockInfo))) return;
+
+        try {
+            $node = $this->server->tree->getNodeForPath($uri);
+            if ($node instanceof Sabre_DAV_ILockable) return $node->lock($lockInfo);
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+            // In case the node didn't exist, this could be a lock-null request
+        }
+        if ($this->locksBackend) return $this->locksBackend->lock($uri,$lockInfo);
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Locking support is not enabled for this resource. No Locking backend was found so if you didn\'t expect this error, please check your configuration.');
+
+    }
+
+    /**
+     * Unlocks a uri
+     *
+     * This method removes a lock from a uri. It is assumed all the supplied information is correct and verified
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return void
+     */
+    public function unlockNode($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        if (!$this->server->broadcastEvent('beforeUnlock',array($uri,$lockInfo))) return;
+        try {
+            $node = $this->server->tree->getNodeForPath($uri);
+            if ($node instanceof Sabre_DAV_ILockable) return $node->unlock($lockInfo);
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+            // In case the node didn't exist, this could be a lock-null request
+        }
+
+        if ($this->locksBackend) return $this->locksBackend->unlock($uri,$lockInfo);
+
+    }
+
+
+    /**
+     * Returns the contents of the HTTP Timeout header. 
+     * 
+     * The method formats the header into an integer.
+     *
+     * @return int
+     */
+    public function getTimeoutHeader() {
+
+        $header = $this->server->httpRequest->getHeader('Timeout');
+        
+        if ($header) {
+
+            if (stripos($header,'second-')===0) $header = (int)(substr($header,7));
+            else if (strtolower($header)=='infinite') $header=Sabre_DAV_Locks_LockInfo::TIMEOUT_INFINITE;
+            else throw new Sabre_DAV_Exception_BadRequest('Invalid HTTP timeout header');
+
+        } else {
+
+            $header = 0;
+
+        }
+
+        return $header;
+
+    }
+
+    /**
+     * Generates the response for successfull LOCK requests 
+     * 
+     * @param Sabre_DAV_Locks_LockInfo $lockInfo 
+     * @return string 
+     */
+    protected function generateLockResponse(Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+        $dom = new DOMDocument('1.0','utf-8');
+        $dom->formatOutput = true;
+        
+        $prop = $dom->createElementNS('DAV:','d:prop');
+        $dom->appendChild($prop);
+
+        $lockDiscovery = $dom->createElementNS('DAV:','d:lockdiscovery');
+        $prop->appendChild($lockDiscovery);
+
+        $lockObj = new Sabre_DAV_Property_LockDiscovery(array($lockInfo),true);
+        $lockObj->serialize($this->server,$lockDiscovery);
+
+        return $dom->saveXML();
+
+    }
+    
+    /**
+     * validateLock should be called when a write operation is about to happen
+     * It will check if the requested url is locked, and see if the correct lock tokens are passed 
+     *
+     * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri
+     * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo)
+     * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees.
+     * @return bool
+     */
+    protected function validateLock($urls = null,&$lastLock = null, $checkChildLocks = false) {
+
+        if (is_null($urls)) {
+            $urls = array($this->server->getRequestUri());
+        } elseif (is_string($urls)) {
+            $urls = array($urls);
+        } elseif (!is_array($urls)) {
+            throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array');
+        }
+
+        $conditions = $this->getIfConditions();
+
+        // We're going to loop through the urls and make sure all lock conditions are satisfied
+        foreach($urls as $url) {
+
+            $locks = $this->getLocks($url, $checkChildLocks);
+
+            // If there were no conditions, but there were locks, we fail 
+            if (!$conditions && $locks) {
+                reset($locks);
+                $lastLock = current($locks);
+                return false;
+            }
+          
+            // If there were no locks or conditions, we go to the next url
+            if (!$locks && !$conditions) continue;
+
+            foreach($conditions as $condition) {
+
+                if (!$condition['uri']) {
+                    $conditionUri = $this->server->getRequestUri();
+                } else {
+                    $conditionUri = $this->server->calculateUri($condition['uri']);
+                }
+
+                // If the condition has a url, and it isn't part of the affected url at all, check the next condition
+                if ($conditionUri && strpos($url,$conditionUri)!==0) continue;
+
+                // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken
+                // At least 1 condition has to be satisfied
+                foreach($condition['tokens'] as $conditionToken) {
+
+                    $etagValid = true;
+                    $lockValid  = true;
+
+                    // key 2 can contain an etag
+                    if ($conditionToken[2]) {
+
+                        $uri = $conditionUri?$conditionUri:$this->server->getRequestUri(); 
+                        $node = $this->server->tree->getNodeForPath($uri);
+                        $etagValid = $node->getETag()==$conditionToken[2];
+
+                    }
+
+                    // key 1 can contain a lock token
+                    if ($conditionToken[1]) {
+
+                        $lockValid = false;
+                        // Match all the locks
+                        foreach($locks as $lockIndex=>$lock) {
+
+                            $lockToken = 'opaquelocktoken:' . $lock->token;
+
+                            // Checking NOT
+                            if (!$conditionToken[0] && $lockToken != $conditionToken[1]) {
+
+                                // Condition valid, onto the next
+                                $lockValid = true;
+                                break;
+                            }
+                            if ($conditionToken[0] && $lockToken == $conditionToken[1]) {
+
+                                $lastLock = $lock;
+                                // Condition valid and lock matched
+                                unset($locks[$lockIndex]);
+                                $lockValid = true;
+                                break;
+
+                            }
+
+                        }
+
+                    }
+
+                    // If, after checking both etags and locks they are stil valid,
+                    // we can continue with the next condition.
+                    if ($etagValid && $lockValid) continue 2;
+               }
+               // No conditions matched, so we fail
+               throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match','If');
+            }
+
+            // Conditions were met, we'll also need to check if all the locks are gone
+            if (count($locks)) {
+
+                reset($locks);
+
+                // There's still locks, we fail
+                $lastLock = current($locks);
+                return false;
+
+            }
+
+
+        }
+
+        // We got here, this means every condition was satisfied
+        return true;
+
+    }
+
+    /**
+     * This method is created to extract information from the WebDAV HTTP 'If:' header
+     *
+     * The If header can be quite complex, and has a bunch of features. We're using a regex to extract all relevant information
+     * The function will return an array, containg structs with the following keys
+     *
+     *   * uri   - the uri the condition applies to. If this is returned as an 
+     *     empty string, this implies it's referring to the request url.
+     *   * tokens - The lock token. another 2 dimensional array containg 2 elements (0 = true/false.. If this is a negative condition its set to false, 1 = the actual token)
+     *   * etag - an etag, if supplied
+     * 
+     * @return void
+     */
+    public function getIfConditions() {
+
+        $header = $this->server->httpRequest->getHeader('If'); 
+        if (!$header) return array();
+
+        $matches = array();
+
+        $regex = '/(?:\<(?P<uri>.*?)\>\s)?\((?P<not>Not\s)?(?:\<(?P<token>[^\>]*)\>)?(?:\s?)(?:\[(?P<etag>[^\]]*)\])?\)/im'; 
+        preg_match_all($regex,$header,$matches,PREG_SET_ORDER);
+
+        $conditions = array();
+
+        foreach($matches as $match) {
+
+            $condition = array(
+                'uri'   => $match['uri'],
+                'tokens' => array(
+                    array($match['not']?0:1,$match['token'],isset($match['etag'])?$match['etag']:'')
+                ),    
+            );
+
+            if (!$condition['uri'] && count($conditions)) $conditions[count($conditions)-1]['tokens'][] = array(
+                $match['not']?0:1,
+                $match['token'],
+                isset($match['etag'])?$match['etag']:''
+            );
+            else {
+                $conditions[] = $condition;
+            }
+
+        }
+
+        return $conditions;
+
+    }
+
+    /**
+     * Parses a webdav lock xml body, and returns a new Sabre_DAV_Locks_LockInfo object 
+     * 
+     * @param string $body 
+     * @return Sabre_DAV_Locks_LockInfo
+     */
+    protected function parseLockRequest($body) {
+
+        $xml = simplexml_load_string($body,null,LIBXML_NOWARNING);
+        $xml->registerXPathNamespace('d','DAV:');
+        $lockInfo = new Sabre_DAV_Locks_LockInfo();
+
+        $children = $xml->children("DAV:");
+        $lockInfo->owner = (string)$children->owner;
+
+        $lockInfo->token = Sabre_DAV_UUIDUtil::getUUID();
+        $lockInfo->scope = count($xml->xpath('d:lockscope/d:exclusive'))>0?Sabre_DAV_Locks_LockInfo::EXCLUSIVE:Sabre_DAV_Locks_LockInfo::SHARED;
+
+        return $lockInfo;
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/DAV/Mount/Plugin.php b/3rdparty/Sabre/DAV/Mount/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..f93a1aa25a1f00793e848d2fdb752ab092be785d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Mount/Plugin.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * This plugin provides support for RFC4709: Mounting WebDAV servers
+ *
+ * Simply append ?mount to any collection to generate the davmount response.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ */
+class Sabre_DAV_Mount_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Reference to Server class 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * Initializes the plugin and registers event handles 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $this->server->subscribeEvent('beforeMethod',array($this,'beforeMethod'), 90);
+
+    }
+
+    /**
+     * 'beforeMethod' event handles. This event handles intercepts GET requests ending
+     * with ?mount
+     * 
+     * @param string $method 
+     * @return void
+     */
+    public function beforeMethod($method, $uri) {
+
+        if ($method!='GET') return;
+        if ($this->server->httpRequest->getQueryString()!='mount') return;
+
+        $currentUri = $this->server->httpRequest->getAbsoluteUri();
+
+        // Stripping off everything after the ?
+        list($currentUri) = explode('?',$currentUri);
+
+        $this->davMount($currentUri);
+
+        // Returning false to break the event chain
+        return false;
+
+    }
+
+    /**
+     * Generates the davmount response 
+     * 
+     * @param string $uri absolute uri 
+     * @return void
+     */
+    public function davMount($uri) {
+       
+        $this->server->httpResponse->sendStatus(200);
+        $this->server->httpResponse->setHeader('Content-Type','application/davmount+xml');
+        ob_start();
+        echo '<?xml version="1.0"?>', "\n";
+        echo "<dm:mount xmlns:dm=\"http://purl.org/NET/webdav/mount\">\n";
+        echo "  <dm:url>", htmlspecialchars($uri, ENT_NOQUOTES, 'UTF-8'), "</dm:url>\n";
+        echo "</dm:mount>";
+        $this->server->httpResponse->sendBody(ob_get_clean());
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/DAV/Node.php b/3rdparty/Sabre/DAV/Node.php
new file mode 100644
index 0000000000000000000000000000000000000000..0510df5fdf28156bd1941cce78f1734992377308
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Node.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * Node class
+ *
+ * This is a helper class, that should aid in getting nodes setup. 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Node implements Sabre_DAV_INode {
+
+    /**
+     * Returns the last modification time 
+     *
+     * In this case, it will simply return the current time
+     *
+     * @return int 
+     */
+    public function getLastModified() {
+
+        return time();
+
+    }
+
+    /**
+     * Deleted the current node
+     *
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @return void 
+     */
+    public function delete() {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to delete node');
+
+    }
+
+    /**
+     * Renames the node
+     * 
+     * @throws Sabre_DAV_Exception_Forbidden
+     * @param string $name The new name
+     * @return void
+     */
+    public function setName($name) {
+
+        throw new Sabre_DAV_Exception_Forbidden('Permission denied to rename file');
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ObjectTree.php b/3rdparty/Sabre/DAV/ObjectTree.php
new file mode 100644
index 0000000000000000000000000000000000000000..f12a36837052c058d4a35e7ae30a0c48d4aeb13e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ObjectTree.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * ObjectTree class
+ *
+ * This implementation of the Tree class makes use of the INode, IFile and ICollection API's 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_ObjectTree extends Sabre_DAV_Tree {
+
+    /**
+     * The root node 
+     * 
+     * @var Sabre_DAV_ICollection
+     */
+    protected $rootNode;
+
+    /**
+     * This is the node cache. Accessed nodes are stored here 
+     * 
+     * @var array 
+     */
+    protected $cache = array();
+
+    /**
+     * Creates the object
+     *
+     * This method expects the rootObject to be passed as a parameter
+     * 
+     * @param Sabre_DAV_ICollection $rootNode 
+     * @return void
+     */
+    public function __construct(Sabre_DAV_ICollection $rootNode) {
+
+        $this->rootNode = $rootNode;
+
+    }
+
+    /**
+     * Returns the INode object for the requested path  
+     * 
+     * @param string $path 
+     * @return Sabre_DAV_INode 
+     */
+    public function getNodeForPath($path) {
+
+        $path = trim($path,'/');
+        if (isset($this->cache[$path])) return $this->cache[$path];
+
+        //if (!$path || $path=='.') return $this->rootNode;
+        $currentNode = $this->rootNode;
+        $i=0;
+        // We're splitting up the path variable into folder/subfolder components and traverse to the correct node.. 
+        foreach(explode('/',$path) as $pathPart) {
+
+            // If this part of the path is just a dot, it actually means we can skip it
+            if ($pathPart=='.' || $pathPart=='') continue;
+
+            if (!($currentNode instanceof Sabre_DAV_ICollection))
+                throw new Sabre_DAV_Exception_FileNotFound('Could not find node at path: ' . $path);
+
+            $currentNode = $currentNode->getChild($pathPart); 
+
+        }
+
+        $this->cache[$path] = $currentNode;
+        return $currentNode;
+
+    }
+
+    /**
+     * This function allows you to check if a node exists.
+     *
+     * @param string $path 
+     * @return bool 
+     */
+    public function nodeExists($path) {
+
+        try {
+
+            // The root always exists
+            if ($path==='') return true;
+
+            list($parent, $base) = Sabre_DAV_URLUtil::splitPath($path);
+
+            $parentNode = $this->getNodeForPath($parent);
+            if (!$parentNode instanceof Sabre_DAV_ICollection) return false;
+            return $parentNode->childExists($base);
+
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+
+            return false;
+
+        }
+
+    }
+
+    /**
+     * Returns a list of childnodes for a given path. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    public function getChildren($path) {
+
+        $node = $this->getNodeForPath($path);
+        $children = $node->getChildren();
+        foreach($children as $child) {
+
+            $this->cache[trim($path,'/') . '/' . $child->getName()] = $child;
+
+        }
+        return $children;
+
+    }
+
+    /**
+     * This method is called with every tree update
+     *
+     * Examples of tree updates are:
+     *   * node deletions
+     *   * node creations
+     *   * copy
+     *   * move
+     *   * renaming nodes 
+     * 
+     * If Tree classes implement a form of caching, this will allow
+     * them to make sure caches will be expired.
+     * 
+     * If a path is passed, it is assumed that the entire subtree is dirty
+     *
+     * @param string $path 
+     * @return void
+     */
+    public function markDirty($path) {
+
+        // We don't care enough about sub-paths
+        // flushing the entire cache
+        $path = trim($path,'/');
+        foreach($this->cache as $nodePath=>$node) {
+            if ($nodePath == $path || strpos($nodePath,$path.'/')===0)
+                unset($this->cache[$nodePath]);
+            
+        }
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property.php b/3rdparty/Sabre/DAV/Property.php
new file mode 100644
index 0000000000000000000000000000000000000000..577535b012769b418a41f58487dd8c1718551a2e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * Abstract property class
+ *
+ * Extend this class to create custom complex properties
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Property {
+
+    abstract function serialize(Sabre_DAV_Server $server, DOMElement $prop); 
+
+    static function unserialize(DOMElement $prop) {
+
+        throw new Sabre_DAV_Exception('Unserialize has not been implemented for this class');
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/GetLastModified.php b/3rdparty/Sabre/DAV/Property/GetLastModified.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a81262997148025ecc68f28d55c243c8f6e0f1a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/GetLastModified.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * This property represents the {DAV:}getlastmodified property.
+ * 
+ * Although this is normally a simple property, windows requires us to add
+ * some new attributes.
+ *
+ * This class uses unix timestamps internally, and converts them to RFC 1123 times for 
+ * serialization
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_GetLastModified extends Sabre_DAV_Property {
+
+    /**
+     * time 
+     * 
+     * @var int 
+     */
+    public $time;
+
+    /**
+     * __construct 
+     * 
+     * @param int|DateTime $time 
+     * @return void
+     */
+    public function __construct($time) {
+
+        if ($time instanceof DateTime) {
+            $this->time = $time;
+        } elseif (is_int($time) || ctype_digit($time)) {
+            $this->time = new DateTime('@' . $time);
+        } else {
+            $this->time = new DateTime($time);
+        }
+
+        // Setting timezone to UTC
+        $this->time->setTimezone(new DateTimeZone('UTC'));
+
+    }
+
+    /**
+     * serialize 
+     * 
+     * @param DOMElement $prop 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server, DOMElement $prop) {
+
+        $doc = $prop->ownerDocument;
+        $prop->setAttribute('xmlns:b','urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/');
+        $prop->setAttribute('b:dt','dateTime.rfc1123');
+        $prop->nodeValue = $this->time->format(DateTime::RFC1123);
+
+    }
+
+    /**
+     * getTime 
+     * 
+     * @return DateTime 
+     */
+    public function getTime() {
+
+        return $this->time;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/Href.php b/3rdparty/Sabre/DAV/Property/Href.php
new file mode 100644
index 0000000000000000000000000000000000000000..3294ff2ac6848fcf0564af191c10c2a7e9cd010b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/Href.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * Href property
+ *
+ * The href property represpents a url within a {DAV:}href element.
+ * This is used by many WebDAV extensions, but not really within the WebDAV core spec
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_Href extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
+
+    /**
+     * href 
+     * 
+     * @var string 
+     */
+    private $href;
+
+    /**
+     * Automatically prefix the url with the server base directory 
+     * 
+     * @var bool 
+     */
+    private $autoPrefix = true;
+
+    /**
+     * __construct 
+     * 
+     * @param string $href 
+     * @return void
+     */
+    public function __construct($href, $autoPrefix = true) {
+
+        $this->href = $href;
+        $this->autoPrefix = $autoPrefix;
+
+    }
+
+    /**
+     * Returns the uri 
+     * 
+     * @return string 
+     */
+    public function getHref() {
+
+        return $this->href;
+
+    }
+
+    /**
+     * Serializes this property.
+     *
+     * It will additionally prepend the href property with the server's base uri.
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $dom 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
+
+        $prefix = $server->xmlNamespaces['DAV:'];
+
+        $elem = $dom->ownerDocument->createElement($prefix . ':href');
+        $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $this->href;
+        $dom->appendChild($elem);
+
+    }
+
+    /**
+     * Unserializes this property from a DOM Element 
+     *
+     * This method returns an instance of this class.
+     * It will only decode {DAV:}href values. For non-compatible elements null will be returned.
+     *
+     * @param DOMElement $dom 
+     * @return Sabre_DAV_Property_Href 
+     */
+    static function unserialize(DOMElement $dom) {
+
+        if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)==='{DAV:}href') {
+            return new self($dom->firstChild->textContent,false);
+        }
+
+    } 
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/HrefList.php b/3rdparty/Sabre/DAV/Property/HrefList.php
new file mode 100644
index 0000000000000000000000000000000000000000..76a5512901c6ded74e7ccfc33d2f1460dfcae8d3
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/HrefList.php
@@ -0,0 +1,96 @@
+<?php
+
+/**
+ * HrefList property
+ *
+ * This property contains multiple {DAV:}href elements, each containing a url. 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_HrefList extends Sabre_DAV_Property {
+
+    /**
+     * hrefs 
+     * 
+     * @var array 
+     */
+    private $hrefs;
+
+    /**
+     * Automatically prefix the url with the server base directory 
+     * 
+     * @var bool 
+     */
+    private $autoPrefix = true;
+
+    /**
+     * __construct 
+     * 
+     * @param array $hrefs
+     * @param bool $autoPrefix 
+     */
+    public function __construct(array $hrefs, $autoPrefix = true) {
+
+        $this->hrefs = $hrefs;
+        $this->autoPrefix = $autoPrefix;
+
+    }
+
+    /**
+     * Returns the uris 
+     * 
+     * @return array 
+     */
+    public function getHrefs() {
+
+        return $this->hrefs;
+
+    }
+
+    /**
+     * Serializes this property.
+     *
+     * It will additionally prepend the href property with the server's base uri.
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $dom 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
+
+        $prefix = $server->xmlNamespaces['DAV:'];
+
+        foreach($this->hrefs as $href) {
+            $elem = $dom->ownerDocument->createElement($prefix . ':href');
+            $elem->nodeValue = ($this->autoPrefix?$server->getBaseUri():'') . $href;
+            $dom->appendChild($elem);
+        }
+
+    }
+
+    /**
+     * Unserializes this property from a DOM Element 
+     *
+     * This method returns an instance of this class.
+     * It will only decode {DAV:}href values.
+     *
+     * @param DOMElement $dom 
+     * @return Sabre_DAV_Property_Href 
+     */
+    static function unserialize(DOMElement $dom) {
+
+        $hrefs = array();
+        foreach($dom->childNodes as $child) {
+            if (Sabre_DAV_XMLUtil::toClarkNotation($child)==='{DAV:}href') {
+                $hrefs[] = $child->textContent;
+            }
+        }
+        return new self($hrefs, false);
+
+    } 
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/IHref.php b/3rdparty/Sabre/DAV/Property/IHref.php
new file mode 100644
index 0000000000000000000000000000000000000000..29d76a44fcd3fb5cbcc143c829c72b07eeb53b3a
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/IHref.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * IHref interface
+ *
+ * Any property implementing this interface can expose a related url.
+ * This is used by certain subsystems to aquire more information about for example
+ * the owner of a file
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAV_Property_IHref {
+
+    /**
+     * getHref 
+     * 
+     * @return string 
+     */
+    function getHref();
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/LockDiscovery.php b/3rdparty/Sabre/DAV/Property/LockDiscovery.php
new file mode 100644
index 0000000000000000000000000000000000000000..05c7470b4ed86f61448c47f6f11f6f76b74190e7
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/LockDiscovery.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * Represents {DAV:}lockdiscovery property
+ *
+ * This property contains all the open locks on a given resource
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_LockDiscovery extends Sabre_DAV_Property {
+
+    /**
+     * locks 
+     * 
+     * @var array 
+     */
+    public $locks;
+ 
+    /**
+     * Should we show the locktoken as well? 
+     * 
+     * @var bool 
+     */
+    public $revealLockToken;
+
+    /**
+     * Hides the {DAV:}lockroot element from the response.
+     *
+     * It was reported that showing the lockroot in the response can break
+     * Office 2000 compatibility.
+     */
+    static public $hideLockRoot = false;
+
+    /**
+     * __construct 
+     * 
+     * @param array $locks 
+     * @param bool $revealLockToken 
+     * @return void
+     */
+    public function __construct($locks,$revealLockToken = false) {
+
+        $this->locks = $locks;
+        $this->revealLockToken = $revealLockToken;
+
+    }
+
+    /**
+     * serialize 
+     * 
+     * @param DOMElement $prop 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
+
+        $doc = $prop->ownerDocument;
+
+        foreach($this->locks as $lock) {
+
+            $activeLock = $doc->createElementNS('DAV:','d:activelock');
+            $prop->appendChild($activeLock);
+
+            $lockScope = $doc->createElementNS('DAV:','d:lockscope');
+            $activeLock->appendChild($lockScope);
+
+            $lockScope->appendChild($doc->createElementNS('DAV:','d:' . ($lock->scope==Sabre_DAV_Locks_LockInfo::EXCLUSIVE?'exclusive':'shared')));
+
+            $lockType = $doc->createElementNS('DAV:','d:locktype');
+            $activeLock->appendChild($lockType);
+
+            $lockType->appendChild($doc->createElementNS('DAV:','d:write'));
+
+            /* {DAV:}lockroot */ 
+            if (!self::$hideLockRoot) {
+                $lockRoot = $doc->createElementNS('DAV:','d:lockroot');
+                $activeLock->appendChild($lockRoot);
+                $href = $doc->createElementNS('DAV:','d:href');
+                $href->appendChild($doc->createTextNode($server->getBaseUri() . $lock->uri));
+                $lockRoot->appendChild($href);
+            }
+
+            $activeLock->appendChild($doc->createElementNS('DAV:','d:depth',($lock->depth == Sabre_DAV_Server::DEPTH_INFINITY?'infinity':$lock->depth)));
+            $activeLock->appendChild($doc->createElementNS('DAV:','d:timeout','Second-' . $lock->timeout));
+
+            if ($this->revealLockToken) {
+                $lockToken = $doc->createElementNS('DAV:','d:locktoken');
+                $activeLock->appendChild($lockToken);
+                $lockToken->appendChild($doc->createElementNS('DAV:','d:href','opaquelocktoken:' . $lock->token));
+            }
+           
+            $activeLock->appendChild($doc->createElementNS('DAV:','d:owner',$lock->owner));
+
+        }
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/ResourceType.php b/3rdparty/Sabre/DAV/Property/ResourceType.php
new file mode 100644
index 0000000000000000000000000000000000000000..2c606c22d60de7c84e4711a3e8c00f8355436ab4
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/ResourceType.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * This class represents the {DAV:}resourcetype property
+ *
+ * Normally for files this is empty, and for collection {DAV:}collection.
+ * However, other specs define different values for this. 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_ResourceType extends Sabre_DAV_Property {
+
+    /**
+     * resourceType 
+     * 
+     * @var array
+     */
+    public $resourceType = array();
+
+    /**
+     * __construct 
+     * 
+     * @param mixed $resourceType 
+     * @return void
+     */
+    public function __construct($resourceType = array()) {
+
+        if ($resourceType === Sabre_DAV_Server::NODE_FILE)
+            $this->resourceType = array();
+        elseif ($resourceType === Sabre_DAV_Server::NODE_DIRECTORY)
+            $this->resourceType = array('{DAV:}collection');
+        elseif (is_array($resourceType)) 
+            $this->resourceType = $resourceType;
+        else
+            $this->resourceType = array($resourceType);
+
+    }
+
+    /**
+     * serialize 
+     * 
+     * @param DOMElement $prop 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
+
+        $propName = null;
+        $rt = $this->resourceType;
+        
+        foreach($rt as $resourceType) {
+            if (preg_match('/^{([^}]*)}(.*)$/',$resourceType,$propName)) { 
+       
+                if (isset($server->xmlNamespaces[$propName[1]])) {
+                    $prop->appendChild($prop->ownerDocument->createElement($server->xmlNamespaces[$propName[1]] . ':' . $propName[2]));
+                } else {
+                    $prop->appendChild($prop->ownerDocument->createElementNS($propName[1],'custom:' . $propName[2]));
+                }
+            
+            }
+        }
+
+    }
+
+    /**
+     * Returns the values in clark-notation
+     *
+     * For example array('{DAV:}collection')
+     * 
+     * @return array 
+     */
+    public function getValue() {
+
+        return $this->resourceType;
+
+    }
+
+    /**
+     * Checks if the principal contains a certain value 
+     * 
+     * @param string $type 
+     * @return bool 
+     */
+    public function is($type) {
+
+        return in_array($type, $this->resourceType);
+
+    }
+
+    /**
+     * Adds a resourcetype value to this property
+     *
+     * @param string $type
+     * @return void
+     */
+    public function add($type) {
+
+        $this->resourceType[] = $type;
+        $this->resourceType = array_unique($this->resourceType);
+
+    }
+
+    /**
+     * Unserializes a DOM element into a ResourceType property. 
+     * 
+     * @param DOMElement $dom 
+     * @return void
+     */
+    static public function unserialize(DOMElement $dom) {
+
+        $value = array();
+        foreach($dom->childNodes as $child) {
+
+            $value[] = Sabre_DAV_XMLUtil::toClarkNotation($child);
+
+        }
+
+        return new self($value);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/Response.php b/3rdparty/Sabre/DAV/Property/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..7d3a2db0387d190966a801d0488a69f3e610be5e
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/Response.php
@@ -0,0 +1,156 @@
+<?php
+
+/**
+ * Response property 
+ * 
+ * This class represents the {DAV:}response XML element.
+ * This is used by the Server class to encode individual items within a multistatus 
+ * response.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_Response extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
+
+    /**
+     * Url for the response 
+     * 
+     * @var string 
+     */
+    private $href;
+
+    /**
+     * Propertylist, ordered by HTTP status code 
+     * 
+     * @var array 
+     */
+    private $responseProperties;
+
+    /**
+     * The responseProperties argument is a list of properties
+     * within an array with keys representing HTTP status codes
+     * 
+     * @param string $href 
+     * @param array $responseProperties 
+     * @return void
+     */
+    public function __construct($href,array $responseProperties) {
+
+        $this->href = $href;
+        $this->responseProperties = $responseProperties; 
+
+    }
+
+    /**
+     * Returns the url 
+     * 
+     * @return string 
+     */
+    public function getHref() {
+
+        return $this->href;
+
+    }
+
+    /**
+     * Returns the property list 
+     * 
+     * @return array 
+     */
+    public function getResponseProperties() {
+
+        return $this->responseProperties;
+
+    }
+
+    /**
+     * serialize 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $dom 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
+
+        $document = $dom->ownerDocument;
+        $properties = $this->responseProperties;
+        
+        $xresponse = $document->createElement('d:response');
+        $dom->appendChild($xresponse); 
+
+        $uri = Sabre_DAV_URLUtil::encodePath($this->href);
+
+        // Adding the baseurl to the beginning of the url
+        $uri = $server->getBaseUri() . $uri;
+
+        $xresponse->appendChild($document->createElement('d:href',$uri));
+       
+        // The properties variable is an array containing properties, grouped by
+        // HTTP status
+        foreach($properties as $httpStatus=>$propertyGroup) {
+
+            // The 'href' is also in this array, and it's special cased.
+            // We will ignore it
+            if ($httpStatus=='href') continue;
+
+            // If there are no properties in this group, we can also just carry on
+            if (!count($propertyGroup)) continue;
+
+            $xpropstat = $document->createElement('d:propstat');
+            $xresponse->appendChild($xpropstat);
+
+            $xprop = $document->createElement('d:prop');
+            $xpropstat->appendChild($xprop);
+
+            $nsList = $server->xmlNamespaces;
+
+            foreach($propertyGroup as $propertyName=>$propertyValue) {
+
+                $propName = null;
+                preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
+            
+                // special case for empty namespaces
+                if ($propName[1]=='') {
+
+                    $currentProperty = $document->createElement($propName[2]);
+                    $xprop->appendChild($currentProperty);
+                    $currentProperty->setAttribute('xmlns','');
+
+                } else {
+
+                    if (!isset($nsList[$propName[1]])) {
+                        $nsList[$propName[1]] = 'x' . count($nsList);
+                    }
+
+                    // If the namespace was defined in the top-level xml namespaces, it means 
+                    // there was already a namespace declaration, and we don't have to worry about it.
+                    if (isset($server->xmlNamespaces[$propName[1]])) {
+                        $currentProperty = $document->createElement($nsList[$propName[1]] . ':' . $propName[2]);
+                    } else {
+                        $currentProperty = $document->createElementNS($propName[1],$nsList[$propName[1]].':' . $propName[2]);
+                    }
+                    $xprop->appendChild($currentProperty);
+
+                }
+
+                if (is_scalar($propertyValue)) {
+                    $text = $document->createTextNode($propertyValue);
+                    $currentProperty->appendChild($text);
+                } elseif ($propertyValue instanceof Sabre_DAV_Property) {
+                    $propertyValue->serialize($server,$currentProperty);
+                } elseif (!is_null($propertyValue)) {
+                    throw new Sabre_DAV_Exception('Unknown property value type: ' . gettype($propertyValue) . ' for property: ' . $propertyName);
+                }
+
+            }
+
+            $xpropstat->appendChild($document->createElement('d:status',$server->httpResponse->getStatusMessage($httpStatus)));
+
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/ResponseList.php b/3rdparty/Sabre/DAV/Property/ResponseList.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd70b12861d05130f1cf0f399bdf74b2be710109
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/ResponseList.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * ResponseList property 
+ * 
+ * This class represents multiple {DAV:}response XML elements.
+ * This is used by the Server class to encode items within a multistatus 
+ * response.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_ResponseList extends Sabre_DAV_Property {
+
+    /**
+     * Response objects. 
+     * 
+     * @var array 
+     */
+    private $responses;
+
+    /**
+     * The only valid argument is a list of Sabre_DAV_Property_Response 
+     * objects.
+     * 
+     * @param array $responses; 
+     * @return void
+     */
+    public function __construct($responses) {
+
+        foreach($responses as $response) {
+            if (!($response instanceof Sabre_DAV_Property_Response)) {
+                throw new InvalidArgumentException('You must pass an array of Sabre_DAV_Property_Response objects');
+            }
+        }
+        $this->responses = $responses;
+
+    }
+
+    /**
+     * serialize 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $dom 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $dom) {
+
+        foreach($this->responses as $response) {
+            $response->serialize($server, $dom);
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Property/SupportedLock.php b/3rdparty/Sabre/DAV/Property/SupportedLock.php
new file mode 100644
index 0000000000000000000000000000000000000000..01e63f58d9d5136a4f4b9aa5734d8acfdfd4b231
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/SupportedLock.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * This class represents the {DAV:}supportedlock property
+ *
+ * This property contains information about what kind of locks
+ * this server supports.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_SupportedLock extends Sabre_DAV_Property {
+
+    /**
+     * supportsLocks 
+     * 
+     * @var mixed
+     */
+    public $supportsLocks = false;
+
+    /**
+     * __construct 
+     * 
+     * @param mixed $supportsLocks 
+     * @return void
+     */
+    public function __construct($supportsLocks) {
+
+        $this->supportsLocks = $supportsLocks;
+
+    }
+
+    /**
+     * serialize 
+     * 
+     * @param DOMElement $prop 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
+
+        $doc = $prop->ownerDocument;
+
+        if (!$this->supportsLocks) return null;
+
+        $lockEntry1 = $doc->createElementNS('DAV:','d:lockentry');
+        $lockEntry2 = $doc->createElementNS('DAV:','d:lockentry');
+
+        $prop->appendChild($lockEntry1);
+        $prop->appendChild($lockEntry2);
+
+        $lockScope1 = $doc->createElementNS('DAV:','d:lockscope');
+        $lockScope2 = $doc->createElementNS('DAV:','d:lockscope');
+        $lockType1 = $doc->createElementNS('DAV:','d:locktype');
+        $lockType2 = $doc->createElementNS('DAV:','d:locktype');
+
+        $lockEntry1->appendChild($lockScope1);
+        $lockEntry1->appendChild($lockType1);
+        $lockEntry2->appendChild($lockScope2);
+        $lockEntry2->appendChild($lockType2);
+
+        $lockScope1->appendChild($doc->createElementNS('DAV:','d:exclusive'));
+        $lockScope2->appendChild($doc->createElementNS('DAV:','d:shared'));
+
+        $lockType1->appendChild($doc->createElementNS('DAV:','d:write'));
+        $lockType2->appendChild($doc->createElementNS('DAV:','d:write'));
+
+        //$frag->appendXML('<d:lockentry><d:lockscope><d:exclusive /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
+        //$frag->appendXML('<d:lockentry><d:lockscope><d:shared /></d:lockscope><d:locktype><d:write /></d:locktype></d:lockentry>');
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Property/SupportedReportSet.php b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php
new file mode 100644
index 0000000000000000000000000000000000000000..acd9219c0f70656392622d700aebcba38f81e52c
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Property/SupportedReportSet.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * supported-report-set property.
+ *
+ * This property is defined in RFC3253, but since it's
+ * so common in other webdav-related specs, it is part of the core server.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Property_SupportedReportSet extends Sabre_DAV_Property {
+
+    /**
+     * List of reports 
+     * 
+     * @var array
+     */
+    protected $reports = array();
+
+    /**
+     * Creates the property
+     *
+     * Any reports passed in the constructor
+     * should be valid report-types in clark-notation.
+     *
+     * Either a string or an array of strings must be passed.
+     * 
+     * @param mixed $reports 
+     * @return void
+     */
+    public function __construct($reports = null) {
+
+        if (!is_null($reports)) 
+            $this->addReport($reports);
+
+    }
+
+    /**
+     * Adds a report to this property
+     *
+     * The report must be a string in clark-notation.
+     * Multiple reports can be specified as an array.
+     * 
+     * @param mixed $report 
+     * @return void
+     */
+    public function addReport($report) {
+
+        if (!is_array($report)) $report = array($report);
+
+        foreach($report as $r) {
+
+            if (!preg_match('/^{([^}]*)}(.*)$/',$r)) 
+                throw new Sabre_DAV_Exception('Reportname must be in clark-notation');
+
+            $this->reports[] = $r;
+
+        }
+
+    }
+
+    /**
+     * Returns the list of supported reports
+     * 
+     * @return array 
+     */
+    public function getValue() {
+
+        return $this->reports;
+
+    }
+
+    /**
+     * Serializes the node 
+     *
+     * @param Sabre_DAV_Server $server
+     * @param DOMElement $prop 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $prop) {
+
+        foreach($this->reports as $reportName) {
+     
+            $supportedReport = $prop->ownerDocument->createElement('d:supported-report');
+            $prop->appendChild($supportedReport);
+
+            $report = $prop->ownerDocument->createElement('d:report');
+            $supportedReport->appendChild($report);
+
+            preg_match('/^{([^}]*)}(.*)$/',$reportName,$matches);
+            
+            list(, $namespace, $element) = $matches;
+       
+            $prefix = isset($server->xmlNamespaces[$namespace])?$server->xmlNamespaces[$namespace]:null;
+
+            if ($prefix) {
+                $report->appendChild($prop->ownerDocument->createElement($prefix . ':' . $element));
+            } else {
+                $report->appendChild($prop->ownerDocument->createElementNS($namespace, 'x:' . $element));
+            }
+
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Server.php b/3rdparty/Sabre/DAV/Server.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6c63143d13572c0b1487f429d23b7246b1266b0
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Server.php
@@ -0,0 +1,1906 @@
+<?php
+
+/**
+ * Main DAV server class
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Server {
+
+    /**
+     * Inifinity is used for some request supporting the HTTP Depth header and indicates that the operation should traverse the entire tree
+     */
+    const DEPTH_INFINITY = -1;
+
+    /**
+     * Nodes that are files, should have this as the type property
+     */
+    const NODE_FILE = 1;
+
+    /**
+     * Nodes that are directories, should use this value as the type property
+     */
+    const NODE_DIRECTORY = 2;
+
+    const PROP_SET = 1;
+    const PROP_REMOVE = 2;
+
+    /**
+     * XML namespace for all SabreDAV related elements
+     */
+    const NS_SABREDAV = 'http://sabredav.org/ns';
+
+    /**
+     * The tree object
+     * 
+     * @var Sabre_DAV_Tree 
+     */
+    public $tree;
+
+    /**
+     * The base uri 
+     * 
+     * @var string 
+     */
+    protected $baseUri = null; 
+
+    /**
+     * httpResponse 
+     * 
+     * @var Sabre_HTTP_Response 
+     */
+    public $httpResponse;
+
+    /**
+     * httpRequest
+     * 
+     * @var Sabre_HTTP_Request 
+     */
+    public $httpRequest;
+
+    /**
+     * The list of plugins 
+     * 
+     * @var array 
+     */
+    protected $plugins = array();
+
+    /**
+     * This array contains a list of callbacks we should call when certain events are triggered 
+     * 
+     * @var array
+     */
+    protected $eventSubscriptions = array();
+
+    /**
+     * This is a default list of namespaces.
+     *
+     * If you are defining your own custom namespace, add it here to reduce
+     * bandwidth and improve legibility of xml bodies.
+     * 
+     * @var array
+     */
+    public $xmlNamespaces = array(
+        'DAV:' => 'd',
+        'http://sabredav.org/ns' => 's',
+    );
+
+    /**
+     * The propertymap can be used to map properties from 
+     * requests to property classes.
+     * 
+     * @var array
+     */
+    public $propertyMap = array(
+        '{DAV:}resourcetype' => 'Sabre_DAV_Property_ResourceType',
+    );
+
+    public $protectedProperties = array(
+        // RFC4918
+        '{DAV:}getcontentlength',
+        '{DAV:}getetag',
+        '{DAV:}getlastmodified',
+        '{DAV:}lockdiscovery',
+        '{DAV:}resourcetype',
+        '{DAV:}supportedlock',
+
+        // RFC4331
+        '{DAV:}quota-available-bytes',
+        '{DAV:}quota-used-bytes',
+
+        // RFC3744
+        '{DAV:}supported-privilege-set',
+        '{DAV:}current-user-privilege-set',
+        '{DAV:}acl',
+        '{DAV:}acl-restrictions',
+        '{DAV:}inherited-acl-set',
+
+    );
+
+    /**
+     * This is a flag that allow or not showing file, line and code
+     * of the exception in the returned XML
+     *
+     * @var bool 
+     */
+    public $debugExceptions = false;
+
+    /**
+     * This property allows you to automatically add the 'resourcetype' value 
+     * based on a node's classname or interface.
+     *
+     * The preset ensures that {DAV:}collection is automaticlly added for nodes 
+     * implementing Sabre_DAV_ICollection.
+     * 
+     * @var array
+     */
+    public $resourceTypeMapping = array(
+        'Sabre_DAV_ICollection' => '{DAV:}collection',
+    );
+
+
+    /**
+     * Sets up the server
+     *
+     * If a Sabre_DAV_Tree object is passed as an argument, it will
+     * use it as the directory tree. If a Sabre_DAV_INode is passed, it
+     * will create a Sabre_DAV_ObjectTree and use the node as the root.
+     *
+     * If nothing is passed, a Sabre_DAV_SimpleCollection is created in 
+     * a Sabre_DAV_ObjectTree.
+     *
+     * If an array is passed, we automatically create a root node, and use
+     * the nodes in the array as top-level children. 
+     * 
+     * @param Sabre_DAV_Tree $tree The tree object 
+     * @return void
+     */
+    public function __construct($treeOrNode = null) {
+
+        if ($treeOrNode instanceof Sabre_DAV_Tree) {
+            $this->tree = $treeOrNode;
+        } elseif ($treeOrNode instanceof Sabre_DAV_INode) {
+            $this->tree = new Sabre_DAV_ObjectTree($treeOrNode);
+        } elseif (is_array($treeOrNode)) {
+
+            // If it's an array, a list of nodes was passed, and we need to
+            // create the root node.
+            foreach($treeOrNode as $node) {
+                if (!($node instanceof Sabre_DAV_INode)) {
+                    throw new Sabre_DAV_Exception('Invalid argument passed to constructor. If you\'re passing an array, all the values must implement Sabre_DAV_INode');
+                }
+            }
+
+            $root = new Sabre_DAV_SimpleCollection('root', $treeOrNode);
+            $this->tree = new Sabre_DAV_ObjectTree($root);
+
+        } elseif (is_null($treeOrNode)) {
+            $root = new Sabre_DAV_SimpleCollection('root');
+            $this->tree = new Sabre_DAV_ObjectTree($root);
+        } else {
+            throw new Sabre_DAV_Exception('Invalid argument passed to constructor. Argument must either be an instance of Sabre_DAV_Tree, Sabre_DAV_INode, an array or null');
+        }
+        $this->httpResponse = new Sabre_HTTP_Response();
+        $this->httpRequest = new Sabre_HTTP_Request();
+
+    }
+
+    /**
+     * Starts the DAV Server 
+     *
+     * @return void
+     */
+    public function exec() {
+
+        try {
+
+            $this->invokeMethod($this->httpRequest->getMethod(), $this->getRequestUri());
+
+        } catch (Exception $e) {
+
+            $DOM = new DOMDocument('1.0','utf-8');
+            $DOM->formatOutput = true;
+
+            $error = $DOM->createElementNS('DAV:','d:error');
+            $error->setAttribute('xmlns:s',self::NS_SABREDAV);
+            $DOM->appendChild($error);
+
+            $error->appendChild($DOM->createElement('s:exception',get_class($e)));
+            $error->appendChild($DOM->createElement('s:message',$e->getMessage()));
+            if ($this->debugExceptions) {
+                $error->appendChild($DOM->createElement('s:file',$e->getFile()));
+                $error->appendChild($DOM->createElement('s:line',$e->getLine()));
+                $error->appendChild($DOM->createElement('s:code',$e->getCode()));
+                $error->appendChild($DOM->createElement('s:stacktrace',$e->getTraceAsString()));
+
+            }
+            $error->appendChild($DOM->createElement('s:sabredav-version',Sabre_DAV_Version::VERSION));
+
+            if($e instanceof Sabre_DAV_Exception) {
+
+                $httpCode = $e->getHTTPCode();
+                $e->serialize($this,$error);
+                $headers = $e->getHTTPHeaders($this);
+
+            } else {
+
+                $httpCode = 500;
+                $headers = array();
+
+            }
+            $headers['Content-Type'] = 'application/xml; charset=utf-8';
+            
+            $this->httpResponse->sendStatus($httpCode);
+            $this->httpResponse->setHeaders($headers);
+            $this->httpResponse->sendBody($DOM->saveXML());
+
+        }
+
+    }
+
+    /**
+     * Sets the base server uri
+     * 
+     * @param string $uri
+     * @return void
+     */
+    public function setBaseUri($uri) {
+
+        // If the baseUri does not end with a slash, we must add it
+        if ($uri[strlen($uri)-1]!=='/') 
+            $uri.='/';
+
+        $this->baseUri = $uri;    
+
+    }
+
+    /**
+     * Returns the base responding uri
+     * 
+     * @return string 
+     */
+    public function getBaseUri() {
+
+        if (is_null($this->baseUri)) $this->baseUri = $this->guessBaseUri();
+        return $this->baseUri;
+
+    }
+
+    /**
+     * This method attempts to detect the base uri.
+     * Only the PATH_INFO variable is considered. 
+     * 
+     * If this variable is not set, the root (/) is assumed. 
+     *
+     * @return void
+     */
+    public function guessBaseUri() {
+
+        $pathInfo = $this->httpRequest->getRawServerValue('PATH_INFO');
+        $uri = $this->httpRequest->getRawServerValue('REQUEST_URI');
+
+        // If PATH_INFO is found, we can assume it's accurate.
+        if (!empty($pathInfo)) {
+
+            // We need to make sure we ignore the QUERY_STRING part
+            if ($pos = strpos($uri,'?'))
+                $uri = substr($uri,0,$pos);
+
+            // PATH_INFO is only set for urls, such as: /example.php/path
+            // in that case PATH_INFO contains '/path'.
+            // Note that REQUEST_URI is percent encoded, while PATH_INFO is
+            // not, Therefore they are only comparable if we first decode
+            // REQUEST_INFO as well.
+            $decodedUri = Sabre_DAV_URLUtil::decodePath($uri);
+
+            // A simple sanity check:
+            if(substr($decodedUri,strlen($decodedUri)-strlen($pathInfo))===$pathInfo) {
+                $baseUri = substr($decodedUri,0,strlen($decodedUri)-strlen($pathInfo));
+                return rtrim($baseUri,'/') . '/';
+            }
+
+            throw new Sabre_DAV_Exception('The REQUEST_URI ('. $uri . ') did not end with the contents of PATH_INFO (' . $pathInfo . '). This server might be misconfigured.'); 
+
+        } 
+
+        // The last fallback is that we're just going to assume the server root. 
+        return '/';
+
+    }
+
+    /**
+     * Adds a plugin to the server
+     * 
+     * For more information, console the documentation of Sabre_DAV_ServerPlugin
+     *
+     * @param Sabre_DAV_ServerPlugin $plugin 
+     * @return void
+     */
+    public function addPlugin(Sabre_DAV_ServerPlugin $plugin) {
+
+        $this->plugins[$plugin->getPluginName()] = $plugin;
+        $plugin->initialize($this);
+
+    }
+
+    /**
+     * Returns an initialized plugin by it's name.
+     *
+     * This function returns null if the plugin was not found.
+     *
+     * @param string $name
+     * @return Sabre_DAV_ServerPlugin 
+     */
+    public function getPlugin($name) {
+
+        if (isset($this->plugins[$name])) 
+            return $this->plugins[$name];
+
+        // This is a fallback and deprecated.
+        foreach($this->plugins as $plugin) {
+            if (get_class($plugin)===$name) return $plugin;
+        }
+
+        return null;
+
+    }
+
+    /**
+     * Returns all plugins 
+     * 
+     * @return array 
+     */
+    public function getPlugins() {
+
+        return $this->plugins;
+
+    }
+
+
+
+    /**
+     * Subscribe to an event.
+     *
+     * When the event is triggered, we'll call all the specified callbacks.
+     * It is possible to control the order of the callbacks through the
+     * priority argument.
+     *
+     * This is for example used to make sure that the authentication plugin
+     * is triggered before anything else. If it's not needed to change this
+     * number, it is recommended to ommit.  
+     * 
+     * @param string $event 
+     * @param callback $callback
+     * @param int $priority
+     * @return void
+     */
+    public function subscribeEvent($event, $callback, $priority = 100) {
+
+        if (!isset($this->eventSubscriptions[$event])) {
+            $this->eventSubscriptions[$event] = array();
+        }
+        while(isset($this->eventSubscriptions[$event][$priority])) $priority++;
+        $this->eventSubscriptions[$event][$priority] = $callback;
+        ksort($this->eventSubscriptions[$event]);
+
+    }
+
+    /**
+     * Broadcasts an event
+     *
+     * This method will call all subscribers. If one of the subscribers returns false, the process stops.
+     *
+     * The arguments parameter will be sent to all subscribers
+     *
+     * @param string $eventName
+     * @param array $arguments
+     * @return bool 
+     */
+    public function broadcastEvent($eventName,$arguments = array()) {
+
+        if (isset($this->eventSubscriptions[$eventName])) {
+
+            foreach($this->eventSubscriptions[$eventName] as $subscriber) {
+
+                $result = call_user_func_array($subscriber,$arguments);
+                if ($result===false) return false;
+
+            }
+
+        }
+
+        return true;
+
+    }
+
+    /**
+     * Handles a http request, and execute a method based on its name 
+     *
+     * @param string $method
+     * @param string $uri
+     * @return void
+     */
+    public function invokeMethod($method, $uri) {
+
+        $method = strtoupper($method); 
+
+        if (!$this->broadcastEvent('beforeMethod',array($method, $uri))) return;
+
+        // Make sure this is a HTTP method we support
+        $internalMethods = array(
+            'OPTIONS',
+            'GET',
+            'HEAD',
+            'DELETE',
+            'PROPFIND',
+            'MKCOL',
+            'PUT',
+            'PROPPATCH',
+            'COPY',
+            'MOVE',
+            'REPORT'
+        );
+
+        if (in_array($method,$internalMethods)) {
+
+            call_user_func(array($this,'http' . $method), $uri);
+
+        } else {
+
+            if ($this->broadcastEvent('unknownMethod',array($method, $uri))) {
+                // Unsupported method
+                throw new Sabre_DAV_Exception_NotImplemented();
+            }
+
+        }
+
+    }
+
+    // {{{ HTTP Method implementations
+    
+    /**
+     * HTTP OPTIONS 
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpOptions($uri) {
+
+        $methods = $this->getAllowedMethods($uri);
+
+        $this->httpResponse->setHeader('Allow',strtoupper(implode(', ',$methods)));
+        $features = array('1','3', 'extended-mkcol');
+
+        foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
+        
+        $this->httpResponse->setHeader('DAV',implode(', ',$features));
+        $this->httpResponse->setHeader('MS-Author-Via','DAV');
+        $this->httpResponse->setHeader('Accept-Ranges','bytes');
+        $this->httpResponse->setHeader('X-Sabre-Version',Sabre_DAV_Version::VERSION);
+        $this->httpResponse->setHeader('Content-Length',0);
+        $this->httpResponse->sendStatus(200);
+
+    }
+
+    /**
+     * HTTP GET
+     *
+     * This method simply fetches the contents of a uri, like normal
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpGet($uri) {
+
+        $node = $this->tree->getNodeForPath($uri,0);
+
+        if (!$this->checkPreconditions(true)) return false; 
+
+        if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_NotImplemented('GET is only implemented on File objects');
+        $body = $node->get();
+
+        // Converting string into stream, if needed.
+        if (is_string($body)) {
+            $stream = fopen('php://temp','r+');
+            fwrite($stream,$body);
+            rewind($stream);
+            $body = $stream;
+        }
+
+        /*
+         * TODO: getetag, getlastmodified, getsize should also be used using
+         * this method
+         */
+        $httpHeaders = $this->getHTTPHeaders($uri);
+
+        /* ContentType needs to get a default, because many webservers will otherwise
+         * default to text/html, and we don't want this for security reasons.
+         */
+        if (!isset($httpHeaders['Content-Type'])) {
+            $httpHeaders['Content-Type'] = 'application/octet-stream';
+        }
+
+
+        if (isset($httpHeaders['Content-Length'])) {
+
+            $nodeSize = $httpHeaders['Content-Length'];
+
+            // Need to unset Content-Length, because we'll handle that during figuring out the range
+            unset($httpHeaders['Content-Length']);
+
+        } else {
+            $nodeSize = null;
+        }
+        
+        $this->httpResponse->setHeaders($httpHeaders);
+
+        $range = $this->getHTTPRange();
+        $ifRange = $this->httpRequest->getHeader('If-Range');
+        $ignoreRangeHeader = false;
+
+        // If ifRange is set, and range is specified, we first need to check
+        // the precondition.
+        if ($nodeSize && $range && $ifRange) {
+             
+            // if IfRange is parsable as a date we'll treat it as a DateTime
+            // otherwise, we must treat it as an etag.
+            try {
+                $ifRangeDate = new DateTime($ifRange);
+               
+                // It's a date. We must check if the entity is modified since
+                // the specified date.
+                if (!isset($httpHeaders['Last-Modified'])) $ignoreRangeHeader = true;
+                else {
+                    $modified = new DateTime($httpHeaders['Last-Modified']);
+                    if($modified > $ifRangeDate) $ignoreRangeHeader = true;
+                }
+
+            } catch (Exception $e) {
+               
+                // It's an entity. We can do a simple comparison. 
+                if (!isset($httpHeaders['ETag'])) $ignoreRangeHeader = true;
+                elseif ($httpHeaders['ETag']!==$ifRange) $ignoreRangeHeader = true;
+            }
+        }
+
+        // We're only going to support HTTP ranges if the backend provided a filesize
+        if (!$ignoreRangeHeader && $nodeSize && $range) {
+
+            // Determining the exact byte offsets
+            if (!is_null($range[0])) {
+
+                $start = $range[0];
+                $end = $range[1]?$range[1]:$nodeSize-1;
+                if($start >= $nodeSize) 
+                    throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The start offset (' . $range[0] . ') exceeded the size of the entity (' . $nodeSize . ')');
+
+                if($end < $start) throw new Sabre_DAV_Exception_RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
+                if($end >= $nodeSize) $end = $nodeSize-1;
+
+            } else {
+
+                $start = $nodeSize-$range[1];
+                $end  = $nodeSize-1;
+
+                if ($start<0) $start = 0;
+
+            }
+
+            // New read/write stream
+            $newStream = fopen('php://temp','r+');
+
+            stream_copy_to_stream($body, $newStream, $end-$start+1, $start);
+            rewind($newStream);
+
+            $this->httpResponse->setHeader('Content-Length', $end-$start+1);
+            $this->httpResponse->setHeader('Content-Range','bytes ' . $start . '-' . $end . '/' . $nodeSize);
+            $this->httpResponse->sendStatus(206);
+            $this->httpResponse->sendBody($newStream);
+
+
+        } else {
+
+            if ($nodeSize) $this->httpResponse->setHeader('Content-Length',$nodeSize);
+            $this->httpResponse->sendStatus(200);
+            $this->httpResponse->sendBody($body);
+
+        }
+
+    }
+
+    /**
+     * HTTP HEAD
+     *
+     * This method is normally used to take a peak at a url, and only get the HTTP response headers, without the body
+     * This is used by clients to determine if a remote file was changed, so they can use a local cached version, instead of downloading it again
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpHead($uri) {
+
+        $node = $this->tree->getNodeForPath($uri);
+        /* This information is only collection for File objects.
+         * Ideally we want to throw 405 Method Not Allowed for every 
+         * non-file, but MS Office does not like this
+         */
+        if ($node instanceof Sabre_DAV_IFile) {
+            $headers = $this->getHTTPHeaders($this->getRequestUri());
+            if (!isset($headers['Content-Type'])) {
+                $headers['Content-Type'] = 'application/octet-stream';
+            }
+            $this->httpResponse->setHeaders($headers);
+        }
+        $this->httpResponse->sendStatus(200);
+
+    }
+
+    /**
+     * HTTP Delete 
+     *
+     * The HTTP delete method, deletes a given uri
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpDelete($uri) {
+
+        if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
+        $this->tree->delete($uri);
+
+        $this->httpResponse->sendStatus(204);
+        $this->httpResponse->setHeader('Content-Length','0');
+
+    }
+
+
+    /**
+     * WebDAV PROPFIND 
+     *
+     * This WebDAV method requests information about an uri resource, or a list of resources
+     * If a client wants to receive the properties for a single resource it will add an HTTP Depth: header with a 0 value
+     * If the value is 1, it means that it also expects a list of sub-resources (e.g.: files in a directory)
+     *
+     * The request body contains an XML data structure that has a list of properties the client understands 
+     * The response body is also an xml document, containing information about every uri resource and the requested properties
+     *
+     * It has to return a HTTP 207 Multi-status status code
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpPropfind($uri) {
+
+        // $xml = new Sabre_DAV_XMLReader(file_get_contents('php://input'));
+        $requestedProperties = $this->parsePropfindRequest($this->httpRequest->getBody(true));
+
+        $depth = $this->getHTTPDepth(1);
+        // The only two options for the depth of a propfind is 0 or 1 
+        if ($depth!=0) $depth = 1;
+
+        $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth);
+
+        // This is a multi-status response
+        $this->httpResponse->sendStatus(207);
+        $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+
+        // Normally this header is only needed for OPTIONS responses, however.. 
+        // iCal seems to also depend on these being set for PROPFIND. Since 
+        // this is not harmful, we'll add it.
+        $features = array('1','3', 'extended-mkcol');
+        foreach($this->plugins as $plugin) $features = array_merge($features,$plugin->getFeatures());
+        $this->httpResponse->setHeader('DAV',implode(', ',$features));
+
+        $data = $this->generateMultiStatus($newProperties);
+        $this->httpResponse->sendBody($data);
+
+    }
+
+    /**
+     * WebDAV PROPPATCH
+     *
+     * This method is called to update properties on a Node. The request is an XML body with all the mutations.
+     * In this XML body it is specified which properties should be set/updated and/or deleted
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpPropPatch($uri) {
+
+        $newProperties = $this->parsePropPatchRequest($this->httpRequest->getBody(true));
+        
+        $result = $this->updateProperties($uri, $newProperties);
+
+        $this->httpResponse->sendStatus(207);
+        $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+
+        $this->httpResponse->sendBody(
+            $this->generateMultiStatus(array($result))
+        );
+
+    }
+
+    /**
+     * HTTP PUT method 
+     * 
+     * This HTTP method updates a file, or creates a new one.
+     *
+     * If a new resource was created, a 201 Created status code should be returned. If an existing resource is updated, it's a 200 Ok
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpPut($uri) {
+
+        $body = $this->httpRequest->getBody();
+
+        // Intercepting the Finder problem
+        if (($expected = $this->httpRequest->getHeader('X-Expected-Entity-Length')) && $expected > 0) {
+           
+            /**
+            Many webservers will not cooperate well with Finder PUT requests, 
+            because it uses 'Chunked' transfer encoding for the request body.
+
+            The symptom of this problem is that Finder sends files to the 
+            server, but they arrive as 0-lenght files in PHP.
+
+            If we don't do anything, the user might think they are uploading
+            files successfully, but they end up empty on the server. Instead,
+            we throw back an error if we detect this.
+
+            The reason Finder uses Chunked, is because it thinks the files
+            might change as it's being uploaded, and therefore the
+            Content-Length can vary.
+
+            Instead it sends the X-Expected-Entity-Length header with the size
+            of the file at the very start of the request. If this header is set,
+            but we don't get a request body we will fail the request to
+            protect the end-user.
+            */
+
+            // Only reading first byte
+            $firstByte = fread($body,1);
+            if (strlen($firstByte)!==1) {
+                throw new Sabre_DAV_Exception_Forbidden('This server is not compatible with OS/X finder. Consider using a different WebDAV client or webserver.');
+            }
+
+            // The body needs to stay intact, so we copy everything to a
+            // temporary stream.
+
+            $newBody = fopen('php://temp','r+');
+            fwrite($newBody,$firstByte);
+            stream_copy_to_stream($body, $newBody);
+            rewind($newBody);
+
+            $body = $newBody;
+
+        }
+
+        if ($this->tree->nodeExists($uri)) { 
+
+            $node = $this->tree->getNodeForPath($uri);
+          
+            // Checking If-None-Match and related headers.
+            if (!$this->checkPreconditions()) return;
+            
+            // If the node is a collection, we'll deny it
+            if (!($node instanceof Sabre_DAV_IFile)) throw new Sabre_DAV_Exception_Conflict('PUT is not allowed on non-files.');
+            if (!$this->broadcastEvent('beforeWriteContent',array($this->getRequestUri()))) return false;
+
+            $node->put($body);
+            $this->httpResponse->setHeader('Content-Length','0');
+            $this->httpResponse->sendStatus(200);
+
+        } else {
+
+            // If we got here, the resource didn't exist yet.
+            $this->createFile($this->getRequestUri(),$body);
+            $this->httpResponse->setHeader('Content-Length','0');
+            $this->httpResponse->sendStatus(201);
+
+        }
+
+    }
+
+
+    /**
+     * WebDAV MKCOL
+     *
+     * The MKCOL method is used to create a new collection (directory) on the server
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpMkcol($uri) {
+
+        $requestBody = $this->httpRequest->getBody(true);
+
+        if ($requestBody) {
+
+            $contentType = $this->httpRequest->getHeader('Content-Type');
+            if (strpos($contentType,'application/xml')!==0 && strpos($contentType,'text/xml')!==0) {
+
+                // We must throw 415 for unsupport mkcol bodies
+                throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
+
+            }
+
+            $dom = Sabre_DAV_XMLUtil::loadDOMDocument($requestBody);
+            if (Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild)!=='{DAV:}mkcol') {
+
+                // We must throw 415 for unsupport mkcol bodies
+                throw new Sabre_DAV_Exception_UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.');
+
+            }
+
+            $properties = array();
+            foreach($dom->firstChild->childNodes as $childNode) {
+
+                if (Sabre_DAV_XMLUtil::toClarkNotation($childNode)!=='{DAV:}set') continue;
+                $properties = array_merge($properties, Sabre_DAV_XMLUtil::parseProperties($childNode, $this->propertyMap));
+
+            }
+            if (!isset($properties['{DAV:}resourcetype'])) 
+                throw new Sabre_DAV_Exception_BadRequest('The mkcol request must include a {DAV:}resourcetype property');
+
+            $resourceType = $properties['{DAV:}resourcetype']->getValue();
+            unset($properties['{DAV:}resourcetype']);
+
+        } else {
+
+            $properties = array();
+            $resourceType = array('{DAV:}collection');
+
+        }
+
+        $result = $this->createCollection($uri, $resourceType, $properties);
+
+        if (is_array($result)) {
+            $this->httpResponse->sendStatus(207);
+            $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+
+            $this->httpResponse->sendBody(
+                $this->generateMultiStatus(array($result))
+            );
+
+        } else {
+            $this->httpResponse->setHeader('Content-Length','0');
+            $this->httpResponse->sendStatus(201);
+        }
+
+    }
+
+    /**
+     * WebDAV HTTP MOVE method
+     *
+     * This method moves one uri to a different uri. A lot of the actual request processing is done in getCopyMoveInfo
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpMove($uri) {
+
+        $moveInfo = $this->getCopyAndMoveInfo();
+
+        // If the destination is part of the source tree, we must fail
+        if ($moveInfo['destination']==$uri) 
+            throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.');
+
+        if ($moveInfo['destinationExists']) {
+
+            if (!$this->broadcastEvent('beforeUnbind',array($moveInfo['destination']))) return false;
+            $this->tree->delete($moveInfo['destination']);
+
+        }
+
+        if (!$this->broadcastEvent('beforeUnbind',array($uri))) return false;
+        if (!$this->broadcastEvent('beforeBind',array($moveInfo['destination']))) return false;
+        $this->tree->move($uri,$moveInfo['destination']);
+        $this->broadcastEvent('afterBind',array($moveInfo['destination']));
+
+        // If a resource was overwritten we should send a 204, otherwise a 201
+        $this->httpResponse->setHeader('Content-Length','0');
+        $this->httpResponse->sendStatus($moveInfo['destinationExists']?204:201);
+
+    }
+
+    /**
+     * WebDAV HTTP COPY method
+     *
+     * This method copies one uri to a different uri, and works much like the MOVE request
+     * A lot of the actual request processing is done in getCopyMoveInfo
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpCopy($uri) {
+
+        $copyInfo = $this->getCopyAndMoveInfo();
+        // If the destination is part of the source tree, we must fail
+        if ($copyInfo['destination']==$uri) 
+            throw new Sabre_DAV_Exception_Forbidden('Source and destination uri are identical.');
+
+        if ($copyInfo['destinationExists']) {
+            if (!$this->broadcastEvent('beforeUnbind',array($copyInfo['destination']))) return false;
+            $this->tree->delete($copyInfo['destination']);
+
+        }
+        if (!$this->broadcastEvent('beforeBind',array($copyInfo['destination']))) return false;
+        $this->tree->copy($uri,$copyInfo['destination']);
+        $this->broadcastEvent('afterBind',array($copyInfo['destination']));
+
+        // If a resource was overwritten we should send a 204, otherwise a 201
+        $this->httpResponse->setHeader('Content-Length','0');
+        $this->httpResponse->sendStatus($copyInfo['destinationExists']?204:201);
+
+    }
+
+
+
+    /**
+     * HTTP REPORT method implementation
+     *
+     * Although the REPORT method is not part of the standard WebDAV spec (it's from rfc3253)
+     * It's used in a lot of extensions, so it made sense to implement it into the core.
+     *
+     * @param string $uri
+     * @return void
+     */
+    protected function httpReport($uri) {
+
+        $body = $this->httpRequest->getBody(true);
+        $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+        $reportName = Sabre_DAV_XMLUtil::toClarkNotation($dom->firstChild);
+
+        if ($this->broadcastEvent('report',array($reportName,$dom, $uri))) {
+
+            // If broadcastEvent returned true, it means the report was not supported
+            throw new Sabre_DAV_Exception_ReportNotImplemented();
+
+        }
+
+    }
+
+    // }}}
+    // {{{ HTTP/WebDAV protocol helpers 
+
+    /**
+     * Returns an array with all the supported HTTP methods for a specific uri. 
+     *
+     * @param string $uri 
+     * @return array 
+     */
+    public function getAllowedMethods($uri) {
+
+        $methods = array(
+            'OPTIONS',
+            'GET',
+            'HEAD',
+            'DELETE',
+            'PROPFIND',
+            'PUT',
+            'PROPPATCH',
+            'COPY',
+            'MOVE',
+            'REPORT'
+        );
+
+        // The MKCOL is only allowed on an unmapped uri
+        try {
+            $node = $this->tree->getNodeForPath($uri);
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+            $methods[] = 'MKCOL';
+        }
+
+        // We're also checking if any of the plugins register any new methods
+        foreach($this->plugins as $plugin) $methods = array_merge($methods,$plugin->getHTTPMethods($uri));
+        array_unique($methods);
+
+        return $methods;
+
+    }
+
+    /**
+     * Gets the uri for the request, keeping the base uri into consideration 
+     * 
+     * @return string
+     */
+    public function getRequestUri() {
+
+        return $this->calculateUri($this->httpRequest->getUri());
+
+    }
+
+    /**
+     * Calculates the uri for a request, making sure that the base uri is stripped out 
+     * 
+     * @param string $uri 
+     * @throws Sabre_DAV_Exception_Forbidden A permission denied exception is thrown whenever there was an attempt to supply a uri outside of the base uri
+     * @return string
+     */
+    public function calculateUri($uri) {
+
+        if ($uri[0]!='/' && strpos($uri,'://')) {
+
+            $uri = parse_url($uri,PHP_URL_PATH);
+
+        }
+
+        $uri = str_replace('//','/',$uri);
+
+        if (strpos($uri,$this->getBaseUri())===0) {
+
+            return trim(Sabre_DAV_URLUtil::decodePath(substr($uri,strlen($this->getBaseUri()))),'/');
+
+        // A special case, if the baseUri was accessed without a trailing 
+        // slash, we'll accept it as well. 
+        } elseif ($uri.'/' === $this->getBaseUri()) { 
+
+            return '';
+
+        } else {
+
+            throw new Sabre_DAV_Exception_Forbidden('Requested uri (' . $uri . ') is out of base uri (' . $this->getBaseUri() . ')');
+
+        }
+
+    }
+
+    /**
+     * Returns the HTTP depth header
+     *
+     * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre_DAV_Server::DEPTH_INFINITY object
+     * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existant
+     * 
+     * @param mixed $default 
+     * @return int 
+     */
+    public function getHTTPDepth($default = self::DEPTH_INFINITY) {
+
+        // If its not set, we'll grab the default
+        $depth = $this->httpRequest->getHeader('Depth');
+
+        if (is_null($depth)) return $default;
+
+        if ($depth == 'infinity') return self::DEPTH_INFINITY;
+
+           
+        // If its an unknown value. we'll grab the default
+        if (!ctype_digit($depth)) return $default;
+
+        return (int)$depth;
+
+    }
+
+    /**
+     * Returns the HTTP range header
+     *
+     * This method returns null if there is no well-formed HTTP range request
+     * header or array($start, $end).
+     *
+     * The first number is the offset of the first byte in the range.
+     * The second number is the offset of the last byte in the range.
+     *
+     * If the second offset is null, it should be treated as the offset of the last byte of the entity
+     * If the first offset is null, the second offset should be used to retrieve the last x bytes of the entity 
+     *
+     * return $mixed
+     */
+    public function getHTTPRange() {
+
+        $range = $this->httpRequest->getHeader('range');
+        if (is_null($range)) return null; 
+
+        // Matching "Range: bytes=1234-5678: both numbers are optional
+
+        if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i',$range,$matches)) return null;
+
+        if ($matches[1]==='' && $matches[2]==='') return null;
+
+        return array(
+            $matches[1]!==''?$matches[1]:null,
+            $matches[2]!==''?$matches[2]:null,
+        );
+
+    }
+
+
+    /**
+     * Returns information about Copy and Move requests
+     * 
+     * This function is created to help getting information about the source and the destination for the 
+     * WebDAV MOVE and COPY HTTP request. It also validates a lot of information and throws proper exceptions 
+     * 
+     * The returned value is an array with the following keys:
+     *   * destination - Destination path
+     *   * destinationExists - Wether or not the destination is an existing url (and should therefore be overwritten)
+     *
+     * @return array 
+     */
+    public function getCopyAndMoveInfo() {
+
+        // Collecting the relevant HTTP headers
+        if (!$this->httpRequest->getHeader('Destination')) throw new Sabre_DAV_Exception_BadRequest('The destination header was not supplied');
+        $destination = $this->calculateUri($this->httpRequest->getHeader('Destination'));
+        $overwrite = $this->httpRequest->getHeader('Overwrite');
+        if (!$overwrite) $overwrite = 'T';
+        if (strtoupper($overwrite)=='T') $overwrite = true;
+        elseif (strtoupper($overwrite)=='F') $overwrite = false;
+        // We need to throw a bad request exception, if the header was invalid
+        else throw new Sabre_DAV_Exception_BadRequest('The HTTP Overwrite header should be either T or F');
+
+        list($destinationDir) = Sabre_DAV_URLUtil::splitPath($destination);
+
+        try {
+            $destinationParent = $this->tree->getNodeForPath($destinationDir);
+            if (!($destinationParent instanceof Sabre_DAV_ICollection)) throw new Sabre_DAV_Exception_UnsupportedMediaType('The destination node is not a collection');
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+
+            // If the destination parent node is not found, we throw a 409
+            throw new Sabre_DAV_Exception_Conflict('The destination node is not found');
+        }
+
+        try {
+
+            $destinationNode = $this->tree->getNodeForPath($destination);
+            
+            // If this succeeded, it means the destination already exists
+            // we'll need to throw precondition failed in case overwrite is false
+            if (!$overwrite) throw new Sabre_DAV_Exception_PreconditionFailed('The destination node already exists, and the overwrite header is set to false','Overwrite');
+
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+
+            // Destination didn't exist, we're all good
+            $destinationNode = false;
+
+
+
+        }
+
+        // These are the three relevant properties we need to return
+        return array(
+            'destination'       => $destination,
+            'destinationExists' => $destinationNode==true,
+            'destinationNode'   => $destinationNode,
+        );
+
+    }
+
+    /**
+     * Returns a list of properties for a path
+     *
+     * This is a simplified version getPropertiesForPath.
+     * if you aren't interested in status codes, but you just
+     * want to have a flat list of properties. Use this method.
+     *
+     * @param string $path
+     * @param array $propertyNames
+     */
+    public function getProperties($path, $propertyNames) {
+
+        $result = $this->getPropertiesForPath($path,$propertyNames,0);
+        return $result[0][200];
+
+    }
+
+    /**
+     * Returns a list of HTTP headers for a particular resource
+     *
+     * The generated http headers are based on properties provided by the 
+     * resource. The method basically provides a simple mapping between
+     * DAV property and HTTP header.
+     *
+     * The headers are intended to be used for HEAD and GET requests.
+     * 
+     * @param string $path
+     */
+    public function getHTTPHeaders($path) {
+
+        $propertyMap = array(
+            '{DAV:}getcontenttype'   => 'Content-Type',
+            '{DAV:}getcontentlength' => 'Content-Length',
+            '{DAV:}getlastmodified'  => 'Last-Modified', 
+            '{DAV:}getetag'          => 'ETag',
+        );
+
+        $properties = $this->getProperties($path,array_keys($propertyMap));
+
+        $headers = array();
+        foreach($propertyMap as $property=>$header) {
+            if (!isset($properties[$property])) continue;
+
+            if (is_scalar($properties[$property])) { 
+                $headers[$header] = $properties[$property];
+
+            // GetLastModified gets special cased 
+            } elseif ($properties[$property] instanceof Sabre_DAV_Property_GetLastModified) {
+                $headers[$header] = $properties[$property]->getTime()->format(DateTime::RFC1123);
+            }
+
+        }
+
+        return $headers;
+        
+    }
+
+    /**
+     * Returns a list of properties for a given path
+     * 
+     * The path that should be supplied should have the baseUrl stripped out
+     * The list of properties should be supplied in Clark notation. If the list is empty
+     * 'allprops' is assumed.
+     *
+     * If a depth of 1 is requested child elements will also be returned.
+     *
+     * @param string $path 
+     * @param array $propertyNames
+     * @param int $depth 
+     * @return array
+     */
+    public function getPropertiesForPath($path,$propertyNames = array(),$depth = 0) {
+
+        if ($depth!=0) $depth = 1;
+
+        $returnPropertyList = array();
+        
+        $parentNode = $this->tree->getNodeForPath($path);
+        $nodes = array(
+            $path => $parentNode
+        );
+        if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) {
+            foreach($this->tree->getChildren($path) as $childNode)
+                $nodes[$path . '/' . $childNode->getName()] = $childNode;
+        }            
+       
+        // If the propertyNames array is empty, it means all properties are requested.
+        // We shouldn't actually return everything we know though, and only return a
+        // sensible list. 
+        $allProperties = count($propertyNames)==0;
+
+        foreach($nodes as $myPath=>$node) {
+
+            $currentPropertyNames = $propertyNames;
+
+            $newProperties = array(
+                '200' => array(),
+                '404' => array(),
+            );
+
+            if ($allProperties) {
+                // Default list of propertyNames, when all properties were requested.
+                $currentPropertyNames = array(
+                    '{DAV:}getlastmodified',
+                    '{DAV:}getcontentlength',
+                    '{DAV:}resourcetype',
+                    '{DAV:}quota-used-bytes',
+                    '{DAV:}quota-available-bytes',
+                    '{DAV:}getetag',
+                    '{DAV:}getcontenttype',
+                );
+            }
+
+            // If the resourceType was not part of the list, we manually add it 
+            // and mark it for removal. We need to know the resourcetype in order 
+            // to make certain decisions about the entry.
+            // WebDAV dictates we should add a / and the end of href's for collections
+            $removeRT = false;
+            if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) {
+                $currentPropertyNames[] = '{DAV:}resourcetype';
+                $removeRT = true;
+            }
+
+            $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties));
+            // If this method explicitly returned false, we must ignore this 
+            // node as it is inacessible.
+            if ($result===false) continue;
+
+            if (count($currentPropertyNames) > 0) {
+
+                if ($node instanceof Sabre_DAV_IProperties) 
+                    $newProperties['200'] = $newProperties[200] + $node->getProperties($currentPropertyNames);
+
+            }
+
+
+            foreach($currentPropertyNames as $prop) {
+                
+                if (isset($newProperties[200][$prop])) continue;
+
+                switch($prop) {
+                    case '{DAV:}getlastmodified'       : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break;
+                    case '{DAV:}getcontentlength'      : if ($node instanceof Sabre_DAV_IFile) $newProperties[200][$prop] = (int)$node->getSize(); break;
+                    case '{DAV:}quota-used-bytes'      : 
+                        if ($node instanceof Sabre_DAV_IQuota) {
+                            $quotaInfo = $node->getQuotaInfo();
+                            $newProperties[200][$prop] = $quotaInfo[0];
+                        }
+                        break;
+                    case '{DAV:}quota-available-bytes' : 
+                        if ($node instanceof Sabre_DAV_IQuota) {
+                            $quotaInfo = $node->getQuotaInfo();
+                            $newProperties[200][$prop] = $quotaInfo[1];
+                        }
+                        break;
+                    case '{DAV:}getetag'               : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag())  $newProperties[200][$prop] = $etag; break;
+                    case '{DAV:}getcontenttype'        : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType())  $newProperties[200][$prop] = $ct; break;
+                    case '{DAV:}supported-report-set'  :
+                        $reports = array();
+                        foreach($this->plugins as $plugin) {
+                            $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath));
+                        }
+                        $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports); 
+                        break;
+                    case '{DAV:}resourcetype' :
+                        $newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType();
+                        foreach($this->resourceTypeMapping as $className => $resourceType) {
+                            if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType);
+                        }
+                        break;
+
+                }
+
+                // If we were unable to find the property, we will list it as 404.
+                if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null;
+
+            }
+         
+            $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties));
+
+            $newProperties['href'] = trim($myPath,'/'); 
+
+            // Its is a WebDAV recommendation to add a trailing slash to collectionnames.
+            // Apple's iCal also requires a trailing slash for principals (rfc 3744).
+            // Therefore we add a trailing / for any non-file. This might need adjustments 
+            // if we find there are other edge cases.
+            if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype']) && count($newProperties[200]['{DAV:}resourcetype']->getValue())>0) $newProperties['href'] .='/';
+
+            // If the resourcetype property was manually added to the requested property list,
+            // we will remove it again.
+            if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']);
+
+            $returnPropertyList[] = $newProperties;
+
+        }
+        
+        return $returnPropertyList;
+
+    }
+
+    /**
+     * This method is invoked by sub-systems creating a new file.
+     *
+     * Currently this is done by HTTP PUT and HTTP LOCK (in the Locks_Plugin).
+     * It was important to get this done through a centralized function, 
+     * allowing plugins to intercept this using the beforeCreateFile event.
+     * 
+     * @param string $uri 
+     * @param resource $data 
+     * @return void
+     */
+    public function createFile($uri,$data) {
+
+        list($dir,$name) = Sabre_DAV_URLUtil::splitPath($uri);
+
+        if (!$this->broadcastEvent('beforeBind',array($uri))) return;
+        if (!$this->broadcastEvent('beforeCreateFile',array($uri,$data))) return;
+
+        $parent = $this->tree->getNodeForPath($dir);
+        $parent->createFile($name,$data);
+        $this->tree->markDirty($dir);
+
+        $this->broadcastEvent('afterBind',array($uri));
+    }
+
+    /**
+     * This method is invoked by sub-systems creating a new directory.
+     *
+     * @param string $uri 
+     * @return void
+     */
+    public function createDirectory($uri) {
+
+        $this->createCollection($uri,array('{DAV:}collection'),array());
+
+    }
+
+    /**
+     * Use this method to create a new collection
+     *
+     * The {DAV:}resourcetype is specified using the resourceType array.
+     * At the very least it must contain {DAV:}collection. 
+     *
+     * The properties array can contain a list of additional properties.
+     * 
+     * @param string $uri The new uri 
+     * @param array $resourceType The resourceType(s) 
+     * @param array $properties A list of properties
+     * @return void
+     */
+    public function createCollection($uri, array $resourceType, array $properties) {
+
+        list($parentUri,$newName) = Sabre_DAV_URLUtil::splitPath($uri);
+
+        // Making sure {DAV:}collection was specified as resourceType
+        if (!in_array('{DAV:}collection', $resourceType)) {
+            throw new Sabre_DAV_Exception_InvalidResourceType('The resourceType for this collection must at least include {DAV:}collection');
+        }
+
+
+        // Making sure the parent exists
+        try {
+
+            $parent = $this->tree->getNodeForPath($parentUri);
+
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+
+            throw new Sabre_DAV_Exception_Conflict('Parent node does not exist');
+
+        }
+
+        // Making sure the parent is a collection
+        if (!$parent instanceof Sabre_DAV_ICollection) {
+            throw new Sabre_DAV_Exception_Conflict('Parent node is not a collection');
+        }
+
+
+
+        // Making sure the child does not already exist
+        try {
+            $parent->getChild($newName);
+
+            // If we got here.. it means there's already a node on that url, and we need to throw a 405
+            throw new Sabre_DAV_Exception_MethodNotAllowed('The resource you tried to create already exists');
+
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+            // This is correct
+        }
+
+        
+        if (!$this->broadcastEvent('beforeBind',array($uri))) return;
+
+        // There are 2 modes of operation. The standard collection 
+        // creates the directory, and then updates properties
+        // the extended collection can create it directly.
+        if ($parent instanceof Sabre_DAV_IExtendedCollection) {
+
+            $parent->createExtendedCollection($newName, $resourceType, $properties);
+
+        } else {
+
+            // No special resourcetypes are supported
+            if (count($resourceType)>1) {
+                throw new Sabre_DAV_Exception_InvalidResourceType('The {DAV:}resourcetype you specified is not supported here.');
+            }
+
+            $parent->createDirectory($newName);
+            $rollBack = false; 
+            $exception = null;
+            $errorResult = null;
+
+            if (count($properties)>0) {
+
+                try {
+
+                    $errorResult = $this->updateProperties($uri, $properties);
+                    if (!isset($errorResult[200])) {
+                        $rollBack = true;
+                    }
+
+                } catch (Sabre_DAV_Exception $e) {
+
+                    $rollBack = true;
+                    $exception = $e;
+
+                }
+
+            }
+
+            if ($rollBack) {
+                if (!$this->broadcastEvent('beforeUnbind',array($uri))) return;
+                $this->tree->delete($uri);
+
+                // Re-throwing exception
+                if ($exception) throw $exception;
+
+                return $errorResult;
+            }
+                
+        }
+        $this->tree->markDirty($parentUri);
+        $this->broadcastEvent('afterBind',array($uri));
+
+    }
+
+    /**
+     * This method updates a resource's properties
+     *
+     * The properties array must be a list of properties. Array-keys are
+     * property names in clarknotation, array-values are it's values.
+     * If a property must be deleted, the value should be null.
+     * 
+     * Note that this request should either completely succeed, or 
+     * completely fail.
+     *
+     * The response is an array with statuscodes for keys, which in turn
+     * contain arrays with propertynames. This response can be used
+     * to generate a multistatus body.
+     * 
+     * @param string $uri 
+     * @param array $properties 
+     * @return array 
+     */
+    public function updateProperties($uri, array $properties) {
+
+        // we'll start by grabbing the node, this will throw the appropriate
+        // exceptions if it doesn't. 
+        $node = $this->tree->getNodeForPath($uri);
+       
+        $result = array(
+            200 => array(),
+            403 => array(),
+            424 => array(),
+        ); 
+        $remainingProperties = $properties;
+        $hasError = false;
+
+        // Running through all properties to make sure none of them are protected
+        if (!$hasError) foreach($properties as $propertyName => $value) {
+            if(in_array($propertyName, $this->protectedProperties)) {
+                $result[403][$propertyName] = null;
+                unset($remainingProperties[$propertyName]);
+                $hasError = true;
+            }
+        }
+
+        if (!$hasError) {
+            // Allowing plugins to take care of property updating
+            $hasError = !$this->broadcastEvent('updateProperties',array(
+                &$remainingProperties,
+                &$result,
+                $node
+            ));
+        }
+
+        // If the node is not an instance of Sabre_DAV_IProperties, every
+        // property is 403 Forbidden
+        if (!$hasError && count($remainingProperties) && !($node instanceof Sabre_DAV_IProperties)) {
+            $hasError = true;
+            foreach($properties as $propertyName=> $value) {
+                $result[403][$propertyName] = null;
+            }
+            $remainingProperties = array();
+        }
+
+        // Only if there were no errors we may attempt to update the resource
+        if (!$hasError) {
+
+            if (count($remainingProperties)>0) {
+
+                $updateResult = $node->updateProperties($remainingProperties);
+
+                if ($updateResult===true) {
+                    // success
+                    foreach($remainingProperties as $propertyName=>$value) {
+                        $result[200][$propertyName] = null;
+                    }
+
+                } elseif ($updateResult===false) {
+                    // The node failed to update the properties for an
+                    // unknown reason
+                    foreach($remainingProperties as $propertyName=>$value) {
+                        $result[403][$propertyName] = null;
+                    }
+
+                } elseif (is_array($updateResult)) {
+
+                    // The node has detailed update information
+                    // We need to merge the results with the earlier results.
+                    foreach($updateResult as $status => $props) {
+                        if (is_array($props)) {
+                            if (!isset($result[$status]))
+                                $result[$status] = array();
+
+                            $result[$status] = array_merge($result[$status], $updateResult[$status]);
+                        }
+                    }
+
+                } else {
+                    throw new Sabre_DAV_Exception('Invalid result from updateProperties');
+                }
+                $remainingProperties = array();
+            }
+
+        }
+
+        foreach($remainingProperties as $propertyName=>$value) {
+            // if there are remaining properties, it must mean
+            // there's a dependency failure
+            $result[424][$propertyName] = null;
+        }
+
+        // Removing empty array values
+        foreach($result as $status=>$props) {
+
+            if (count($props)===0) unset($result[$status]);
+
+        }
+        $result['href'] = $uri;
+        return $result;
+
+    }
+
+    /**
+     * This method checks the main HTTP preconditions.
+     *
+     * Currently these are:
+     *   * If-Match
+     *   * If-None-Match
+     *   * If-Modified-Since
+     *   * If-Unmodified-Since
+     *
+     * The method will return true if all preconditions are met
+     * The method will return false, or throw an exception if preconditions
+     * failed. If false is returned the operation should be aborted, and
+     * the appropriate HTTP response headers are already set.
+     *
+     * Normally this method will throw 412 Precondition Failed for failures
+     * related to If-None-Match, If-Match and If-Unmodified Since. It will 
+     * set the status to 304 Not Modified for If-Modified_since.
+     *
+     * If the $handleAsGET argument is set to true, it will also return 304 
+     * Not Modified for failure of the If-None-Match precondition. This is the
+     * desired behaviour for HTTP GET and HTTP HEAD requests.
+     *
+     * @return bool 
+     */
+    public function checkPreconditions($handleAsGET = false) {
+
+        $uri = $this->getRequestUri();
+        $node = null;
+        $lastMod = null;
+        $etag = null;
+
+        if ($ifMatch = $this->httpRequest->getHeader('If-Match')) {
+
+            // If-Match contains an entity tag. Only if the entity-tag
+            // matches we are allowed to make the request succeed.
+            // If the entity-tag is '*' we are only allowed to make the
+            // request succeed if a resource exists at that url.
+            try {
+                $node = $this->tree->getNodeForPath($uri);
+            } catch (Sabre_DAV_Exception_FileNotFound $e) {
+                throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified and the resource did not exist','If-Match');
+            }
+
+            // Only need to check entity tags if they are not *
+            if ($ifMatch!=='*') {
+
+                // There can be multiple etags
+                $ifMatch = explode(',',$ifMatch);
+                $haveMatch = false;
+                foreach($ifMatch as $ifMatchItem) {
+
+                    // Stripping any extra spaces
+                    $ifMatchItem = trim($ifMatchItem,' ');
+                    
+                    $etag = $node->getETag();
+                    if ($etag===$ifMatchItem) {
+                        $haveMatch = true;
+                    }
+                }
+                if (!$haveMatch) {
+                     throw new Sabre_DAV_Exception_PreconditionFailed('An If-Match header was specified, but none of the specified the ETags matched.','If-Match');
+                }
+            }
+        }
+
+        if ($ifNoneMatch = $this->httpRequest->getHeader('If-None-Match')) {
+
+            // The If-None-Match header contains an etag.
+            // Only if the ETag does not match the current ETag, the request will succeed
+            // The header can also contain *, in which case the request
+            // will only succeed if the entity does not exist at all.
+            $nodeExists = true;
+            if (!$node) {
+                try {
+                    $node = $this->tree->getNodeForPath($uri);
+                } catch (Sabre_DAV_Exception_FileNotFound $e) {
+                    $nodeExists = false;
+                }
+            }
+            if ($nodeExists) {
+                $haveMatch = false;
+                if ($ifNoneMatch==='*') $haveMatch = true;
+                else {
+
+                    // There might be multiple etags
+                    $ifNoneMatch = explode(',', $ifNoneMatch);
+                    $etag = $node->getETag();
+
+                    foreach($ifNoneMatch as $ifNoneMatchItem) {
+                        
+                        // Stripping any extra spaces
+                        $ifNoneMatchItem = trim($ifNoneMatchItem,' ');
+                        
+                        if ($etag===$ifNoneMatchItem) $haveMatch = true;
+
+                    }
+
+                }
+
+                if ($haveMatch) {
+                    if ($handleAsGET) {
+                        $this->httpResponse->sendStatus(304);
+                        return false;
+                    } else {
+                        throw new Sabre_DAV_Exception_PreconditionFailed('An If-None-Match header was specified, but the ETag matched (or * was specified).','If-None-Match');
+                    }
+                }
+            }
+
+        }
+
+        if (!$ifNoneMatch && ($ifModifiedSince = $this->httpRequest->getHeader('If-Modified-Since'))) {
+            
+            // The If-Modified-Since header contains a date. We
+            // will only return the entity if it has been changed since
+            // that date. If it hasn't been changed, we return a 304
+            // header
+            // Note that this header only has to be checked if there was no If-None-Match header
+            // as per the HTTP spec.
+            $date = Sabre_HTTP_Util::parseHTTPDate($ifModifiedSince);
+
+            if ($date) {
+                if (is_null($node)) {
+                    $node = $this->tree->getNodeForPath($uri);
+                }
+                $lastMod = $node->getLastModified();
+                if ($lastMod) {
+                    $lastMod = new DateTime('@' . $lastMod);
+                    if ($lastMod <= $date) {
+                        $this->httpResponse->sendStatus(304);
+                        return false;
+                    } 
+                }
+            }
+        }
+
+        if ($ifUnmodifiedSince = $this->httpRequest->getHeader('If-Unmodified-Since')) {
+            
+            // The If-Unmodified-Since will allow allow the request if the
+            // entity has not changed since the specified date.
+            $date = Sabre_HTTP_Util::parseHTTPDate($ifUnmodifiedSince);
+           
+            // We must only check the date if it's valid
+            if ($date) {
+                if (is_null($node)) {
+                    $node = $this->tree->getNodeForPath($uri);
+                }   
+                $lastMod = $node->getLastModified();
+                if ($lastMod) {
+                    $lastMod = new DateTime('@' . $lastMod);
+                    if ($lastMod > $date) {
+                        throw new Sabre_DAV_Exception_PreconditionFailed('An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.','If-Unmodified-Since');
+                    }
+                }
+            }
+
+        }
+        return true;
+
+    }
+
+    // }}} 
+    // {{{ XML Readers & Writers  
+    
+    
+    /**
+     * Generates a WebDAV propfind response body based on a list of nodes 
+     * 
+     * @param array $fileProperties The list with nodes
+     * @param array $requestedProperties The properties that should be returned
+     * @return string 
+     */
+    public function generateMultiStatus(array $fileProperties) {
+
+        $dom = new DOMDocument('1.0','utf-8');
+        //$dom->formatOutput = true;
+        $multiStatus = $dom->createElement('d:multistatus');
+        $dom->appendChild($multiStatus);
+
+        // Adding in default namespaces
+        foreach($this->xmlNamespaces as $namespace=>$prefix) {
+
+            $multiStatus->setAttribute('xmlns:' . $prefix,$namespace);
+
+        }
+
+        foreach($fileProperties as $entry) {
+
+            $href = $entry['href'];
+            unset($entry['href']);
+            
+            $response = new Sabre_DAV_Property_Response($href,$entry);
+            $response->serialize($this,$multiStatus);
+
+        }
+
+        return $dom->saveXML();
+
+    }
+
+    /**
+     * This method parses a PropPatch request
+     *
+     * PropPatch changes the properties for a resource. This method
+     * returns a list of properties.
+     *
+     * The keys in the returned array contain the property name (e.g.: {DAV:}displayname,
+     * and the value contains the property value. If a property is to be removed the value
+     * will be null.
+     * 
+     * @param string $body xml body
+     * @return array list of properties in need of updating or deletion
+     */
+    public function parsePropPatchRequest($body) {
+
+        //We'll need to change the DAV namespace declaration to something else in order to make it parsable
+        $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+        
+        $newProperties = array();
+
+        foreach($dom->firstChild->childNodes as $child) {
+
+            if ($child->nodeType !== XML_ELEMENT_NODE) continue; 
+
+            $operation = Sabre_DAV_XMLUtil::toClarkNotation($child);
+
+            if ($operation!=='{DAV:}set' && $operation!=='{DAV:}remove') continue;
+            
+            $innerProperties = Sabre_DAV_XMLUtil::parseProperties($child, $this->propertyMap);
+
+            foreach($innerProperties as $propertyName=>$propertyValue) {
+
+                if ($operation==='{DAV:}remove') {
+                    $propertyValue = null;
+                }
+
+                $newProperties[$propertyName] = $propertyValue;
+
+            }
+
+        }
+
+        return $newProperties;
+
+    }
+
+    /**
+     * This method parses the PROPFIND request and returns its information
+     *
+     * This will either be a list of properties, or an empty array; in which case
+     * an {DAV:}allprop was requested.
+     * 
+     * @param string $body 
+     * @return array 
+     */
+    public function parsePropFindRequest($body) {
+
+        // If the propfind body was empty, it means IE is requesting 'all' properties
+        if (!$body) return array();
+
+        $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+        $elem = $dom->getElementsByTagNameNS('urn:DAV','propfind')->item(0);
+        return array_keys(Sabre_DAV_XMLUtil::parseProperties($elem)); 
+
+    }
+
+    // }}}
+
+}
+
diff --git a/3rdparty/Sabre/DAV/ServerPlugin.php b/3rdparty/Sabre/DAV/ServerPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..6909f600c216a169df4a1cee8030d2f205063b6b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/ServerPlugin.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * The baseclass for all server plugins.
+ *
+ * Plugins can modify or extend the servers behaviour. 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_ServerPlugin {
+
+    /**
+     * This initializes the plugin.
+     *
+     * This function is called by Sabre_DAV_Server, after
+     * addPlugin is called.
+     *
+     * This method should set up the requires event subscriptions.
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    abstract public function initialize(Sabre_DAV_Server $server);
+    
+    /**
+     * This method should return a list of server-features. 
+     *
+     * This is for example 'versioning' and is added to the DAV: header
+     * in an OPTIONS response.
+     * 
+     * @return array
+     */
+    public function getFeatures() {
+
+        return array();
+
+    }
+
+    /**
+     * Use this method to tell the server this plugin defines additional
+     * HTTP methods.
+     *
+     * This method is passed a uri. It should only return HTTP methods that are 
+     * available for the specified uri.
+     *
+     * @param string $uri
+     * @return array 
+     */
+    public function getHTTPMethods($uri) {
+
+        return array();
+
+    }
+
+    /**
+     * Returns a plugin name.
+     * 
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
+     * 
+     * @return string 
+     */
+    public function getPluginName() {
+
+        return get_class($this);
+
+    }
+
+    /**
+     * Returns a list of reports this plugin supports.
+     *
+     * This will be used in the {DAV:}supported-report-set property.
+     * Note that you still need to subscribe to the 'report' event to actually 
+     * implement them 
+     * 
+     * @param string $uri
+     * @return array 
+     */
+    public function getSupportedReportSet($uri) {
+
+        return array();
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/SimpleCollection.php b/3rdparty/Sabre/DAV/SimpleCollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..223d05fed554bbba58ed608b9fdaff4677c9fb14
--- /dev/null
+++ b/3rdparty/Sabre/DAV/SimpleCollection.php
@@ -0,0 +1,106 @@
+<?php
+
+/**
+ * SimpleCollection
+ *
+ * The SimpleCollection is used to quickly setup static directory structures.
+ * Just create the object with a proper name, and add children to use it.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_SimpleCollection extends Sabre_DAV_Collection {
+
+    /**
+     * List of childnodes 
+     *
+     * @var array
+     */
+    protected $children = array();
+
+    /**
+     * Name of this resource 
+     * 
+     * @var string 
+     */
+    protected $name;
+
+    /**
+     * Creates this node
+     *
+     * The name of the node must be passed, child nodes can also be bassed.
+     * This nodes must be instances of Sabre_DAV_INode
+     * 
+     * @param string $name 
+     * @param array $children 
+     * @return void
+     */
+    public function __construct($name,array $children = array()) {
+
+        $this->name = $name;
+        foreach($children as $child) {
+
+            if (!($child instanceof Sabre_DAV_INode)) throw new Sabre_DAV_Exception('Only instances of Sabre_DAV_INode are allowed to be passed in the children argument');
+            $this->addChild($child);
+
+        }
+
+    }
+
+    /**
+     * Adds a new childnode to this collection 
+     * 
+     * @param Sabre_DAV_INode $child 
+     * @return void
+     */
+    public function addChild(Sabre_DAV_INode $child) {
+
+        $this->children[$child->getName()] = $child;
+
+    }
+
+    /**
+     * Returns the name of the collection 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        return $this->name;
+
+    }
+
+    /**
+     * Returns a child object, by its name.
+     *
+     * This method makes use of the getChildren method to grab all the child nodes, and compares the name. 
+     * Generally its wise to override this, as this can usually be optimized
+     * 
+     * @param string $name
+     * @throws Sabre_DAV_Exception_FileNotFound
+     * @return Sabre_DAV_INode 
+     */
+    public function getChild($name) {
+
+        if (isset($this->children[$name])) return $this->children[$name];
+        throw new Sabre_DAV_Exception_FileNotFound('File not found: ' . $name . ' in \'' . $this->getName() . '\'');
+
+    }
+
+    /**
+     * Returns a list of children for this collection 
+     * 
+     * @return array 
+     */
+    public function getChildren() {
+
+        return array_values($this->children);
+
+    }
+
+
+}
+
diff --git a/3rdparty/Sabre/DAV/SimpleDirectory.php b/3rdparty/Sabre/DAV/SimpleDirectory.php
new file mode 100644
index 0000000000000000000000000000000000000000..516a3aa907c8d55511e7d15d421baa29e17a1738
--- /dev/null
+++ b/3rdparty/Sabre/DAV/SimpleDirectory.php
@@ -0,0 +1,21 @@
+<?php
+
+/**
+ * SimpleDirectory
+ *
+ * The SimpleDirectory is used to quickly setup static directory structures.
+ * Just create the object with a proper name, and add children to use it.
+ *
+ * This class is now deprecated, use Sabre_DAV_SimpleCollection instead.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @deprecated Use Sabre_DAV_SimpleCollection instead.
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_SimpleDirectory extends Sabre_DAV_SimpleCollection {
+
+}
+
diff --git a/3rdparty/Sabre/DAV/StringUtil.php b/3rdparty/Sabre/DAV/StringUtil.php
new file mode 100644
index 0000000000000000000000000000000000000000..b0b708f8e0f10e66da85be97bc076cd5ee6fbb3d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/StringUtil.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * String utility
+ *
+ * This class is mainly used to implement the 'text-match' filter, used by both 
+ * the CalDAV calendar-query REPORT, and CardDAV addressbook-query REPORT. 
+ * Because they both need it, it was decided to put it in Sabre_DAV instead.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_StringUtil {
+
+    /**
+     * Checks if a needle occurs in a haystack ;)
+     *
+     * @param string $haystack 
+     * @param string $needle 
+     * @param string $collation 
+     * @param string $matchType 
+     * @return bool 
+     */
+    static public function textMatch($haystack, $needle, $collation, $matchType = 'contains') {
+
+        switch($collation) {
+
+            case 'i;ascii-casemap' :
+                // default strtolower takes locale into consideration
+                // we don't want this.
+                $haystack = str_replace(range('a','z'), range('A','Z'), $haystack);
+                $needle = str_replace(range('a','z'), range('A','Z'), $needle);
+                break;
+
+            case 'i;octet' :
+                // Do nothing
+                break; 
+
+            case 'i;unicode-casemap' :
+                $haystack = mb_strtoupper($haystack, 'UTF-8');
+                $needle = mb_strtoupper($needle, 'UTF-8');
+                break;
+
+            default :
+                throw new Sabre_DAV_Exception_BadRequest('Collation type: ' . $collation . ' is not supported');
+
+        }
+
+        switch($matchType) {
+
+            case 'contains' :
+                return strpos($haystack, $needle)!==false;
+            case 'equals' :
+                return $haystack === $needle;
+            case 'starts-with' :
+                return strpos($haystack, $needle)===0;
+            case 'ends-with' :
+                return strrpos($haystack, $needle)===strlen($haystack)-strlen($needle);
+            default :
+                throw new Sabre_DAV_Exception_BadRequest('Match-type: ' . $matchType . ' is not supported');
+
+        }
+        
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..e8276af5613c7815c40be5a308f10c3de52a912b
--- /dev/null
+++ b/3rdparty/Sabre/DAV/TemporaryFileFilterPlugin.php
@@ -0,0 +1,279 @@
+<?php
+
+/**
+ * Temporary File Filter Plugin
+ *
+ * The purpose of this filter is to intercept some of the garbage files
+ * operation systems and applications tend to generate when mounting
+ * a WebDAV share as a disk.
+ *
+ * It will intercept these files and place them in a separate directory.
+ * these files are not deleted automatically, so it is adviceable to
+ * delete these after they are not accessed for 24 hours.
+ *
+ * Currently it supports:
+ *   * OS/X style resource forks and .DS_Store
+ *   * desktop.ini and Thumbs.db (windows)
+ *   * .*.swp (vim temporary files)
+ *   * .dat.* (smultron temporary files)
+ *
+ * Additional patterns can be added, by adding on to the
+ * temporaryFilePatterns property.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_TemporaryFileFilterPlugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * This is the list of patterns we intercept.
+     * If new patterns are added, they must be valid patterns for preg_match.
+     * 
+     * @var array
+     */
+    public $temporaryFilePatterns = array(
+        '/^\._(.*)$/',     // OS/X resource forks
+        '/^.DS_Store$/',   // OS/X custom folder settings
+        '/^desktop.ini$/', // Windows custom folder settings
+        '/^Thumbs.db$/',   // Windows thumbnail cache
+        '/^.(.*).swp$/',   // ViM temporary files
+        '/^\.dat(.*)$/',   // Smultron seems to create these
+        '/^~lock.(.*)#$/', // Windows 7 lockfiles
+    );
+    
+    /**
+     * This is the directory where this plugin
+     * will store it's files.
+     * 
+     * @var string 
+     */
+    private $dataDir;
+
+    /**
+     * A reference to the main Server class
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    private $server;
+
+    /**
+     * Creates the plugin.
+     *
+     * Make sure you specify a directory for your files. If you don't, we
+     * will use PHP's directory for session-storage instead, and you might
+     * not want that.
+     * 
+     * @param string $dataDir 
+     * @return void
+     */
+    public function __construct($dataDir = null) {
+
+        if (!$dataDir) $dataDir = ini_get('session.save_path').'/sabredav/';
+        if (!is_dir($dataDir)) mkdir($dataDir);
+        $this->dataDir = $dataDir;
+
+    } 
+
+    /**
+     * Initialize the plugin
+     *
+     * This is called automatically be the Server class after this plugin is
+     * added with Sabre_DAV_Server::addPlugin()
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $server->subscribeEvent('beforeMethod',array($this,'beforeMethod'));
+        $server->subscribeEvent('beforeCreateFile',array($this,'beforeCreateFile'));
+
+    }
+
+    /**
+     * This method is called before any HTTP method handler
+     *
+     * This method intercepts any GET, DELETE, PUT and PROPFIND calls to 
+     * filenames that are known to match the 'temporary file' regex.
+     * 
+     * @param string $method 
+     * @return bool 
+     */
+    public function beforeMethod($method, $uri) {
+
+        if (!$tempLocation = $this->isTempFile($uri))
+            return true;
+
+        switch($method) {
+            case 'GET' :
+                return $this->httpGet($tempLocation);
+            case 'PUT' :
+                return $this->httpPut($tempLocation);
+            case 'PROPFIND' :
+                return $this->httpPropfind($tempLocation, $uri);
+            case 'DELETE' :
+                return $this->httpDelete($tempLocation);
+         }
+         return true;
+
+    }
+
+    /**
+     * This method is invoked if some subsystem creates a new file.
+     *
+     * This is used to deal with HTTP LOCK requests which create a new 
+     * file.
+     * 
+     * @param string $uri 
+     * @param resource $data 
+     * @return bool 
+     */
+    public function beforeCreateFile($uri,$data) {
+
+        if ($tempPath = $this->isTempFile($uri)) {
+            
+            $hR = $this->server->httpResponse;
+            $hR->setHeader('X-Sabre-Temp','true');
+            file_put_contents($tempPath,$data);
+            return false;
+        }
+        return true; 
+
+    }
+
+    /**
+     * This method will check if the url matches the temporary file pattern
+     * if it does, it will return an path based on $this->dataDir for the 
+     * temporary file storage.
+     * 
+     * @param string $path 
+     * @return boolean|string 
+     */
+    protected function isTempFile($path) {
+
+        // We're only interested in the basename.
+        list(, $tempPath) = Sabre_DAV_URLUtil::splitPath($path);
+
+        foreach($this->temporaryFilePatterns as $tempFile) {
+
+            if (preg_match($tempFile,$tempPath)) {
+                return $this->dataDir . '/sabredav_' . md5($path) . '.tempfile';
+            }
+
+        }
+
+        return false;
+
+    }
+
+
+    /**
+     * This method handles the GET method for temporary files.
+     * If the file doesn't exist, it will return false which will kick in
+     * the regular system for the GET method.
+     * 
+     * @param string $tempLocation 
+     * @return bool 
+     */
+    public function httpGet($tempLocation) {
+
+        if (!file_exists($tempLocation)) return true;
+
+        $hR = $this->server->httpResponse;
+        $hR->setHeader('Content-Type','application/octet-stream');
+        $hR->setHeader('Content-Length',filesize($tempLocation));
+        $hR->setHeader('X-Sabre-Temp','true');
+        $hR->sendStatus(200);
+        $hR->sendBody(fopen($tempLocation,'r'));
+        return false;
+
+    }
+
+    /**
+     * This method handles the PUT method.
+     * 
+     * @param string $tempLocation 
+     * @return bool 
+     */
+    public function httpPut($tempLocation) {
+
+        $hR = $this->server->httpResponse;
+        $hR->setHeader('X-Sabre-Temp','true');
+
+        $newFile = !file_exists($tempLocation);
+       
+        if (!$newFile && ($this->server->httpRequest->getHeader('If-None-Match'))) {
+             throw new Sabre_DAV_Exception_PreconditionFailed('The resource already exists, and an If-None-Match header was supplied');
+        }
+
+        file_put_contents($tempLocation,$this->server->httpRequest->getBody());
+        $hR->sendStatus($newFile?201:200);
+        return false;
+
+    }
+
+    /**
+     * This method handles the DELETE method.
+     *
+     * If the file didn't exist, it will return false, which will make the 
+     * standard HTTP DELETE handler kick in.
+     * 
+     * @param string $tempLocation 
+     * @return bool 
+     */
+    public function httpDelete($tempLocation) {
+
+        if (!file_exists($tempLocation)) return true;
+
+        unlink($tempLocation);
+        $hR = $this->server->httpResponse;
+        $hR->setHeader('X-Sabre-Temp','true');
+        $hR->sendStatus(204);
+        return false;
+
+    }
+
+    /**
+     * This method handles the PROPFIND method. 
+     *
+     * It's a very lazy method, it won't bother checking the request body
+     * for which properties were requested, and just sends back a default
+     * set of properties.
+     *
+     * @param string $tempLocation 
+     * @return void
+     */
+    public function httpPropfind($tempLocation, $uri) {
+
+        if (!file_exists($tempLocation)) return true;
+       
+        $hR = $this->server->httpResponse;
+        $hR->setHeader('X-Sabre-Temp','true');
+        $hR->sendStatus(207);
+        $hR->setHeader('Content-Type','application/xml; charset=utf-8');
+
+        $requestedProps = $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true)); 
+
+        $properties = array(
+            'href' => $uri,
+            200 => array(
+                '{DAV:}getlastmodified' => new Sabre_DAV_Property_GetLastModified(filemtime($tempLocation)),
+                '{DAV:}getcontentlength' => filesize($tempLocation),
+                '{DAV:}resourcetype' => new Sabre_DAV_Property_ResourceType(null),
+                '{'.Sabre_DAV_Server::NS_SABREDAV.'}tempFile' => true, 
+
+            ),
+         );
+
+        $data = $this->server->generateMultiStatus(array($properties));
+        $hR->sendBody($data);
+        return false;
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/DAV/Tree.php b/3rdparty/Sabre/DAV/Tree.php
new file mode 100644
index 0000000000000000000000000000000000000000..98e6f62c9e588cb5462408884b1bbd9206abde3d
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Tree.php
@@ -0,0 +1,192 @@
+<?php
+
+/**
+ * Abstract tree object 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAV_Tree {
+    
+    /**
+     * This function must return an INode object for a path
+     * If a Path doesn't exist, thrown an Exception_FileNotFound
+     * 
+     * @param string $path 
+     * @throws Exception_FileNotFound
+     * @return Sabre_DAV_INode 
+     */
+    abstract function getNodeForPath($path);
+
+    /**
+     * This function allows you to check if a node exists.
+     *
+     * Implementors of this class should override this method to make 
+     * it cheaper.
+     * 
+     * @param string $path 
+     * @return bool 
+     */
+    public function nodeExists($path) {
+
+        try {
+
+            $this->getNodeForPath($path);
+            return true;
+
+        } catch (Sabre_DAV_Exception_FileNotFound $e) {
+
+            return false;
+
+        }
+
+    }
+
+    /**
+     * Copies a file from path to another
+     *
+     * @param string $sourcePath The source location
+     * @param string $destinationPath The full destination path
+     * @return void 
+     */
+    public function copy($sourcePath, $destinationPath) {
+
+        $sourceNode = $this->getNodeForPath($sourcePath);
+       
+        // grab the dirname and basename components
+        list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath);
+
+        $destinationParent = $this->getNodeForPath($destinationDir);
+        $this->copyNode($sourceNode,$destinationParent,$destinationName);
+
+        $this->markDirty($destinationDir);
+
+    }
+
+    /**
+     * Moves a file from one location to another 
+     * 
+     * @param string $sourcePath The path to the file which should be moved 
+     * @param string $destinationPath The full destination path, so not just the destination parent node
+     * @return int
+     */
+    public function move($sourcePath, $destinationPath) {
+
+        list($sourceDir, $sourceName) = Sabre_DAV_URLUtil::splitPath($sourcePath);
+        list($destinationDir, $destinationName) = Sabre_DAV_URLUtil::splitPath($destinationPath);
+
+        if ($sourceDir===$destinationDir) {
+            $renameable = $this->getNodeForPath($sourcePath);
+            $renameable->setName($destinationName);
+        } else {
+            $this->copy($sourcePath,$destinationPath);
+            $this->getNodeForPath($sourcePath)->delete();
+        }
+        $this->markDirty($sourceDir);
+        $this->markDirty($destinationDir);
+
+    }
+
+    /**
+     * Deletes a node from the tree 
+     * 
+     * @param string $path 
+     * @return void
+     */
+    public function delete($path) {
+
+        $node = $this->getNodeForPath($path);
+        $node->delete();
+        
+        list($parent) = Sabre_DAV_URLUtil::splitPath($path);
+        $this->markDirty($parent);
+
+    }
+
+    /**
+     * Returns a list of childnodes for a given path. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    public function getChildren($path) {
+
+        $node = $this->getNodeForPath($path);
+        return $node->getChildren();
+
+    }
+
+    /**
+     * This method is called with every tree update
+     *
+     * Examples of tree updates are:
+     *   * node deletions
+     *   * node creations
+     *   * copy
+     *   * move
+     *   * renaming nodes 
+     * 
+     * If Tree classes implement a form of caching, this will allow
+     * them to make sure caches will be expired.
+     * 
+     * If a path is passed, it is assumed that the entire subtree is dirty
+     *
+     * @param string $path 
+     * @return void
+     */
+    public function markDirty($path) {
+
+
+    }
+
+    /**
+     * copyNode 
+     * 
+     * @param Sabre_DAV_INode $source 
+     * @param Sabre_DAV_ICollection $destination 
+     * @return void
+     */
+    protected function copyNode(Sabre_DAV_INode $source,Sabre_DAV_ICollection $destinationParent,$destinationName = null) {
+
+        if (!$destinationName) $destinationName = $source->getName();
+
+        if ($source instanceof Sabre_DAV_IFile) {
+
+            $data = $source->get();
+
+            // If the body was a string, we need to convert it to a stream
+            if (is_string($data)) {
+                $stream = fopen('php://temp','r+');
+                fwrite($stream,$data);
+                rewind($stream);
+                $data = $stream;
+            } 
+            $destinationParent->createFile($destinationName,$data);
+            $destination = $destinationParent->getChild($destinationName);
+
+        } elseif ($source instanceof Sabre_DAV_ICollection) {
+
+            $destinationParent->createDirectory($destinationName);
+            
+            $destination = $destinationParent->getChild($destinationName);
+            foreach($source->getChildren() as $child) {
+
+                $this->copyNode($child,$destination);
+
+            }
+
+        }
+        if ($source instanceof Sabre_DAV_IProperties && $destination instanceof Sabre_DAV_IProperties) {
+
+            $props = $source->getProperties(array());
+            $destination->updateProperties($props);
+
+        }
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/Tree/Filesystem.php b/3rdparty/Sabre/DAV/Tree/Filesystem.php
new file mode 100644
index 0000000000000000000000000000000000000000..5c611047e073fd4c2642babf24696517c53b2201
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Tree/Filesystem.php
@@ -0,0 +1,124 @@
+<?php
+
+/**
+ * Sabre_DAV_Tree_Filesystem 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Tree_Filesystem extends Sabre_DAV_Tree {
+
+    /**
+     * Base url on the filesystem.
+     *
+     * @var string 
+     */
+    protected $basePath;
+
+    /**
+     * Creates this tree
+     *
+     * Supply the path you'd like to share.
+     * 
+     * @param string $basePath 
+     * @return void
+     */
+    public function __construct($basePath) {
+
+        $this->basePath = $basePath;
+
+    }
+
+    /**
+     * Returns a new node for the given path 
+     * 
+     * @param string $path 
+     * @return void
+     */
+    public function getNodeForPath($path) {
+
+        $realPath = $this->getRealPath($path);
+        if (!file_exists($realPath)) throw new Sabre_DAV_Exception_FileNotFound('File at location ' . $realPath . ' not found');
+        if (is_dir($realPath)) { 
+            return new Sabre_DAV_FS_Directory($path);
+        } else {
+            return new Sabre_DAV_FS_File($path);
+        }
+
+    }
+
+    /**
+     * Returns the real filesystem path for a webdav url. 
+     * 
+     * @param string $publicPath 
+     * @return string 
+     */
+    protected function getRealPath($publicPath) {
+
+        return rtrim($this->basePath,'/') . '/' . trim($publicPath,'/');
+
+    }
+
+    /**
+     * Copies a file or directory.
+     *
+     * This method must work recursively and delete the destination
+     * if it exists
+     * 
+     * @param string $source 
+     * @param string $destination 
+     * @return void
+     */
+    public function copy($source,$destination) {
+
+        $source = $this->getRealPath($source);
+        $destination = $this->getRealPath($destination);
+        $this->realCopy($source,$destination); 
+
+    }
+
+    /**
+     * Used by self::copy 
+     * 
+     * @param string $source 
+     * @param string $destination 
+     * @return void
+     */
+    protected function realCopy($source,$destination) {
+
+        if (is_file($source)) {
+            copy($source,$destination);
+        } else {
+            mkdir($destination);
+            foreach(scandir($source) as $subnode) {
+
+                if ($subnode=='.' || $subnode=='..') continue;
+                $this->realCopy($source.'/'.$subnode,$destination.'/'.$subnode);
+
+            }
+        }
+
+    }
+
+    /**
+     * Moves a file or directory recursively.
+     *
+     * If the destination exists, delete it first.
+     * 
+     * @param string $source 
+     * @param string $destination 
+     * @return void
+     */
+    public function move($source,$destination) {
+
+        $source = $this->getRealPath($source);
+        $destination = $this->getRealPath($destination);
+        rename($source,$destination);
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAV/URLUtil.php b/3rdparty/Sabre/DAV/URLUtil.php
new file mode 100644
index 0000000000000000000000000000000000000000..1502e4dd2cea38d652f1038bbb9a9bdbf3b46ee2
--- /dev/null
+++ b/3rdparty/Sabre/DAV/URLUtil.php
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * URL utility class
+ *
+ * This class provides methods to deal with encoding and decoding url (percent encoded) strings.
+ *
+ * It was not possible to use PHP's built-in methods for this, because some clients don't like
+ * encoding of certain characters.
+ *
+ * Specifically, it was found that GVFS (gnome's webdav client) does not like encoding of ( and
+ * ). Since these are reserved, but don't have a reserved meaning in url, these characters are
+ * kept as-is.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_URLUtil {
+
+    /**
+     * Encodes the path of a url.
+     *
+     * slashes (/) are treated as path-separators.
+     * 
+     * @param string $path 
+     * @return string 
+     */
+    static function encodePath($path) {
+
+        $path = explode('/',$path);
+        return implode('/',array_map(array('Sabre_DAV_URLUtil','encodePathSegment'), $path));
+
+    }
+
+    /**
+     * Encodes a 1 segment of a path
+     *
+     * Slashes are considered part of the name, and are encoded as %2f
+     * 
+     * @param string $pathSegment 
+     * @return string 
+     */
+    static function encodePathSegment($pathSegment) {
+
+        $newStr = '';
+        for($i=0;$i<strlen($pathSegment);$i++) {
+            $c = ord($pathSegment[$i]);
+
+            if(
+                
+                /* Unreserved chacaters */
+
+                ($c>=0x41 /* A */ && $c<=0x5a /* Z */) ||
+                ($c>=0x61 /* a */ && $c<=0x7a /* z */) ||
+                ($c>=0x30 /* 0 */ && $c<=0x39 /* 9 */) ||
+                $c===0x5f /* _ */ ||
+                $c===0x2d /* - */ ||
+                $c===0x2e /* . */ ||
+                $c===0x7E /* ~ */ ||
+
+                /* Reserved, but no reserved purpose */
+                $c===0x28 /* ( */ ||
+                $c===0x29 /* ) */
+
+            ) { 
+                $newStr.=$pathSegment[$i];
+            } else {
+                $newStr.='%' . str_pad(dechex($c), 2, '0', STR_PAD_LEFT);
+            }
+                
+        }
+        return $newStr;
+
+    }
+
+    /**
+     * Decodes a url-encoded path
+     *
+     * @param string $path 
+     * @return string 
+     */
+    static function decodePath($path) {
+
+        return self::decodePathSegment($path);
+
+    }
+
+    /**
+     * Decodes a url-encoded path segment
+     *
+     * @param string $path 
+     * @return string 
+     */
+    static function decodePathSegment($path) {
+
+        $path = urldecode($path);
+        $encoding = mb_detect_encoding($path, array('UTF-8','ISO-8859-1'));
+
+        switch($encoding) {
+
+            case 'ISO-8859-1' : 
+                $path = utf8_encode($path);
+        }
+
+        return $path;
+
+    }
+
+    /**
+     * Returns the 'dirname' and 'basename' for a path. 
+     *
+     * The reason there is a custom function for this purpose, is because
+     * basename() is locale aware (behaviour changes if C locale or a UTF-8 locale is used)
+     * and we need a method that just operates on UTF-8 characters.
+     *
+     * In addition basename and dirname are platform aware, and will treat backslash (\) as a
+     * directory separator on windows.
+     *
+     * This method returns the 2 components as an array.
+     *
+     * If there is no dirname, it will return an empty string. Any / appearing at the end of the
+     * string is stripped off.
+     *
+     * @param string $path 
+     * @return array 
+     */
+    static function splitPath($path) {
+
+        $matches = array();
+        if(preg_match('/^(?:(?:(.*)(?:\/+))?([^\/]+))(?:\/?)$/u',$path,$matches)) {
+            return array($matches[1],$matches[2]);
+        } else {
+            return array(null,null);
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/UUIDUtil.php b/3rdparty/Sabre/DAV/UUIDUtil.php
new file mode 100644
index 0000000000000000000000000000000000000000..e42a536ad8a3feff157c8c2cebec4be534d30967
--- /dev/null
+++ b/3rdparty/Sabre/DAV/UUIDUtil.php
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * UUID Utility
+ *
+ * This class has static methods to generate and validate UUID's.
+ * UUIDs are used a decent amount within various *DAV standards, so it made 
+ * sense to include it.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_UUIDUtil {
+
+    /**
+     * Returns a pseudo-random v4 UUID
+     *
+     * This function is based on a comment by Andrew Moore on php.net 
+     * 
+     * @see http://www.php.net/manual/en/function.uniqid.php#94959
+     * @return void
+     */
+    static function getUUID() {
+
+        return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
+            // 32 bits for "time_low"
+            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
+
+            // 16 bits for "time_mid"
+            mt_rand( 0, 0xffff ),
+
+            // 16 bits for "time_hi_and_version",
+            // four most significant bits holds version number 4
+            mt_rand( 0, 0x0fff ) | 0x4000,
+
+            // 16 bits, 8 bits for "clk_seq_hi_res",
+            // 8 bits for "clk_seq_low",
+            // two most significant bits holds zero and one for variant DCE1.1
+            mt_rand( 0, 0x3fff ) | 0x8000,
+
+            // 48 bits for "node"
+            mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
+        );
+    }
+
+    /**
+     * Checks if a string is a valid UUID.
+     * 
+     * @param string $uuid 
+     * @return bool 
+     */
+    static function validateUUID($uuid) {
+
+        return preg_match(
+            '/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i',
+            $uuid
+        ) == true;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAV/Version.php b/3rdparty/Sabre/DAV/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..c93d793ab672d3bb1f48d5826d420208b99d74ce
--- /dev/null
+++ b/3rdparty/Sabre/DAV/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the SabreDAV version constants.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.5.0';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'alpha';
+
+}
diff --git a/3rdparty/Sabre/DAV/XMLUtil.php b/3rdparty/Sabre/DAV/XMLUtil.php
new file mode 100644
index 0000000000000000000000000000000000000000..bd05be4b2297b77e36c47d62904ff964617a0516
--- /dev/null
+++ b/3rdparty/Sabre/DAV/XMLUtil.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * XML utilities for WebDAV
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAV_XMLUtil {
+
+    /**
+     * Returns the 'clark notation' for an element.
+     * 
+     * For example, and element encoded as:
+     * <b:myelem xmlns:b="http://www.example.org/" />
+     * will be returned as:
+     * {http://www.example.org}myelem
+     *
+     * This format is used throughout the SabreDAV sourcecode.
+     * Elements encoded with the urn:DAV namespace will 
+     * be returned as if they were in the DAV: namespace. This is to avoid
+     * compatibility problems.
+     *
+     * This function will return null if a nodetype other than an Element is passed.
+     *
+     * @param DOMElement $dom 
+     * @return string 
+     */
+    static function toClarkNotation(DOMNode $dom) {
+
+        if ($dom->nodeType !== XML_ELEMENT_NODE) return null;
+
+        // Mapping back to the real namespace, in case it was dav
+        if ($dom->namespaceURI=='urn:DAV') $ns = 'DAV:'; else $ns = $dom->namespaceURI;
+        
+        // Mapping to clark notation
+        return '{' . $ns . '}' . $dom->localName;
+
+    }
+
+    /**
+     * Parses a clark-notation string, and returns the namespace and element 
+     * name components.
+     *
+     * If the string was invalid, it will throw an InvalidArgumentException.
+     * 
+     * @param string $str
+     * @throws InvalidArgumentException 
+     * @return array 
+     */
+    static function parseClarkNotation($str) {
+
+        if (!preg_match('/^{([^}]*)}(.*)$/',$str,$matches)) {
+            throw new InvalidArgumentException('\'' . $str . '\' is not a valid clark-notation formatted string');
+        }
+
+        return array(
+            $matches[1],
+            $matches[2]
+        );
+
+    }
+
+    /**
+     * This method takes an XML document (as string) and converts all instances of the
+     * DAV: namespace to urn:DAV
+     *
+     * This is unfortunately needed, because the DAV: namespace violates the xml namespaces
+     * spec, and causes the DOM to throw errors
+     */
+    static function convertDAVNamespace($xmlDocument) {
+
+        // This is used to map the DAV: namespace to urn:DAV. This is needed, because the DAV:
+        // namespace is actually a violation of the XML namespaces specification, and will cause errors
+        return preg_replace("/xmlns(:[A-Za-z0-9_]*)?=(\"|\')DAV:(\\2)/","xmlns\\1=\\2urn:DAV\\2",$xmlDocument);
+
+    }
+
+    /**
+     * This method provides a generic way to load a DOMDocument for WebDAV use.
+     *
+     * This method throws a Sabre_DAV_Exception_BadRequest exception for any xml errors.
+     * It does not preserve whitespace, and it converts the DAV: namespace to urn:DAV. 
+     * 
+     * @param string $xml
+     * @throws Sabre_DAV_Exception_BadRequest 
+     * @return DOMDocument 
+     */
+    static function loadDOMDocument($xml) {
+
+        if (empty($xml))
+            throw new Sabre_DAV_Exception_BadRequest('Empty XML document sent');
+       
+        // The BitKinex client sends xml documents as UTF-16. PHP 5.3.1 (and presumably lower)
+        // does not support this, so we must intercept this and convert to UTF-8.
+        if (substr($xml,0,12) === "\x3c\x00\x3f\x00\x78\x00\x6d\x00\x6c\x00\x20\x00") {
+
+            // Note: the preceeding byte sequence is "<?xml" encoded as UTF_16, without the BOM.
+            $xml = iconv('UTF-16LE','UTF-8',$xml);
+
+            // Because the xml header might specify the encoding, we must also change this.
+            // This regex looks for the string encoding="UTF-16" and replaces it with
+            // encoding="UTF-8".
+            $xml = preg_replace('|<\?xml([^>]*)encoding="UTF-16"([^>]*)>|u','<?xml\1encoding="UTF-8"\2>',$xml);
+
+        }
+
+        // Retaining old error setting
+        $oldErrorSetting =  libxml_use_internal_errors(true);
+
+        // Clearing any previous errors 
+        libxml_clear_errors();
+
+        $dom = new DOMDocument();
+        $dom->loadXML(self::convertDAVNamespace($xml),LIBXML_NOWARNING | LIBXML_NOERROR);
+
+        // We don't generally care about any whitespace
+        $dom->preserveWhiteSpace = false;
+
+        if ($error = libxml_get_last_error()) {
+            libxml_clear_errors();
+            throw new Sabre_DAV_Exception_BadRequest('The request body had an invalid XML body. (message: ' . $error->message . ', errorcode: ' . $error->code . ', line: ' . $error->line . ')');
+        }
+
+        // Restoring old mechanism for error handling
+        if ($oldErrorSetting===false) libxml_use_internal_errors(false);
+
+        return $dom;
+
+    }
+
+    /**
+     * Parses all WebDAV properties out of a DOM Element
+     *
+     * Generally WebDAV properties are encloded in {DAV:}prop elements. This
+     * method helps by going through all these and pulling out the actual
+     * propertynames, making them array keys and making the property values,
+     * well.. the array values.
+     *
+     * If no value was given (self-closing element) null will be used as the
+     * value. This is used in for example PROPFIND requests.
+     *
+     * Complex values are supported through the propertyMap argument. The
+     * propertyMap should have the clark-notation properties as it's keys, and
+     * classnames as values. 
+     *
+     * When any of these properties are found, the unserialize() method will be
+     * (statically) called. The result of this method is used as the value.
+     *
+     * @param DOMElement $parentNode
+     * @param array $propertyMap
+     * @return array
+     */
+    static function parseProperties(DOMElement $parentNode, array $propertyMap = array()) {
+
+        $propList = array();
+        foreach($parentNode->childNodes as $propNode) {
+
+            if (Sabre_DAV_XMLUtil::toClarkNotation($propNode)!=='{DAV:}prop') continue;
+
+            foreach($propNode->childNodes as $propNodeData) {
+
+                /* If there are no elements in here, we actually get 1 text node, this special case is dedicated to netdrive */
+                if ($propNodeData->nodeType != XML_ELEMENT_NODE) continue;
+
+                $propertyName = Sabre_DAV_XMLUtil::toClarkNotation($propNodeData);
+                if (isset($propertyMap[$propertyName])) {
+                    $propList[$propertyName] = call_user_func(array($propertyMap[$propertyName],'unserialize'),$propNodeData); 
+                } else {
+                    $propList[$propertyName] = $propNodeData->textContent;
+                }
+            }
+
+
+        }
+        return $propList;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..a361e054610dd2251f3bb1961b879a95de39d79a
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/AbstractPrincipalCollection.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * Principals Collection
+ *
+ * This is a helper class that easily allows you to create a collection that 
+ * has a childnode for every principal.
+ * 
+ * To use this class, simply implement the getChildForPrincipal method. 
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_DAVACL_AbstractPrincipalCollection extends Sabre_DAV_Collection  {
+
+    /**
+     * Node or 'directory' name. 
+     * 
+     * @var string 
+     */
+    protected $path;
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * If this value is set to true, it effectively disables listing of users
+     * it still allows user to find other users if they have an exact url. 
+     * 
+     * @var bool 
+     */
+    public $disableListing = false;
+
+    /**
+     * Creates the object
+     *
+     * This object must be passed the principal backend. This object will 
+     * filter all principals from a specfied prefix ($principalPrefix). The 
+     * default is 'principals', if your principals are stored in a different 
+     * collection, override $principalPrefix
+     * 
+     * 
+     * @param Sabre_DAVACL_IPrincipalBackend $principalBackend 
+     * @param string $principalPrefix
+     * @param string $nodeName
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, $principalPrefix = 'principals') {
+
+        $this->principalPrefix = $principalPrefix;
+        $this->principalBackend = $principalBackend;
+
+    }
+
+    /**
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
+     * 
+     * @param array $principalInfo 
+     * @return Sabre_DAVACL_IPrincipal
+     */
+    abstract function getChildForPrincipal(array $principalInfo);
+
+    /**
+     * Returns the name of this collection. 
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        list(,$name) = Sabre_DAV_URLUtil::splitPath($this->principalPrefix);
+        return $name; 
+
+    }
+
+    /**
+     * Return the list of users 
+     * 
+     * @return void
+     */
+    public function getChildren() {
+
+        if ($this->disableListing)
+            throw new Sabre_DAV_Exception_MethodNotAllowed('Listing members of this collection is disabled');
+
+        $children = array();
+        foreach($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) {
+
+            $children[] = $this->getChildForPrincipal($principalInfo);
+
+
+        }
+        return $children; 
+
+    }
+
+    /**
+     * Returns a child object, by its name.
+     * 
+     * @param string $name
+     * @throws Sabre_DAV_Exception_FileNotFound
+     * @return Sabre_DAV_IPrincipal
+     */
+    public function getChild($name) {
+
+        $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name);
+        if (!$principalInfo) throw new Sabre_DAV_Exception_FileNotFound('Principal with name ' . $name . ' not found');
+        return $this->getChildForPrincipal($principalInfo);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Exception/AceConflict.php b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php
new file mode 100644
index 0000000000000000000000000000000000000000..d10aeb4345c6ff2c653f139db50f904dbf779093
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/AceConflict.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_AceConflict 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Exception_AceConflict extends Sabre_DAV_Exception_Conflict {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}no-ace-conflict element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:no-ace-conflict');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php
new file mode 100644
index 0000000000000000000000000000000000000000..640ab8efff4277be9d455631dc6ca06175103ba4
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NeedPrivileges.php
@@ -0,0 +1,84 @@
+<?php
+
+/**
+ * NeedPrivileges 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @version $Id$
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+/**
+ * NeedPrivileges 
+ *
+ * The 403-need privileges is thrown when a user didn't have the appropriate
+ * permissions to perform an operation
+ */
+class Sabre_DAVACL_Exception_NeedPrivileges extends Sabre_DAV_Exception_Forbidden {
+
+    /**
+     * The relevant uri 
+     * 
+     * @var string 
+     */
+    protected $uri;
+
+    /**
+     * The privileges the user didn't have. 
+     * 
+     * @var array 
+     */
+    protected $privileges;
+
+    /**
+     * Constructor 
+     * 
+     * @param string $uri 
+     * @param array $privileges 
+     */
+    public function __construct($uri,array $privileges) {
+
+        $this->uri = $uri;
+        $this->privileges = $privileges;
+
+    }
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}need-privileges element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:need-privileges');
+        $errorNode->appendChild($np);
+
+        foreach($this->privileges as $privilege) {
+
+            $resource = $doc->createElementNS('DAV:','d:resource');
+            $np->appendChild($resource);
+
+            $resource->appendChild($doc->createElementNS('DAV:','d:href',$server->getBaseUri() . $this->uri));
+
+            $priv = $doc->createElementNS('DAV:','d:privilege');
+            $resource->appendChild($priv);
+
+            preg_match('/^{([^}]*)}(.*)$/',$privilege,$privilegeParts);
+            $priv->appendChild($doc->createElementNS($privilegeParts[1],'d:' . $privilegeParts[2]));
+
+
+        }
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php
new file mode 100644
index 0000000000000000000000000000000000000000..60f49ebff4adec68e1eee8454628737909961310
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NoAbstract.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_NoAbstract
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Exception_NoAbstract extends Sabre_DAV_Exception_PreconditionFailed {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}no-abstract element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:no-abstract');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
new file mode 100644
index 0000000000000000000000000000000000000000..e056dc9e4f70ff2c25e2a679fdb4e4669f2db2c9
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NotRecognizedPrincipal.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_NotRecognizedPrincipal
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Exception_NotRecognizedPrincipal extends Sabre_DAV_Exception_PreconditionFailed {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}recognized-principal element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:recognized-principal');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
new file mode 100644
index 0000000000000000000000000000000000000000..27db7cdd7dd43be716b6248a8d4acdf73505e707
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Exception/NotSupportedPrivilege.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Sabre_DAVACL_Exception_NotSupportedPrivilege
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Exception_NotSupportedPrivilege extends Sabre_DAV_Exception_PreconditionFailed {
+
+    /**
+     * Adds in extra information in the xml response.
+     *
+     * This method adds the {DAV:}not-supported-privilege element as defined in rfc3744
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $errorNode 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $errorNode) {
+        
+        $doc = $errorNode->ownerDocument;
+        
+        $np = $doc->createElementNS('DAV:','d:not-supported-privilege');
+        $errorNode->appendChild($np);
+
+    }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/DAVACL/IACL.php b/3rdparty/Sabre/DAVACL/IACL.php
new file mode 100644
index 0000000000000000000000000000000000000000..506be4248d76b8bca9facd87097c128846e4f2bd
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/IACL.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * ACL-enabled node
+ *
+ * If you want to add WebDAV ACL to a node, you must implement this class
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAVACL_IACL extends Sabre_DAV_INode {
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    function getOwner();
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    function getGroup();
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    function getACL();
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    function setACL(array $acl);
+
+}
diff --git a/3rdparty/Sabre/DAVACL/IPrincipal.php b/3rdparty/Sabre/DAVACL/IPrincipal.php
new file mode 100644
index 0000000000000000000000000000000000000000..7868811db76394ef6101516b627bde7d1da54929
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/IPrincipal.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * IPrincipal interface
+ * 
+ * Implement this interface to define your own principals
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAVACL_IPrincipal extends Sabre_DAV_INode {
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    function getAlternateUriSet();
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    function getPrincipalUrl();
+
+    /**
+     * Returns the list of group members
+     * 
+     * If this principal is a group, this function should return
+     * all member principal uri's for the group. 
+     * 
+     * @return array
+     */
+    function getGroupMemberSet();
+
+    /**
+     * Returns the list of groups this principal is member of
+     * 
+     * If this principal is a member of a (list of) groups, this function
+     * should return a list of principal uri's for it's members. 
+     * 
+     * @return array 
+     */
+    function getGroupMembership();
+
+    /**
+     * Sets a list of group members
+     *
+     * If this principal is a group, this method sets all the group members.
+     * The list of members is always overwritten, never appended to.
+     * 
+     * This method should throw an exception if the members could not be set. 
+     * 
+     * @param array $principals 
+     * @return void 
+     */
+    function setGroupMemberSet(array $principals);
+
+    /**
+     * Returns the displayname
+     *
+     * This should be a human readable name for the principal.
+     * If none is available, return the nodename. 
+     * 
+     * @return string 
+     */
+    function getDisplayName();
+
+}
diff --git a/3rdparty/Sabre/DAVACL/IPrincipalBackend.php b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php
new file mode 100644
index 0000000000000000000000000000000000000000..8899f6f80dffda4e838cf963ec1ce6ade41660f7
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/IPrincipalBackend.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * Implement this interface to create your own principal backends.
+ *
+ * Creating backends for principals is entirely optional. You can also 
+ * implement Sabre_DAVACL_IPrincipal directly. This interface is used solely by 
+ * Sabre_DAVACL_AbstractPrincipalCollection.
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+interface Sabre_DAVACL_IPrincipalBackend {
+
+    /**
+     * Returns a list of principals based on a prefix.
+     *
+     * This prefix will often contain something like 'principals'. You are only 
+     * expected to return principals that are in this base path.
+     *
+     * You are expected to return at least a 'uri' for every user, you can 
+     * return any additional properties if you wish so. Common properties are:
+     *   {DAV:}displayname 
+     *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV 
+     *     field that's actualy injected in a number of other properties. If
+     *     you have an email address, use this property.
+     * 
+     * @param string $prefixPath 
+     * @return array 
+     */
+    function getPrincipalsByPrefix($prefixPath);
+
+    /**
+     * Returns a specific principal, specified by it's path.
+     * The returned structure should be the exact same as from 
+     * getPrincipalsByPrefix. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    function getPrincipalByPath($path);
+
+    /**
+     * Returns the list of members for a group-principal 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    function getGroupMemberSet($principal);
+
+    /**
+     * Returns the list of groups a principal is a member of 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    function getGroupMembership($principal);
+
+    /**
+     * Updates the list of group members for a group principal.
+     *
+     * The principals should be passed as a list of uri's. 
+     * 
+     * @param string $principal 
+     * @param array $members 
+     * @return void
+     */
+    function setGroupMemberSet($principal, array $members); 
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Plugin.php b/3rdparty/Sabre/DAVACL/Plugin.php
new file mode 100644
index 0000000000000000000000000000000000000000..b964bdb5dec84466b71002cf4cce356174b5bb97
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Plugin.php
@@ -0,0 +1,1238 @@
+<?php
+
+/**
+ * SabreDAV ACL Plugin
+ *
+ * This plugin provides funcitonality to enforce ACL permissions.
+ * ACL is defined in RFC3744.
+ *
+ * In addition it also provides support for the {DAV:}current-user-principal 
+ * property, defined in RFC5397 and the {DAV:}expand-property report, as 
+ * defined in RFC3253. 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Plugin extends Sabre_DAV_ServerPlugin {
+
+    /**
+     * Recursion constants
+     *
+     * This only checks the base node
+     */
+    const R_PARENT = 1;
+
+    /**
+     * Recursion constants
+     *
+     * This checks every node in the tree
+     */
+    const R_RECURSIVE = 2;
+
+    /**
+     * Recursion constants
+     *
+     * This checks every parentnode in the tree, but not leaf-nodes.
+     */
+    const R_RECURSIVEPARENTS = 3;
+
+    /**
+     * Reference to server object. 
+     * 
+     * @var Sabre_DAV_Server 
+     */
+    protected $server;
+
+    /**
+     * List of urls containing principal collections.
+     * Modify this if your principals are located elsewhere. 
+     * 
+     * @var array
+     */
+    public $principalCollectionSet = array(
+        'principals',
+    );
+
+    /**
+     * By default ACL is only enforced for nodes that have ACL support (the 
+     * ones that implement Sabre_DAVACL_IACL). For any other node, access is 
+     * always granted.
+     *
+     * To override this behaviour you can turn this setting off. This is useful 
+     * if you plan to fully support ACL in the entire tree.
+     *
+     * @var bool 
+     */
+    public $allowAccessToNodesWithoutACL = true;
+
+    /**
+     * By default nodes that are inaccessible by the user, can still be seen
+     * in directory listings (PROPFIND on parent with Depth: 1)
+     *
+     * In certain cases it's desirable to hide inaccessible nodes. Setting this 
+     * to true will cause these nodes to be hidden from directory listings.
+     * 
+     * @var bool 
+     */
+    public $hideNodesFromListings = false;
+
+    /**
+     * This string is prepended to the username of the currently logged in 
+     * user. This allows the plugin to determine the principal path based on 
+     * the username.
+     * 
+     * @var string
+     */
+    public $defaultUsernamePath = 'principals';
+
+    /**
+     * Returns a list of features added by this plugin.
+     *
+     * This list is used in the response of a HTTP OPTIONS request.
+     * 
+     * @return array 
+     */
+    public function getFeatures() {
+
+        return array('access-control');
+
+    }
+
+    /**
+     * Returns a list of available methods for a given url 
+     * 
+     * @param string $uri 
+     * @return array 
+     */
+    public function getMethods($uri) {
+
+        return array('ACL');
+
+    }
+
+    /**
+     * Returns a plugin name.
+     * 
+     * Using this name other plugins will be able to access other plugins
+     * using Sabre_DAV_Server::getPlugin 
+     * 
+     * @return string 
+     */
+    public function getPluginName() {
+
+        return 'acl';
+
+    }
+
+    /**
+     * Returns a list of reports this plugin supports.
+     *
+     * This will be used in the {DAV:}supported-report-set property.
+     * Note that you still need to subscribe to the 'report' event to actually 
+     * implement them 
+     * 
+     * @param string $uri
+     * @return array 
+     */
+    public function getSupportedReportSet($uri) {
+
+        return array(
+            '{DAV:}expand-property',
+            '{DAV:}principal-property-search',
+            '{DAV:}principal-search-property-set', 
+        );
+
+    }
+
+
+    /**
+     * Checks if the current user has the specified privilege(s). 
+     * 
+     * You can specify a single privilege, or a list of privileges.
+     * This method will throw an exception if the privilege is not available
+     * and return true otherwise.
+     *
+     * @param string $uri
+     * @param array|string $privileges
+     * @param bool $throwExceptions if set to false, this method won't through exceptions. 
+     * @throws Sabre_DAVACL_Exception_NeedPrivileges
+     * @return bool 
+     */
+    public function checkPrivileges($uri,$privileges,$recursion = self::R_PARENT, $throwExceptions = true) {
+
+        if (!is_array($privileges)) $privileges = array($privileges);
+
+        $acl = $this->getCurrentUserPrivilegeSet($uri);
+
+        if (is_null($acl)) {
+            if ($this->allowAccessToNodesWithoutACL) {
+                return true;
+            } else {
+                if ($throwExceptions) 
+                    throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$privileges);
+                else
+                    return false;
+
+            }
+        }
+
+        $failed = array();
+        foreach($privileges as $priv) {
+
+            if (!in_array($priv, $acl)) {
+                $failed[] = $priv;
+            }
+
+        }
+
+        if ($failed) {
+            if ($throwExceptions) 
+                throw new Sabre_DAVACL_Exception_NeedPrivileges($uri,$failed);
+            else
+                return false;
+        }
+        return true;
+
+    }
+
+    /**
+     * Returns the standard users' principal.
+     *
+     * This is one authorative principal url for the current user.
+     * This method will return null if the user wasn't logged in. 
+     * 
+     * @return string|null 
+     */
+    public function getCurrentUserPrincipal() {
+
+        $authPlugin = $this->server->getPlugin('auth');
+        if (is_null($authPlugin)) return null;
+
+        $userName = $authPlugin->getCurrentUser();
+        if (!$userName) return null;
+
+        return $this->defaultUsernamePath . '/' . $userName;
+
+    }
+
+    /**
+     * Returns a list of principals that's associated to the current
+     * user, either directly or through group membership. 
+     * 
+     * @return array 
+     */
+    public function getCurrentUserPrincipals() {
+
+        $currentUser = $this->getCurrentUserPrincipal();
+
+        if (is_null($currentUser)) return array();
+
+        $check = array($currentUser);
+        $principals = array($currentUser);
+
+        while(count($check)) {
+
+            $principal = array_shift($check);
+ 
+            $node = $this->server->tree->getNodeForPath($principal);
+            if ($node instanceof Sabre_DAVACL_IPrincipal) {
+                foreach($node->getGroupMembership() as $groupMember) {
+
+                    if (!in_array($groupMember, $principals)) {
+
+                        $check[] = $groupMember;
+                        $principals[] = $groupMember;
+
+                    }
+
+                }
+
+            }
+
+        }
+
+        return $principals;
+
+    }
+
+    /**
+     * Returns the supported privilege structure for this ACL plugin.
+     *
+     * See RFC3744 for more details. Currently we default on a simple,
+     * standard structure. 
+     * 
+     * @return array 
+     */
+    public function getSupportedPrivilegeSet() {
+
+        return array(
+            'privilege'  => '{DAV:}all',
+            'abstract'   => true,
+            'aggregates' => array(
+                array(
+                    'privilege'  => '{DAV:}read',
+                    'aggregates' => array(
+                        array(
+                            'privilege' => '{DAV:}read-acl',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}read-current-user-privilege-set',
+                            'abstract'  => true,
+                        ),
+                    ),
+                ), // {DAV:}read
+                array(
+                    'privilege'  => '{DAV:}write',
+                    'aggregates' => array(
+                        array(
+                            'privilege' => '{DAV:}write-acl',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}write-properties',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}write-content',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}bind',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}unbind',
+                            'abstract'  => true,
+                        ),
+                        array(
+                            'privilege' => '{DAV:}unlock',
+                            'abstract'  => true,
+                        ),
+                    ),
+                ), // {DAV:}write
+            ), 
+        ); // {DAV:}all
+
+    }
+
+    /**
+     * Returns the supported privilege set as a flat list
+     *
+     * This is much easier to parse.
+     *
+     * The returned list will be index by privilege name.
+     * The value is a struct containing the following properties:
+     *   - aggregates
+     *   - abstract
+     *   - concrete
+     * 
+     * @return array 
+     */
+    final public function getFlatPrivilegeSet() {
+
+        $privs = $this->getSupportedPrivilegeSet();
+
+        $flat = array();
+        $this->getFPSTraverse($privs, null, $flat);
+
+        return $flat;
+
+    }
+
+    /**
+     * Traverses the privilege set tree for reordering
+     *
+     * This function is solely used by getFlatPrivilegeSet, and would have been 
+     * a closure if it wasn't for the fact I need to support PHP 5.2.
+     * 
+     * @return void
+     */
+    final private function getFPSTraverse($priv, $concrete, &$flat) {
+
+        $myPriv = array(
+            'privilege' => $priv['privilege'],
+            'abstract' => isset($priv['abstract']) && $priv['abstract'],
+            'aggregates' => array(),
+            'concrete' => isset($priv['abstract']) && $priv['abstract']?$concrete:$priv['privilege'],
+        );
+
+        if (isset($priv['aggregates']))
+            foreach($priv['aggregates'] as $subPriv) $myPriv['aggregates'][] = $subPriv['privilege'];
+
+        $flat[$priv['privilege']] = $myPriv;
+
+        if (isset($priv['aggregates'])) {
+
+            foreach($priv['aggregates'] as $subPriv) {
+            
+                $this->getFPSTraverse($subPriv, $myPriv['concrete'], $flat);
+
+            }
+
+        }
+
+    }
+
+    /**
+     * Returns the full ACL list.
+     *
+     * Either a uri or a Sabre_DAV_INode may be passed.
+     *
+     * null will be returned if the node doesn't support ACLs. 
+     * 
+     * @param string|Sabre_DAV_INode $node
+     * @return array
+     */
+    public function getACL($node) {
+
+        if (is_string($node)) {
+            $node = $this->server->tree->getNodeForPath($node);
+        }
+        if ($node instanceof Sabre_DAVACL_IACL) {
+            return $node->getACL();
+        }
+        return null; 
+
+    }
+
+    /**
+     * Returns a list of privileges the current user has
+     * on a particular node.
+     *
+     * Either a uri or a Sabre_DAV_INode may be passed.
+     *
+     * null will be returned if the node doesn't support ACLs. 
+     * 
+     * @param string|Sabre_DAV_INode $node 
+     * @return array 
+     */
+    public function getCurrentUserPrivilegeSet($node) {
+
+        if (is_string($node)) {
+            $node = $this->server->tree->getNodeForPath($node);
+        }
+
+        $acl = $this->getACL($node);
+        if (is_null($acl)) return null;
+
+        $principals = $this->getCurrentUserPrincipals();
+
+        $collected = array();
+
+        foreach($acl as $ace) {
+
+            if (in_array($ace['principal'], $principals)) {
+                $collected[] = $ace;
+            }
+
+        }
+
+        // Now we deduct all aggregated privileges.
+        $flat = $this->getFlatPrivilegeSet();
+
+        $collected2 = array();
+        foreach($collected as $privilege) {
+
+            $collected2[] = $privilege['privilege'];
+            foreach($flat[$privilege['privilege']]['aggregates'] as $subPriv) {
+                if (!in_array($subPriv, $collected2)) 
+                    $collected2[] = $subPriv;
+            }
+
+        }
+
+        return $collected2;
+
+    }
+
+    /**
+     * Sets up the plugin
+     *
+     * This method is automatically called by the server class.
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    public function initialize(Sabre_DAV_Server $server) {
+
+        $this->server = $server;
+        $server->subscribeEvent('beforeGetProperties',array($this,'beforeGetProperties'));
+
+        $server->subscribeEvent('beforeMethod', array($this,'beforeMethod'),20);
+        $server->subscribeEvent('beforeBind', array($this,'beforeBind'),20);
+        $server->subscribeEvent('beforeUnbind', array($this,'beforeUnbind'),20);
+        $server->subscribeEvent('updateProperties',array($this,'updateProperties'));
+        $server->subscribeEvent('beforeUnlock', array($this,'beforeUnlock'),20);
+        $server->subscribeEvent('report',array($this,'report'));
+        $server->subscribeEvent('unknownMethod', array($this, 'unknownMethod'));
+
+        array_push($server->protectedProperties,
+            '{DAV:}alternate-URI-set',
+            '{DAV:}principal-URL',
+            '{DAV:}group-membership',
+            '{DAV:}principal-collection-set',
+            '{DAV:}current-user-principal',
+            '{DAV:}supported-privilege-set',
+            '{DAV:}current-user-privilege-set',
+            '{DAV:}acl',
+            '{DAV:}acl-restrictions',
+            '{DAV:}inherited-acl-set',
+            '{DAV:}owner',
+            '{DAV:}group'
+        );
+
+        // Automatically mapping nodes implementing IPrincipal to the 
+        // {DAV:}principal resourcetype.
+        $server->resourceTypeMapping['Sabre_DAVACL_IPrincipal'] = '{DAV:}principal';
+
+        // Mapping the group-member-set property to the HrefList property 
+        // class.
+        $server->propertyMap['{DAV:}group-member-set'] = 'Sabre_DAV_Property_HrefList';
+
+    }
+
+
+    /* {{{ Event handlers */
+
+    /**
+     * Triggered before any method is handled 
+     * 
+     * @param string $method 
+     * @param string $uri 
+     * @return void
+     */
+    public function beforeMethod($method, $uri) {
+
+        $exists = $this->server->tree->nodeExists($uri);
+
+        // If the node doesn't exists, none of these checks apply
+        if (!$exists) return;
+
+        switch($method) {
+
+            case 'GET' :
+            case 'HEAD' :
+            case 'OPTIONS' :
+                // For these 3 we only need to know if the node is readable.
+                $this->checkPrivileges($uri,'{DAV:}read');
+                break;
+
+            case 'PUT' :
+            case 'LOCK' :
+            case 'UNLOCK' : 
+                // This method requires the write-content priv if the node 
+                // already exists, and bind on the parent if the node is being 
+                // created. 
+                // The bind privilege is handled in the beforeBind event. 
+                $this->checkPrivileges($uri,'{DAV:}write-content');
+                break;
+            
+
+            case 'PROPPATCH' :
+                $this->checkPrivileges($uri,'{DAV:}write-properties');
+                break;
+
+            case 'ACL' :
+                $this->checkPrivileges($uri,'{DAV:}write-acl');
+                break;
+
+            case 'COPY' :
+            case 'MOVE' :
+                // Copy requires read privileges on the entire source tree.
+                // If the target exists write-content normally needs to be 
+                // checked, however, we're deleting the node beforehand and 
+                // creating a new one after, so this is handled by the 
+                // beforeUnbind event.
+                // 
+                // The creation of the new node is handled by the beforeBind 
+                // event.
+                //
+                // If MOVE is used beforeUnbind will also be used to check if 
+                // the sourcenode can be deleted. 
+                $this->checkPrivileges($uri,'{DAV:}read',self::R_RECURSIVE);
+
+                break;
+
+        }
+
+    }
+
+    /**
+     * Triggered before a new node is created.
+     * 
+     * This allows us to check permissions for any operation that creates a
+     * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE.
+     * 
+     * @param string $uri 
+     * @return void
+     */
+    public function beforeBind($uri) {
+
+        list($parentUri,$nodeName) = Sabre_DAV_URLUtil::splitPath($uri);
+        $this->checkPrivileges($parentUri,'{DAV:}bind');
+
+    }
+
+    /**
+     * Triggered before a node is deleted 
+     * 
+     * This allows us to check permissions for any operation that will delete 
+     * an existing node. 
+     * 
+     * @param string $uri 
+     * @return void
+     */
+    public function beforeUnbind($uri) {
+
+        list($parentUri,$nodeName) = Sabre_DAV_URLUtil::splitPath($uri);
+        $this->checkPrivileges($parentUri,'{DAV:}unbind',self::R_RECURSIVEPARENTS);
+
+    }
+
+    /**
+     * Triggered before a node is unlocked. 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_Locks_LockInfo $lock
+     * @TODO: not yet implemented 
+     * @return void
+     */
+    public function beforeUnlock($uri, Sabre_DAV_Locks_LockInfo $lock) {
+           
+
+    }
+
+    /**
+     * Triggered before properties are looked up in specific nodes. 
+     * 
+     * @param string $uri 
+     * @param Sabre_DAV_INode $node 
+     * @param array $requestedProperties 
+     * @param array $returnedProperties
+     * @TODO really should be broken into multiple methods, or even a class. 
+     * @return void
+     */
+    public function beforeGetProperties($uri, Sabre_DAV_INode $node, &$requestedProperties, &$returnedProperties) {
+
+        // Checking the read permission
+        if (!$this->checkPrivileges($uri,'{DAV:}read',self::R_PARENT,false)) {
+
+            // User is not allowed to read properties
+            if ($this->hideNodesFromListings) {
+                return false;
+            }
+
+            // Marking all requested properties as '403'.
+            foreach($requestedProperties as $key=>$requestedProperty) {
+                unset($requestedProperties[$key]);
+                $returnedProperties[403][$requestedProperty] = null;
+            }
+            return;
+
+        } 
+
+        /* Adding principal properties */
+        if ($node instanceof Sabre_DAVACL_IPrincipal) {
+
+            if (false !== ($index = array_search('{DAV:}alternate-URI-set', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}alternate-URI-set'] = new Sabre_DAV_Property_HrefList($node->getAlternateUriSet());
+
+            }
+            if (false !== ($index = array_search('{DAV:}principal-URL', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}principal-URL'] = new Sabre_DAV_Property_Href($node->getPrincipalUrl() . '/');
+
+            }
+            if (false !== ($index = array_search('{DAV:}group-member-set', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}group-member-set'] = new Sabre_DAV_Property_HrefList($node->getGroupMemberSet());
+
+            }
+            if (false !== ($index = array_search('{DAV:}group-membership', $requestedProperties))) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[200]['{DAV:}group-membership'] = new Sabre_DAV_Property_HrefList($node->getGroupMembership());
+
+            }
+
+            if (false !== ($index = array_search('{DAV:}displayname', $requestedProperties))) {
+
+                $returnedProperties[200]['{DAV:}displayname'] = $node->getDisplayName();
+
+            }
+
+        }
+        if (false !== ($index = array_search('{DAV:}principal-collection-set', $requestedProperties))) {
+
+            unset($requestedProperties[$index]);
+            $val = $this->principalCollectionSet;
+            // Ensuring all collections end with a slash
+            foreach($val as $k=>$v) $val[$k] = $v . '/';
+            $returnedProperties[200]['{DAV:}principal-collection-set'] = new Sabre_DAV_Property_HrefList($val);
+
+        }
+        if (false !== ($index = array_search('{DAV:}current-user-principal', $requestedProperties))) {
+
+            unset($requestedProperties[$index]);
+            if ($url = $this->getCurrentUserPrincipal()) {
+                $returnedProperties[200]['{DAV:}current-user-principal'] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::HREF, $url . '/');
+            } else {
+                $returnedProperties[200]['{DAV:}current-user-principal'] = new Sabre_DAVACL_Property_Principal(Sabre_DAVACL_Property_Principal::UNAUTHENTICATED);
+            }
+
+        }
+        if (false !== ($index = array_search('{DAV:}supported-privilege-set', $requestedProperties))) {
+
+            unset($requestedProperties[$index]);
+            $returnedProperties[200]['{DAV:}supported-privilege-set'] = new Sabre_DAVACL_Property_SupportedPrivilegeSet($this->getSupportedPrivilegeSet());
+
+        }
+        if (false !== ($index = array_search('{DAV:}current-user-privilege-set', $requestedProperties))) {
+
+            if (!$this->checkPrivileges($uri, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
+                $returnedProperties[403]['{DAV:}current-user-privilege-set'] = null;
+                unset($requestedProperties[$index]);
+            } else {
+                $val = $this->getCurrentUserPrivilegeSet($node);
+                if (!is_null($val)) {
+                    unset($requestedProperties[$index]);
+                    $returnedProperties[200]['{DAV:}current-user-privilege-set'] = new Sabre_DAVACL_Property_CurrentUserPrivilegeSet($val);
+                }
+            }
+
+        }
+
+        /* The ACL property contains all the permissions */
+        if (false !== ($index = array_search('{DAV:}acl', $requestedProperties))) {
+
+            if (!$this->checkPrivileges($uri, '{DAV:}read-acl', self::R_PARENT, false)) {
+
+                unset($requestedProperties[$index]);
+                $returnedProperties[403]['{DAV:}acl'] = null;
+
+            } else {
+
+                $acl = $this->getACL($node);
+                if (!is_null($acl)) {
+                    unset($requestedProperties[$index]);
+                    $returnedProperties[200]['{DAV:}acl'] = new Sabre_DAVACL_Property_Acl($this->getACL($node));
+                }
+
+            }
+
+        }
+
+    }
+
+    /**
+     * This method intercepts PROPPATCH methods and make sure the 
+     * group-member-set is updated correctly. 
+     * 
+     * @param array $propertyDelta 
+     * @param array $result 
+     * @param Sabre_DAV_INode $node 
+     * @return void
+     */
+    public function updateProperties(&$propertyDelta, &$result, Sabre_DAV_INode $node) {
+
+        if (!array_key_exists('{DAV:}group-member-set', $propertyDelta))
+            return;
+
+        if (is_null($propertyDelta['{DAV:}group-member-set'])) {
+            $memberSet = array();
+        } elseif ($propertyDelta['{DAV:}group-member-set'] instanceof Sabre_DAV_Property_HrefList) {
+            $memberSet = $propertyDelta['{DAV:}group-member-set']->getHrefs();
+        } else {
+            throw new Sabre_DAV_Exception('The group-member-set property MUST be an instance of Sabre_DAV_Property_HrefList or null');
+        }
+
+        if (!($node instanceof Sabre_DAVACL_IPrincipal)) {
+            $result[403]['{DAV:}group-member-set'] = null;
+            unset($propertyDelta['{DAV:}group-member-set']);
+
+            // Returning false will stop the updateProperties process
+            return false;
+        }
+
+        $node->setGroupMemberSet($memberSet);
+        
+        $result[200]['{DAV:}group-member-set'] = null;
+        unset($propertyDelta['{DAV:}group-member-set']);
+
+    }
+
+    /**
+     * This method handels HTTP REPORT requests 
+     * 
+     * @param string $reportName 
+     * @param DOMNode $dom 
+     * @return void
+     */
+    public function report($reportName, $dom) {
+
+        switch($reportName) {
+
+            case '{DAV:}principal-property-search' :
+                $this->principalPropertySearchReport($dom);
+                return false;
+            case '{DAV:}principal-search-property-set' :
+                $this->principalSearchPropertySetReport($dom);
+                return false; 
+            case '{DAV:}expand-property' :
+                $this->expandPropertyReport($dom);
+                return false;
+
+        }
+
+    }
+
+    /**
+     * This event is triggered for any HTTP method that is not known by the 
+     * webserver. 
+     *
+     * @param string $method 
+     * @param string $uri 
+     * @return void
+     */
+    public function unknownMethod($method, $uri) {
+
+        if ($method!=='ACL') return;
+
+        $this->httpACL($uri);
+        return false;
+
+    }
+
+    /**
+     * This method is responsible for handling the 'ACL' event.
+     *
+     * @param string $uri
+     * @return void
+     */
+    public function httpACL($uri) { 
+
+        $body = $this->server->httpRequest->getBody(true);
+        $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body);
+
+        $newAcl = 
+            Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild)
+            ->getPrivileges();
+
+        // Normalizing urls
+        foreach($newAcl as $k=>$newAce) {
+            $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
+        }
+
+        $node = $this->server->tree->getNodeForPath($uri);
+
+        if (!($node instanceof Sabre_DAVACL_IACL)) {
+            throw new Sabre_DAV_Exception_MethodNotAllowed('This node does not support the ACL method');
+        }
+
+        $oldAcl = $this->getACL($node);
+
+        $supportedPrivileges = $this->getFlatPrivilegeSet(); 
+
+        /* Checking if protected principals from the existing principal set are 
+           not overwritten. */
+        foreach($oldAcl as $k=>$oldAce) {
+
+            if (!isset($oldAce['protected']) || !$oldAce['protected']) continue; 
+
+            $found = false;
+            foreach($newAcl as $newAce) {
+                if (
+                    $newAce['privilege'] === $oldAce['privilege'] &&
+                    $newAce['principal'] === $oldAce['principal'] &&
+                    $newAce['protected']
+                ) 
+                $found = true;
+            }
+
+            if (!$found) 
+                throw new Sabre_DAVACL_Exception_AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
+
+        }
+
+        foreach($newAcl as $k=>$newAce) {
+
+            // Do we recognize the privilege
+            if (!isset($supportedPrivileges[$newAce['privilege']])) {
+                throw new Sabre_DAVACL_Exception_NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
+            }
+
+            if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
+                throw new Sabre_DAVACL_Exception_NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege');
+            }
+
+            // Looking up the principal
+            try {
+                $principal = $this->server->tree->getNodeForPath($newAce['principal']);
+            } catch (Sabre_DAV_Exception_FileNotFound $e) {
+                throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
+            }
+            if (!($principal instanceof Sabre_DAVACL_IPrincipal)) {
+                throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
+            } 
+
+        }
+        $node->setACL($newAcl);
+
+    }
+
+    /* }}} */
+
+    /* Reports {{{ */
+
+    /**
+     * The expand-property report is defined in RFC3253 section 3-8. 
+     *
+     * This report is very similar to a standard PROPFIND. The difference is
+     * that it has the additional ability to look at properties containing a
+     * {DAV:}href element, follow that property and grab additional elements
+     * there.
+     *
+     * Other rfc's, such as ACL rely on this report, so it made sense to put
+     * it in this plugin.
+     *
+     * @param DOMElement $dom 
+     * @return void
+     */
+    protected function expandPropertyReport($dom) {
+
+        $requestedProperties = $this->parseExpandPropertyReportRequest($dom->firstChild->firstChild);
+        $depth = $this->server->getHTTPDepth(0);
+        $requestUri = $this->server->getRequestUri();
+
+        $result = $this->expandProperties($requestUri,$requestedProperties,$depth);
+
+        $dom = new DOMDocument('1.0','utf-8');
+        $dom->formatOutput = true;
+        $multiStatus = $dom->createElement('d:multistatus');
+        $dom->appendChild($multiStatus);
+
+        // Adding in default namespaces
+        foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
+
+            $multiStatus->setAttribute('xmlns:' . $prefix,$namespace);
+
+        }
+
+        foreach($result as $response) {
+            $response->serialize($this->server, $multiStatus);
+        }
+
+        $xml = $dom->saveXML();
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->sendBody($xml);
+
+    }
+
+    /**
+     * This method is used by expandPropertyReport to parse
+     * out the entire HTTP request.
+     * 
+     * @param DOMElement $node 
+     * @return array 
+     */
+    protected function parseExpandPropertyReportRequest($node) {
+
+        $requestedProperties = array();
+        do {
+
+            if (Sabre_DAV_XMLUtil::toClarkNotation($node)!=='{DAV:}property') continue;
+                
+            if ($node->firstChild) {
+                
+                $children = $this->parseExpandPropertyReportRequest($node->firstChild);
+
+            } else {
+
+                $children = array();
+
+            }
+
+            $namespace = $node->getAttribute('namespace');
+            if (!$namespace) $namespace = 'DAV:';
+
+            $propName = '{'.$namespace.'}' . $node->getAttribute('name');
+            $requestedProperties[$propName] = $children; 
+
+        } while ($node = $node->nextSibling);
+
+        return $requestedProperties;
+
+    }
+
+    /**
+     * This method expands all the properties and returns
+     * a list with property values
+     *
+     * @param array $path
+     * @param array $requestedProperties the list of required properties
+     * @param array $depth
+     */
+    protected function expandProperties($path,array $requestedProperties,$depth) { 
+
+        $foundProperties = $this->server->getPropertiesForPath($path,array_keys($requestedProperties),$depth);
+
+        $result = array();
+
+        foreach($foundProperties as $node) {
+
+            foreach($requestedProperties as $propertyName=>$childRequestedProperties) {
+
+                // We're only traversing if sub-properties were requested
+                if(count($childRequestedProperties)===0) continue;
+                
+                // We only have to do the expansion if the property was found
+                // and it contains an href element.
+                if (!array_key_exists($propertyName,$node[200])) continue;
+
+                if ($node[200][$propertyName] instanceof Sabre_DAV_Property_IHref) {
+                    $hrefs = array($node[200][$propertyName]->getHref());
+                } elseif ($node[200][$propertyName] instanceof Sabre_DAV_Property_HrefList) {
+                    $hrefs = $node[200][$propertyName]->getHrefs();
+                }
+
+                $childProps = array();
+                foreach($hrefs as $href) {
+                    $childProps = array_merge($childProps, $this->expandProperties($href,$childRequestedProperties,0));
+                }
+                $node[200][$propertyName] = new Sabre_DAV_Property_ResponseList($childProps);
+
+            }
+            $result[] = new Sabre_DAV_Property_Response($path, $node);
+
+        }
+
+        return $result;
+
+    }
+
+    /**
+     * principalSearchPropertySetReport
+     *
+     * This method responsible for handing the 
+     * {DAV:}principal-search-property-set report. This report returns a list
+     * of properties the client may search on, using the
+     * {DAV:}principal-property-search report.
+     * 
+     * @param DOMDocument $dom 
+     * @return void
+     */
+    protected function principalSearchPropertySetReport(DOMDocument $dom) {
+
+        $searchProperties = array(
+            '{DAV:}displayname' => 'display name'
+        );
+
+        $httpDepth = $this->server->getHTTPDepth(0);
+        if ($httpDepth!==0) {
+            throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0');
+        }
+        
+        if ($dom->firstChild->hasChildNodes()) 
+            throw new Sabre_DAV_Exception_BadRequest('The principal-search-property-set report element is not allowed to have child elements'); 
+
+        $dom = new DOMDocument('1.0','utf-8');
+        $dom->formatOutput = true;
+        $root = $dom->createElement('d:principal-search-property-set');
+        $dom->appendChild($root);
+        // Adding in default namespaces
+        foreach($this->server->xmlNamespaces as $namespace=>$prefix) {
+
+            $root->setAttribute('xmlns:' . $prefix,$namespace);
+
+        }
+
+        $nsList = $this->server->xmlNamespaces; 
+
+        foreach($searchProperties as $propertyName=>$description) {
+
+            $psp = $dom->createElement('d:principal-search-property');
+            $root->appendChild($psp);
+
+            $prop = $dom->createElement('d:prop');
+            $psp->appendChild($prop);
+  
+            $propName = null;
+            preg_match('/^{([^}]*)}(.*)$/',$propertyName,$propName);
+
+            $currentProperty = $dom->createElement($nsList[$propName[1]] . ':' . $propName[2]);
+            $prop->appendChild($currentProperty);
+
+            $descriptionElem = $dom->createElement('d:description');
+            $descriptionElem->setAttribute('xml:lang','en');
+            $descriptionElem->appendChild($dom->createTextNode($description));
+            $psp->appendChild($descriptionElem);
+
+
+        }
+
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendStatus(200);
+        $this->server->httpResponse->sendBody($dom->saveXML());
+
+    }
+
+    /**
+     * principalPropertySearchReport
+     *
+     * This method is reponsible for handing the 
+     * {DAV:}principal-property-search report. This report can be used for 
+     * clients to search for groups of principals, based on the value of one
+     * or more properties.
+     * 
+     * @param DOMDocument $dom 
+     * @return void
+     */
+    protected function principalPropertySearchReport(DOMDocument $dom) {
+
+        $searchableProperties = array(
+            '{DAV:}displayname' => 'display name'
+
+        );
+
+        list($searchProperties, $requestedProperties, $applyToPrincipalCollectionSet) = $this->parsePrincipalPropertySearchReportRequest($dom);
+
+        $result = array();
+
+        if ($applyToPrincipalCollectionSet) {
+            $uris = array();
+        } else {
+            $uris = array($this->server->getRequestUri());
+        }
+
+        $lookupResults = array();
+        foreach($uris as $uri) {
+
+            $p = array_keys($searchProperties);
+            $p[] = '{DAV:}resourcetype';
+            $r = $this->server->getPropertiesForPath($uri, $p, 1);
+
+            // The first item in the results is the parent, so we get rid of it.
+            array_shift($r);
+            $lookupResults = array_merge($lookupResults, $r);
+        } 
+
+        $matches = array();
+
+        foreach($lookupResults as $lookupResult) {
+
+            // We're only looking for principals 
+            if (!isset($lookupResult[200]['{DAV:}resourcetype']) || 
+                (!($lookupResult[200]['{DAV:}resourcetype'] instanceof Sabre_DAV_Property_ResourceType)) ||
+                !$lookupResult[200]['{DAV:}resourcetype']->is('{DAV:}principal')) continue;
+
+            foreach($searchProperties as $searchProperty=>$searchValue) {
+                if (!isset($searchableProperties[$searchProperty])) {
+                    // If a property is not 'searchable', the spec dictates 
+                    // this is not a match. 
+                    continue;
+                }
+
+                if (isset($lookupResult[200][$searchProperty]) &&
+                    mb_stripos($lookupResult[200][$searchProperty], $searchValue, 0, 'UTF-8')!==false) {
+                        $matches[] = $lookupResult['href'];
+                }
+
+            }
+
+        }
+
+        $matchProperties = array();
+
+        foreach($matches as $match) {
+            
+           list($result) = $this->server->getPropertiesForPath($match, $requestedProperties, 0);
+           $matchProperties[] = $result;
+
+        }
+
+        $xml = $this->server->generateMultiStatus($matchProperties);
+        $this->server->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8');
+        $this->server->httpResponse->sendStatus(207);
+        $this->server->httpResponse->sendBody($xml);
+
+    }
+
+    /**
+     * parsePrincipalPropertySearchReportRequest
+     *
+     * This method parses the request body from a
+     * {DAV:}principal-property-search report.
+     *
+     * This method returns an array with two elements:
+     *  1. an array with properties to search on, and their values
+     *  2. a list of propertyvalues that should be returned for the request.
+     * 
+     * @param DOMDocument $dom 
+     * @return array 
+     */
+    protected function parsePrincipalPropertySearchReportRequest($dom) {
+
+        $httpDepth = $this->server->getHTTPDepth(0);
+        if ($httpDepth!==0) {
+            throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0');
+        }
+
+        $searchProperties = array();
+
+        $applyToPrincipalCollectionSet = false;
+
+        // Parsing the search request
+        foreach($dom->firstChild->childNodes as $searchNode) {
+
+            if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set')
+                $applyToPrincipalCollectionSet = true;
+
+            if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode)!=='{DAV:}property-search')
+                continue;
+
+            $propertyName = null;
+            $propertyValue = null;
+
+            foreach($searchNode->childNodes as $childNode) {
+
+                switch(Sabre_DAV_XMLUtil::toClarkNotation($childNode)) {
+
+                    case '{DAV:}prop' :
+                        $property = Sabre_DAV_XMLUtil::parseProperties($searchNode);
+                        reset($property); 
+                        $propertyName = key($property);
+                        break;
+
+                    case '{DAV:}match' :
+                        $propertyValue = $childNode->textContent;
+                        break;
+
+                }
+
+
+            }
+
+            if (is_null($propertyName) || is_null($propertyValue))
+                throw new Sabre_DAV_Exception_BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue);
+
+            $searchProperties[$propertyName] = $propertyValue;
+
+        }
+
+        return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet);
+
+    }
+
+
+    /* }}} */
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Principal.php b/3rdparty/Sabre/DAVACL/Principal.php
new file mode 100644
index 0000000000000000000000000000000000000000..158b271058c704246667c696087d920db3e40516
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Principal.php
@@ -0,0 +1,256 @@
+<?php
+
+/**
+ * Principal class
+ *
+ * This class is a representation of a simple principal
+ * 
+ * Many WebDAV specs require a user to show up in the directory 
+ * structure. 
+ *
+ * This principal also has basic ACL settings, only allowing the principal
+ * access it's own principal. 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Principal extends Sabre_DAV_Node implements Sabre_DAVACL_IPrincipal, Sabre_DAV_IProperties, Sabre_DAVACL_IACL {
+
+    /**
+     * Struct with principal information.
+     *
+     * @var array 
+     */
+    protected $principalProperties;
+
+    /**
+     * Principal backend 
+     * 
+     * @var Sabre_DAVACL_IPrincipalBackend 
+     */
+    protected $principalBackend;
+
+    /**
+     * Creates the principal object 
+     *
+     * @param array $principalProperties
+     */
+    public function __construct(Sabre_DAVACL_IPrincipalBackend $principalBackend, array $principalProperties = array()) {
+
+        if (!isset($principalProperties['uri'])) {
+            throw new Sabre_DAV_Exception('The principal properties must at least contain the \'uri\' key');
+        }
+        $this->principalBackend = $principalBackend;
+        $this->principalProperties = $principalProperties;
+
+    }
+
+    /**
+     * Returns the full principal url 
+     * 
+     * @return string 
+     */
+    public function getPrincipalUrl() {
+
+        return $this->principalProperties['uri'];
+
+    } 
+
+    /**
+     * Returns a list of altenative urls for a principal
+     * 
+     * This can for example be an email address, or ldap url.
+     * 
+     * @return array 
+     */
+    public function getAlternateUriSet() {
+
+        if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) {
+            return array('mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address']);
+        } else {
+            return array();
+        }
+
+    }
+
+    /**
+     * Returns the list of group members
+     * 
+     * If this principal is a group, this function should return
+     * all member principal uri's for the group. 
+     * 
+     * @return array
+     */
+    public function getGroupMemberSet() {
+
+        return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']);
+
+    }
+
+    /**
+     * Returns the list of groups this principal is member of
+     * 
+     * If this principal is a member of a (list of) groups, this function
+     * should return a list of principal uri's for it's members. 
+     * 
+     * @return array 
+     */
+    public function getGroupMembership() {
+
+        return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']);
+
+    }
+
+
+    /**
+     * Sets a list of group members
+     *
+     * If this principal is a group, this method sets all the group members.
+     * The list of members is always overwritten, never appended to.
+     * 
+     * This method should throw an exception if the members could not be set. 
+     * 
+     * @param array $principals 
+     * @return void 
+     */
+    public function setGroupMemberSet(array $groupMembers) {
+
+        $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers);
+
+    }
+
+
+    /**
+     * Returns this principals name.
+     * 
+     * @return string 
+     */
+    public function getName() {
+
+        $uri = $this->principalProperties['uri'];
+        list(, $name) = Sabre_DAV_URLUtil::splitPath($uri);
+
+        return $name;
+
+    }
+
+    /**
+     * Returns the name of the user 
+     * 
+     * @return void
+     */
+    public function getDisplayName() {
+
+        if (isset($this->principalProperties['{DAV:}displayname'])) {
+            return $this->principalProperties['{DAV:}displayname'];
+        } else {
+            return $this->getName();
+        }
+
+    }
+
+    /**
+     * Returns a list of properties 
+     * 
+     * @param array $requestedProperties 
+     * @return void
+     */
+    public function getProperties($requestedProperties) {
+
+        $newProperties = array();
+        foreach($requestedProperties as $propName) {
+            
+            if (isset($this->principalProperties[$propName])) {
+                $newProperties[$propName] = $this->principalProperties[$propName];
+            }
+
+        }
+
+        return $newProperties;
+        
+    }
+
+    /**
+     * Updates this principals properties.
+     *
+     * Currently this is not supported
+     * 
+     * @param array $properties
+     * @see Sabre_DAV_IProperties::updateProperties
+     * @return bool|array 
+     */
+    public function updateProperties($properties) {
+
+        return false;
+
+    }
+
+    /**
+     * Returns the owner principal
+     *
+     * This must be a url to a principal, or null if there's no owner 
+     * 
+     * @return string|null
+     */
+    public function getOwner() {
+
+        return $this->principalProperties['uri'];
+
+
+    }
+
+    /**
+     * Returns a group principal
+     *
+     * This must be a url to a principal, or null if there's no owner
+     * 
+     * @return string|null 
+     */
+    public function getGroup() {
+
+        return null;
+
+    }
+
+    /**
+     * Returns a list of ACE's for this node.
+     *
+     * Each ACE has the following properties:
+     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are 
+     *     currently the only supported privileges
+     *   * 'principal', a url to the principal who owns the node
+     *   * 'protected' (optional), indicating that this ACE is not allowed to 
+     *      be updated. 
+     * 
+     * @return array 
+     */
+    public function getACL() {
+
+        return array(
+            array(
+                'privilege' => '{DAV:}read',
+                'principal' => $this->principalProperties['uri'],
+                'protected' => true,
+            ),
+        );
+
+    }
+
+    /**
+     * Updates the ACL
+     *
+     * This method will receive a list of new ACE's. 
+     * 
+     * @param array $acl 
+     * @return void
+     */
+    public function setACL(array $acl) {
+
+        throw new Sabre_DAV_Exception_MethodNotAllowed('Updating ACLs is not allowed here');
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php
new file mode 100644
index 0000000000000000000000000000000000000000..55bd1903c9bfe5a626524bcf9f6d01b17eeca78a
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/PrincipalBackend/PDO.php
@@ -0,0 +1,206 @@
+<?php
+
+/**
+ * PDO principal backend
+ *
+ * This is a simple principal backend that maps exactly to the users table, as 
+ * used by Sabre_DAV_Auth_Backend_PDO.
+ *
+ * It assumes all principals are in a single collection. The default collection 
+ * is 'principals/', but this can be overriden.
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_PrincipalBackend_PDO implements Sabre_DAVACL_IPrincipalBackend {
+
+    /**
+     * pdo 
+     * 
+     * @var PDO 
+     */
+    protected $pdo;
+
+    /**
+     * PDO table name for 'principals' 
+     * 
+     * @var string 
+     */
+    protected $tableName;
+
+    /**
+     * PDO table name for 'group members' 
+     * 
+     * @var string 
+     */
+    protected $groupMembersTableName;
+
+    /**
+     * Sets up the backend.
+     * 
+     * @param PDO $pdo
+     * @param string $tableName 
+     */
+    public function __construct(PDO $pdo, $tableName = 'principals', $groupMembersTableName = 'groupmembers') {
+
+        $this->pdo = $pdo;
+        $this->tableName = $tableName;
+        $this->groupMembersTableName = $groupMembersTableName;
+
+    } 
+
+
+    /**
+     * Returns a list of principals based on a prefix.
+     *
+     * This prefix will often contain something like 'principals'. You are only 
+     * expected to return principals that are in this base path.
+     *
+     * You are expected to return at least a 'uri' for every user, you can 
+     * return any additional properties if you wish so. Common properties are:
+     *   {DAV:}displayname 
+     *   {http://sabredav.org/ns}email-address - This is a custom SabreDAV 
+     *     field that's actualy injected in a number of other properties. If
+     *     you have an email address, use this property.
+     * 
+     * @param string $prefixPath 
+     * @return array 
+     */
+    public function getPrincipalsByPrefix($prefixPath) {
+        $result = $this->pdo->query('SELECT uri, email, displayname FROM `'. $this->tableName . '`');
+
+        $principals = array();
+
+        while($row = $result->fetch(PDO::FETCH_ASSOC)) {
+
+            // Checking if the principal is in the prefix
+            list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']);
+            if ($rowPrefix !== $prefixPath) continue;
+
+            $principals[] = array(
+                'uri' => $row['uri'],
+                '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']),
+                '{http://sabredav.org/ns}email-address' => $row['email'],
+            );
+
+        }
+
+        return $principals;
+
+    }
+
+    /**
+     * Returns a specific principal, specified by it's path.
+     * The returned structure should be the exact same as from 
+     * getPrincipalsByPrefix. 
+     * 
+     * @param string $path 
+     * @return array 
+     */
+    public function getPrincipalByPath($path) {
+
+        $stmt = $this->pdo->prepare('SELECT id, uri, email, displayname FROM `'.$this->tableName.'` WHERE uri = ?');
+        $stmt->execute(array($path));
+
+        $users = array();
+
+        $row = $stmt->fetch(PDO::FETCH_ASSOC);
+        if (!$row) return;
+
+        return array(
+            'id'  => $row['id'],
+            'uri' => $row['uri'],
+            '{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri']),
+            '{http://sabredav.org/ns}email-address' => $row['email'],
+        );
+
+    }
+
+    /**
+     * Returns the list of members for a group-principal 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    public function getGroupMemberSet($principal) {
+
+        $principal = $this->getPrincipalByPath($principal);
+        if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+
+        $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM `'.$this->groupMembersTableName.'` AS groupmembers LEFT JOIN `'.$this->tableName.'` AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
+        $stmt->execute(array($principal['id']));
+
+        $result = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $result[] = $row['uri'];
+        }
+        return $result;
+    
+    }
+
+    /**
+     * Returns the list of groups a principal is a member of 
+     * 
+     * @param string $principal 
+     * @return array 
+     */
+    public function getGroupMembership($principal) {
+
+        $principal = $this->getPrincipalByPath($principal);
+        if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+
+        $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM `'.$this->groupMembersTableName.'` AS groupmembers LEFT JOIN `'.$this->tableName.'` AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?');
+        $stmt->execute(array($principal['id']));
+
+        $result = array();
+        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            $result[] = $row['uri'];
+        }
+        return $result;
+
+    }
+
+    /**
+     * Updates the list of group members for a group principal.
+     *
+     * The principals should be passed as a list of uri's. 
+     * 
+     * @param string $principal 
+     * @param array $members 
+     * @return void
+     */
+    public function setGroupMemberSet($principal, array $members) {
+
+        // Grabbing the list of principal id's.
+        $stmt = $this->pdo->prepare('SELECT id, uri FROM `'.$this->tableName.'` WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');');
+        $stmt->execute(array_merge(array($principal), $members));
+
+        $memberIds = array();
+        $principalId = null;
+
+        while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
+            if ($row['uri'] == $principal) {
+                $principalId = $row['id'];
+            } else {
+                $memberIds[] = $row['id'];
+            }
+        }
+        if (!$principalId) throw new Sabre_DAV_Exception('Principal not found');
+
+        // Wiping out old members
+        $stmt = $this->pdo->prepare('DELETE FROM `'.$this->groupMembersTableName.'` WHERE principal_id = ?;');
+        $stmt->execute(array($principalId));
+
+        foreach($memberIds as $memberId) {
+
+            $stmt = $this->pdo->prepare('INSERT INTO `'.$this->groupMembersTableName.'` (principal_id, member_id) VALUES (?, ?);');
+            $stmt->execute(array($principalId, $memberId));
+
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/PrincipalCollection.php b/3rdparty/Sabre/DAVACL/PrincipalCollection.php
new file mode 100644
index 0000000000000000000000000000000000000000..3cc0ae84621aed9862701d1cb9b807922c9bd620
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/PrincipalCollection.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * Principals Collection
+ *
+ * This collection represents a list of users. It uses
+ * Sabre_DAV_Auth_Backend to determine which users are available on the list.
+ *
+ * The users are instances of Sabre_DAV_Auth_Principal
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_PrincipalCollection extends Sabre_DAVACL_AbstractPrincipalCollection {
+
+    /**
+     * This method returns a node for a principal.
+     *
+     * The passed array contains principal information, and is guaranteed to
+     * at least contain a uri item. Other properties may or may not be
+     * supplied by the authentication backend.
+     * 
+     * @param array $principal 
+     * @return Sabre_DAV_INode 
+     */
+    public function getChildForPrincipal(array $principal) {
+
+        return new Sabre_DAVACL_Principal($this->principalBackend, $principal);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Property/Acl.php b/3rdparty/Sabre/DAVACL/Property/Acl.php
new file mode 100644
index 0000000000000000000000000000000000000000..e41e7411310af9e7286da705c45ae587ad9431bc
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/Acl.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * This class represents the {DAV:}acl property 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Property_Acl extends Sabre_DAV_Property {
+
+    /**
+     * List of privileges 
+     * 
+     * @var array 
+     */
+    private $privileges;
+
+    /**
+     * Wether or not the server base url is required to be prefixed when 
+     * serializing the property. 
+     * 
+     * @var boolean 
+     */
+    private $prefixBaseUrl;
+
+    /**
+     * Constructor
+     *
+     * This object requires a structure similar to the return value from 
+     * Sabre_DAVACL_Plugin::getACL().
+     *
+     * Each privilege is a an array with at least a 'privilege' property, and a 
+     * 'principal' property. A privilege may have a 'protected' property as 
+     * well. 
+     *
+     * The prefixBaseUrl should be set to false, if the supplied principal urls 
+     * are already full urls. If this is kept to true, the servers base url 
+     * will automatically be prefixed. 
+     *
+     * @param bool $prefixBaseUrl 
+     * @param array $privileges 
+     */
+    public function __construct(array $privileges, $prefixBaseUrl = true) {
+
+        $this->privileges = $privileges;
+        $this->prefixBaseUrl = $prefixBaseUrl;
+
+    }
+
+    /**
+     * Returns the list of privileges for this property 
+     * 
+     * @return array 
+     */
+    public function getPrivileges() {
+
+        return $this->privileges;
+
+    }
+
+    /**
+     * Serializes the property into a DOMElement 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        foreach($this->privileges as $ace) {
+
+            $this->serializeAce($doc, $node, $ace, $server);
+
+        }
+
+    }
+
+    /**
+     * Unserializes the {DAV:}acl xml element. 
+     * 
+     * @param DOMElement $dom 
+     * @return Sabre_DAVACL_Property_Acl 
+     */
+    static public function unserialize(DOMElement $dom) {
+
+        $privileges = array();
+        $xaces = $dom->getElementsByTagNameNS('urn:DAV','ace');
+        for($ii=0; $ii < $xaces->length; $ii++) {
+
+            $xace = $xaces->item($ii);
+            $principal = $xace->getElementsByTagNameNS('urn:DAV','principal');
+            if ($principal->length !== 1) {
+                throw new Sabre_DAV_Exception_BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
+            }
+            $principal = Sabre_DAVACL_Property_Principal::unserialize($principal->item(0));
+
+            if ($principal->getType()!==Sabre_DAVACL_Property_Principal::HREF) {
+                throw new Sabre_DAV_Exception_NotImplemented('Currently only uri based principals are support, {DAV:}all, {DAV:}unauthenticated and {DAV:}authenticated are not implemented yet');
+            }
+
+            $principal = $principal->getHref();
+            $protected = false;
+
+            if ($xace->getElementsByTagNameNS('urn:DAV','protected')->length > 0) {
+                $protected = true;
+            }
+
+            $grants = $xace->getElementsByTagNameNS('urn:DAV','grant');
+            if ($grants->length < 1) {
+                throw new Sabre_DAV_Exception_NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
+            }
+            $grant = $grants->item(0);
+
+            $xprivs = $grant->getElementsByTagNameNS('urn:DAV','privilege');
+            for($jj=0; $jj<$xprivs->length; $jj++) {
+
+                $xpriv = $xprivs->item($jj);
+
+                $privilegeName = null;
+
+                for ($kk=0;$kk<$xpriv->childNodes->length;$kk++) {
+
+                    $childNode = $xpriv->childNodes->item($kk);
+                    if ($t = Sabre_DAV_XMLUtil::toClarkNotation($childNode)) {
+                        $privilegeName = $t;
+                        break;
+                    }
+                }
+                if (is_null($privilegeName)) {
+                    throw new Sabre_DAV_Exception_BadRequest('{DAV:}privilege elements must have a privilege element contained within them.');
+                }
+
+                $privileges[] = array(
+                    'principal' => $principal,
+                    'protected' => $protected,
+                    'privilege' => $privilegeName,
+                );
+
+            } 
+
+        }
+
+        return new self($privileges);
+
+    }
+
+    /**
+     * Serializes a single access control entry. 
+     * 
+     * @param DOMDocument $doc 
+     * @param DOMElement $node 
+     * @param array $ace
+     * @param Sabre_DAV_Server $server 
+     * @return void
+     */
+    private function serializeAce($doc,$node,$ace, $server) {
+
+        $xace  = $doc->createElementNS('DAV:','d:ace');
+        $node->appendChild($xace);
+
+        $principal = $doc->createElementNS('DAV:','d:principal');
+        $xace->appendChild($principal);
+        $principal->appendChild($doc->createElementNS('DAV:','d:href',($this->prefixBaseUrl?$server->getBaseUri():'') . $ace['principal'] . '/'));
+
+        $grant = $doc->createElementNS('DAV:','d:grant');
+        $xace->appendChild($grant);
+
+        $privParts = null;
+
+        preg_match('/^{([^}]*)}(.*)$/',$ace['privilege'],$privParts);
+
+        $xprivilege = $doc->createElementNS('DAV:','d:privilege');
+        $grant->appendChild($xprivilege);
+
+        $xprivilege->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
+
+        if (isset($ace['protected']) && $ace['protected'])
+            $xace->appendChild($doc->createElement('d:protected'));
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
new file mode 100644
index 0000000000000000000000000000000000000000..72274597b3194c5309039799b9cad51417b83cb8
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/CurrentUserPrivilegeSet.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+ * CurrentUserPrivilegeSet
+ * 
+ * This class represents the current-user-privilege-set property. When 
+ * requested, it contain all the privileges a user has on a specific node. 
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Property_CurrentUserPrivilegeSet extends Sabre_DAV_Property {
+
+    /**
+     * List of privileges 
+     * 
+     * @var array 
+     */
+    private $privileges;
+
+    /**
+     * Creates the object
+     *
+     * Pass the privileges in clark-notation 
+     * 
+     * @param array $privileges 
+     */
+    public function __construct(array $privileges) {
+
+        $this->privileges = $privileges;
+
+    }
+
+    /**
+     * Serializes the property in the DOM 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        foreach($this->privileges as $privName) {
+
+            $this->serializePriv($doc,$node,$privName);
+
+        }
+
+    }
+
+    /**
+     * Serializes one privilege 
+     * 
+     * @param DOMDocument $doc 
+     * @param DOMElement $node 
+     * @param string $privName 
+     * @return void
+     */
+    protected function serializePriv($doc,$node,$privName) {
+
+        $xp  = $doc->createElementNS('DAV:','d:privilege');
+        $node->appendChild($xp);
+
+        $privParts = null;
+        preg_match('/^{([^}]*)}(.*)$/',$privName,$privParts);
+
+        $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Property/Principal.php b/3rdparty/Sabre/DAVACL/Property/Principal.php
new file mode 100644
index 0000000000000000000000000000000000000000..dad9a3550fb5678f493d74dd8efcaacc6d77ca53
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/Principal.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * Principal property
+ *
+ * The principal property represents a principal from RFC3744 (ACL).
+ * The property can be used to specify a principal or pseudo principals. 
+ *
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Property_Principal extends Sabre_DAV_Property implements Sabre_DAV_Property_IHref {
+
+    /**
+     * To specify a not-logged-in user, use the UNAUTHENTICTED principal
+     */
+    const UNAUTHENTICATED = 1;
+
+    /**
+     * To specify any principal that is logged in, use AUTHENTICATED
+     */
+    const AUTHENTICATED = 2;
+
+    /**
+     * Specific princpals can be specified with the HREF
+     */
+    const HREF = 3;
+
+    /**
+     * Principal-type
+     *
+     * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants.
+     * 
+     * @var int 
+     */
+    private $type;
+
+    /**
+     * Url to principal
+     *
+     * This value is only used for the HREF principal type.
+     * 
+     * @var string 
+     */
+    private $href;
+
+    /**
+     * Creates the property.
+     *
+     * The 'type' argument must be one of the type constants defined in this class.
+     *
+     * 'href' is only required for the HREF type.
+     * 
+     * @param int $type 
+     * @param string $href 
+     * @return void
+     */
+    public function __construct($type, $href = null) {
+
+        $this->type = $type;
+
+        if ($type===self::HREF && is_null($href)) {
+            throw new Sabre_DAV_Exception('The href argument must be specified for the HREF principal type.');
+        }
+        $this->href = $href;
+
+    }
+
+    /**
+     * Returns the principal type 
+     * 
+     * @return int 
+     */
+    public function getType() {
+
+        return $this->type;
+
+    }
+
+    /**
+     * Returns the principal uri. 
+     * 
+     * @return string
+     */
+    public function getHref() {
+
+        return $this->href;
+
+    }
+
+    /**
+     * Serializes the property into a DOMElement. 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server, DOMElement $node) {
+
+        $prefix = $server->xmlNamespaces['DAV:'];
+        switch($this->type) {
+
+            case self::UNAUTHENTICATED :
+                $node->appendChild(
+                    $node->ownerDocument->createElement($prefix . ':unauthenticated')
+                );
+                break;
+            case self::AUTHENTICATED :
+                $node->appendChild(
+                    $node->ownerDocument->createElement($prefix . ':authenticated')
+                );
+                break;
+            case self::HREF :
+                $href = $node->ownerDocument->createElement($prefix . ':href');
+                $href->nodeValue = $server->getBaseUri() . $this->href;
+                $node->appendChild($href);
+                break;
+
+        }
+
+    }
+
+    /**
+     * Deserializes a DOM element into a property object. 
+     * 
+     * @param DOMElement $dom 
+     * @return Sabre_DAV_Property_Principal 
+     */
+    static public function unserialize(DOMElement $dom) {
+
+        $parent = $dom->firstChild;
+        while(!Sabre_DAV_XMLUtil::toClarkNotation($parent)) {
+            $parent = $parent->nextSibling;
+        }
+
+        switch(Sabre_DAV_XMLUtil::toClarkNotation($parent)) {
+
+            case '{DAV:}unauthenticated' :
+                return new self(self::UNAUTHENTICATED);
+            case '{DAV:}authenticated' :
+                return new self(self::AUTHENTICATED);
+            case '{DAV:}href':
+                return new self(self::HREF, $parent->textContent);
+            default :
+                throw new Sabre_DAV_Exception_BadRequest('Unexpected element (' . Sabre_DAV_XMLUtil::toClarkNotation($parent) . '). Could not deserialize');
+
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
new file mode 100644
index 0000000000000000000000000000000000000000..93c3895035d0a44c115ce095a0b3e4bda0406380
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Property/SupportedPrivilegeSet.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * SupportedPrivilegeSet property
+ *
+ * This property encodes the {DAV:}supported-privilege-set property, as defined 
+ * in rfc3744. Please consult the rfc for details about it's structure.
+ *
+ * This class expects a structure like the one given from 
+ * Sabre_DAVACL_Plugin::getSupportedPrivilegeSet as the argument in its 
+ * constructor.
+ *   
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Property_SupportedPrivilegeSet extends Sabre_DAV_Property {
+
+    /**
+     * privileges 
+     * 
+     * @var array 
+     */
+    private $privileges;
+
+    /**
+     * Constructor 
+     * 
+     * @param array $privileges 
+     */
+    public function __construct(array $privileges) {
+
+        $this->privileges = $privileges;
+
+    }
+
+    /**
+     * Serializes the property into a domdocument. 
+     * 
+     * @param Sabre_DAV_Server $server 
+     * @param DOMElement $node 
+     * @return void
+     */
+    public function serialize(Sabre_DAV_Server $server,DOMElement $node) {
+
+        $doc = $node->ownerDocument;
+        $this->serializePriv($doc, $node, $this->privileges);
+
+    }
+
+    /**
+     * Serializes a property
+     *
+     * This is a recursive function. 
+     * 
+     * @param DOMDocument $doc 
+     * @param DOMElement $node 
+     * @param array $privilege 
+     * @return void
+     */
+    private function serializePriv($doc,$node,$privilege) {
+
+        $xsp = $doc->createElementNS('DAV:','d:supported-privilege');
+        $node->appendChild($xsp);
+
+        $xp  = $doc->createElementNS('DAV:','d:privilege');
+        $xsp->appendChild($xp);
+
+        $privParts = null;
+        preg_match('/^{([^}]*)}(.*)$/',$privilege['privilege'],$privParts);
+
+        $xp->appendChild($doc->createElementNS($privParts[1],'d:'.$privParts[2]));
+
+        if (isset($privilege['abstract']) && $privilege['abstract']) {
+            $xsp->appendChild($doc->createElementNS('DAV:','d:abstract'));
+        }
+
+        if (isset($privilege['description'])) {
+            $xsp->appendChild($doc->createElementNS('DAV:','d:description',$privilege['description']));
+        }
+
+        if (isset($privilege['aggregates'])) { 
+            foreach($privilege['aggregates'] as $subPrivilege) {
+                $this->serializePriv($doc,$xsp,$subPrivilege);
+            }
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/DAVACL/Version.php b/3rdparty/Sabre/DAVACL/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..a705507486c24c74600aa2298d02a211f1dd581e
--- /dev/null
+++ b/3rdparty/Sabre/DAVACL/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the SabreDAV version constants.
+ * 
+ * @package Sabre
+ * @subpackage DAVACL
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_DAVACL_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.4.4';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/HTTP/AWSAuth.php b/3rdparty/Sabre/HTTP/AWSAuth.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e4668cd94d8ff35589282aa4af9d9cba2d828bd
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/AWSAuth.php
@@ -0,0 +1,226 @@
+<?php
+
+/**
+ * HTTP AWS Authentication handler
+ *
+ * Use this class to leverage amazon's AWS authentication header
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_AWSAuth extends Sabre_HTTP_AbstractAuth {
+
+    /**
+     * The signature supplied by the HTTP client 
+     * 
+     * @var string 
+     */
+    private $signature = null;
+
+    /**
+     * The accesskey supplied by the HTTP client 
+     * 
+     * @var string 
+     */
+    private $accessKey = null;
+
+    /**
+     * An error code, if any
+     *
+     * This value will be filled with one of the ERR_* contants
+     * 
+     * @var int
+     */
+    public $errorCode = 0;
+
+    const ERR_NOAWSHEADER = 1;
+    const ERR_MD5CHECKSUMWRONG = 2;
+    const ERR_INVALIDDATEFORMAT = 3;
+    const ERR_REQUESTTIMESKEWED = 4;
+    const ERR_INVALIDSIGNATURE = 5;
+
+    /**
+     * Gathers all information from the headers
+     *
+     * This method needs to be called prior to anything else.
+     * 
+     * @return bool 
+     */
+    public function init() {
+
+        $authHeader = $this->httpRequest->getHeader('Authorization');
+        $authHeader = explode(' ',$authHeader);
+
+        if ($authHeader[0]!='AWS' || !isset($authHeader[1])) {
+            $this->errorCode = self::ERR_NOAWSHEADER;
+             return false;
+        }
+
+        list($this->accessKey,$this->signature) = explode(':',$authHeader[1]);
+
+        return true;
+
+    }
+
+    /**
+     * Returns the username for the request 
+     * 
+     * @return string 
+     */
+    public function getAccessKey() {
+
+        return $this->accessKey;
+
+    }
+
+    /**
+     * Validates the signature based on the secretKey
+     * 
+     * @return bool 
+     */
+    public function validate($secretKey) {
+
+        $contentMD5 = $this->httpRequest->getHeader('Content-MD5');
+
+        if ($contentMD5) {
+            // We need to validate the integrity of the request
+            $body = $this->httpRequest->getBody(true);
+            $this->httpRequest->setBody($body,true);
+           
+            if ($contentMD5!=base64_encode(md5($body,true))) {
+                // content-md5 header did not match md5 signature of body
+                $this->errorCode = self::ERR_MD5CHECKSUMWRONG;
+                return false;
+            }
+
+        }
+
+        if (!$requestDate = $this->httpRequest->getHeader('x-amz-date')) 
+            $requestDate = $this->httpRequest->getHeader('Date');
+
+        if (!$this->validateRFC2616Date($requestDate)) 
+            return false;
+
+        $amzHeaders = $this->getAmzHeaders();
+
+        $signature = base64_encode(
+            $this->hmacsha1($secretKey,
+                $this->httpRequest->getMethod() . "\n" .
+                $contentMD5 . "\n" . 
+                $this->httpRequest->getHeader('Content-type') . "\n" .
+                $requestDate . "\n" .
+                $amzHeaders . 
+                $this->httpRequest->getURI()
+            )
+        );
+
+        if ($this->signature != $signature) {
+
+            $this->errorCode = self::ERR_INVALIDSIGNATURE;
+            return false;
+
+        }
+
+        return true;
+
+    }
+
+
+    /**
+     * Returns an HTTP 401 header, forcing login
+     *
+     * This should be called when username and password are incorrect, or not supplied at all
+     *
+     * @return void
+     */
+    public function requireLogin() {
+
+        $this->httpResponse->setHeader('WWW-Authenticate','AWS');
+        $this->httpResponse->sendStatus(401);
+
+    }
+
+    /**
+     * Makes sure the supplied value is a valid RFC2616 date.
+     *
+     * If we would just use strtotime to get a valid timestamp, we have no way of checking if a 
+     * user just supplied the word 'now' for the date header.
+     *
+     * This function also makes sure the Date header is within 15 minutes of the operating 
+     * system date, to prevent replay attacks.
+     * 
+     * @param string $dateHeader 
+     * @return bool 
+     */
+    protected function validateRFC2616Date($dateHeader) {
+
+        $date = Sabre_HTTP_Util::parseHTTPDate($dateHeader);
+
+        // Unknown format
+        if (!$date) {
+            $this->errorCode = self::ERR_INVALIDDATEFORMAT;
+            return false;
+        }
+
+        $min = new DateTime('-15 minutes');
+        $max = new DateTime('+15 minutes');
+
+        // We allow 15 minutes around the current date/time
+        if ($date > $max || $date < $min) {
+            $this->errorCode = self::ERR_REQUESTTIMESKEWED;
+            return false;
+        }
+
+        return $date;
+
+    }
+    
+    /**
+     * Returns a list of AMZ headers 
+     * 
+     * @return void
+     */
+    protected function getAmzHeaders() {
+
+        $amzHeaders = array();
+        $headers = $this->httpRequest->getHeaders();
+        foreach($headers as $headerName => $headerValue) {
+            if (strpos(strtolower($headerName),'x-amz-')===0) {
+                $amzHeaders[strtolower($headerName)] = str_replace(array("\r\n"),array(' '),$headerValue) . "\n";
+            }
+        }
+        ksort($amzHeaders);
+       
+        $headerStr = '';
+        foreach($amzHeaders as $h=>$v) {
+            $headerStr.=$h.':'.$v;
+        }
+
+        return $headerStr;
+
+    }
+
+    /**
+     * Generates an HMAC-SHA1 signature 
+     * 
+     * @param string $key 
+     * @param string $message 
+     * @return string 
+     */
+    private function hmacsha1($key, $message) {
+
+        $blocksize=64;
+        if (strlen($key)>$blocksize)
+            $key=pack('H*', sha1($key));
+        $key=str_pad($key,$blocksize,chr(0x00));
+        $ipad=str_repeat(chr(0x36),$blocksize);
+        $opad=str_repeat(chr(0x5c),$blocksize);
+        $hmac = pack('H*',sha1(($key^$opad).pack('H*',sha1(($key^$ipad).$message))));
+        return $hmac;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/HTTP/AbstractAuth.php b/3rdparty/Sabre/HTTP/AbstractAuth.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb528f6fdee56dd507f63eb511952ff6298d49d2
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/AbstractAuth.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * HTTP Authentication baseclass
+ *
+ * This class has the common functionality for BasicAuth and DigestAuth
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_HTTP_AbstractAuth {
+
+    /**
+     * The realm will be displayed in the dialog boxes
+     *
+     * This identifier can be changed through setRealm()
+     * 
+     * @var string
+     */
+    protected $realm = 'SabreDAV';
+
+    /**
+     * HTTP response helper 
+     * 
+     * @var Sabre_HTTP_Response 
+     */
+    protected $httpResponse;
+
+
+    /**
+     * HTTP request helper 
+     * 
+     * @var Sabre_HTTP_Request 
+     */
+    protected $httpRequest;
+
+    /**
+     * __construct 
+     * 
+     */
+    public function __construct() {
+
+        $this->httpResponse = new Sabre_HTTP_Response();
+        $this->httpRequest = new Sabre_HTTP_Request();
+
+    }
+
+    /**
+     * Sets an alternative HTTP response object 
+     * 
+     * @param Sabre_HTTP_Response $response 
+     * @return void
+     */
+    public function setHTTPResponse(Sabre_HTTP_Response $response) {
+
+        $this->httpResponse = $response;
+
+    }
+
+    /**
+     * Sets an alternative HTTP request object 
+     * 
+     * @param Sabre_HTTP_Request $request 
+     * @return void
+     */
+    public function setHTTPRequest(Sabre_HTTP_Request $request) {
+
+        $this->httpRequest = $request;
+
+    }
+
+
+    /**
+     * Sets the realm
+     *
+     * The realm is often displayed in authentication dialog boxes
+     * Commonly an application name displayed here
+     * 
+     * @param string $realm 
+     * @return void
+     */
+    public function setRealm($realm) {
+
+        $this->realm = $realm;
+
+    }
+
+    /**
+     * Returns the realm
+     *
+     * @return string 
+     */
+    public function getRealm() {
+
+        return $this->realm;
+
+    }
+
+    /**
+     * Returns an HTTP 401 header, forcing login
+     *
+     * This should be called when username and password are incorrect, or not supplied at all
+     *
+     * @return void
+     */
+    abstract public function requireLogin(); 
+
+}
diff --git a/3rdparty/Sabre/HTTP/BasicAuth.php b/3rdparty/Sabre/HTTP/BasicAuth.php
new file mode 100644
index 0000000000000000000000000000000000000000..35c22d22dc3837dcd51fe7d87f654b9ff3c1ce18
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/BasicAuth.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * HTTP Basic Authentication handler
+ *
+ * Use this class for easy http authentication setup
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth {
+
+    /**
+     * Returns the supplied username and password.
+     *
+     * The returned array has two values:
+     *   * 0 - username
+     *   * 1 - password
+     *
+     * If nothing was supplied, 'false' will be returned
+     *
+     * @return mixed 
+     */
+    public function getUserPass() {
+
+        // Apache and mod_php
+        if (($user = $this->httpRequest->getRawServerValue('PHP_AUTH_USER')) && ($pass = $this->httpRequest->getRawServerValue('PHP_AUTH_PW'))) {
+
+            return array($user,$pass);
+
+        }
+
+        // Most other webservers 
+        $auth = $this->httpRequest->getHeader('Authorization');
+
+        if (!$auth) return false;
+
+        if (strpos(strtolower($auth),'basic')!==0) return false; 
+
+        return explode(':', base64_decode(substr($auth, 6)));
+
+    }
+
+    /**
+     * Returns an HTTP 401 header, forcing login
+     *
+     * This should be called when username and password are incorrect, or not supplied at all
+     *
+     * @return void
+     */
+    public function requireLogin() {
+
+        $this->httpResponse->setHeader('WWW-Authenticate','Basic realm="' . $this->realm . '"');
+        $this->httpResponse->sendStatus(401);
+
+    }
+
+}
diff --git a/3rdparty/Sabre/HTTP/DigestAuth.php b/3rdparty/Sabre/HTTP/DigestAuth.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e7559295710025ba5bd86f777f75b68a1fac17e
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/DigestAuth.php
@@ -0,0 +1,234 @@
+<?php
+
+/**
+ * HTTP Digest Authentication handler
+ *
+ * Use this class for easy http digest authentication.
+ * Instructions:
+ *
+ *  1. Create the object
+ *  2. Call the setRealm() method with the realm you plan to use
+ *  3. Call the init method function.
+ *  4. Call the getUserName() function. This function may return false if no
+ *     authentication information was supplied. Based on the username you 
+ *     should check your internal database for either the associated password,
+ *     or the so-called A1 hash of the digest.
+ *  5. Call either validatePassword() or validateA1(). This will return true
+ *     or false. 
+ *  6. To make sure an authentication prompt is displayed, call the
+ *     requireLogin() method.
+ * 
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_DigestAuth extends Sabre_HTTP_AbstractAuth {
+
+    /**
+     * These constants are used in setQOP();
+     */
+    const QOP_AUTH = 1;
+    const QOP_AUTHINT = 2;
+
+    protected $nonce;
+    protected $opaque;
+    protected $digestParts;
+    protected $A1;
+    protected $qop = self::QOP_AUTH;
+
+    /**
+     * Initializes the object 
+     */
+    public function __construct() {
+
+        $this->nonce = uniqid();
+        $this->opaque = md5($this->realm);
+        parent::__construct();
+
+    }
+
+    /**
+     * Gathers all information from the headers
+     *
+     * This method needs to be called prior to anything else.
+     * 
+     * @return void
+     */
+    public function init() {
+
+        $digest = $this->getDigest();
+        $this->digestParts = $this->parseDigest($digest);
+
+    }
+
+    /**
+     * Sets the quality of protection value.
+     *
+     * Possible values are:
+     *   Sabre_HTTP_DigestAuth::QOP_AUTH
+     *   Sabre_HTTP_DigestAuth::QOP_AUTHINT
+     *
+     * Multiple values can be specified using logical OR.
+     *
+     * QOP_AUTHINT ensures integrity of the request body, but this is not 
+     * supported by most HTTP clients. QOP_AUTHINT also requires the entire 
+     * request body to be md5'ed, which can put strains on CPU and memory.
+     *
+     * @param int $qop 
+     * @return void
+     */
+    public function setQOP($qop) {
+
+        $this->qop = $qop;
+
+    }
+
+    /**
+     * Validates the user.
+     *
+     * The A1 parameter should be md5($username . ':' . $realm . ':' . $password);
+     *
+     * @param string $A1 
+     * @return bool 
+     */
+    public function validateA1($A1) {
+
+        $this->A1 = $A1;
+        return $this->validate();
+
+    }
+
+    /**
+     * Validates authentication through a password. The actual password must be provided here.
+     * It is strongly recommended not store the password in plain-text and use validateA1 instead.
+     * 
+     * @param string $password 
+     * @return bool 
+     */
+    public function validatePassword($password) {
+
+        $this->A1 = md5($this->digestParts['username'] . ':' . $this->realm . ':' . $password);
+        return $this->validate();
+
+    }
+
+    /**
+     * Returns the username for the request 
+     * 
+     * @return string 
+     */
+    public function getUsername() {
+
+        return $this->digestParts['username'];
+
+    }
+
+    /**
+     * Validates the digest challenge 
+     * 
+     * @return bool 
+     */
+    protected function validate() {
+
+        $A2 = $this->httpRequest->getMethod() . ':' . $this->digestParts['uri'];
+    
+        if ($this->digestParts['qop']=='auth-int') {
+            // Making sure we support this qop value
+            if (!($this->qop & self::QOP_AUTHINT)) return false;
+            // We need to add an md5 of the entire request body to the A2 part of the hash
+            $body = $this->httpRequest->getBody(true);
+            $this->httpRequest->setBody($body,true);
+            $A2 .= ':' . md5($body);
+        } else {
+
+            // We need to make sure we support this qop value 
+            if (!($this->qop & self::QOP_AUTH)) return false; 
+        }
+
+        $A2 = md5($A2);
+
+        $validResponse = md5("{$this->A1}:{$this->digestParts['nonce']}:{$this->digestParts['nc']}:{$this->digestParts['cnonce']}:{$this->digestParts['qop']}:{$A2}"); 
+
+        return $this->digestParts['response']==$validResponse;
+        
+
+    }
+
+    /**
+     * Returns an HTTP 401 header, forcing login
+     *
+     * This should be called when username and password are incorrect, or not supplied at all
+     *
+     * @return void
+     */
+    public function requireLogin() {
+
+        $qop = '';
+        switch($this->qop) {
+            case self::QOP_AUTH    : $qop = 'auth'; break;
+            case self::QOP_AUTHINT : $qop = 'auth-int'; break;
+            case self::QOP_AUTH | self::QOP_AUTHINT : $qop = 'auth,auth-int'; break;
+        }
+
+        $this->httpResponse->setHeader('WWW-Authenticate','Digest realm="' . $this->realm . '",qop="'.$qop.'",nonce="' . $this->nonce . '",opaque="' . $this->opaque . '"');
+        $this->httpResponse->sendStatus(401);
+
+    }
+
+
+    /**
+     * This method returns the full digest string.
+     *
+     * It should be compatibile with mod_php format and other webservers.
+     *
+     * If the header could not be found, null will be returned
+     *
+     * @return mixed 
+     */
+    public function getDigest() {
+
+        // mod_php
+        $digest = $this->httpRequest->getRawServerValue('PHP_AUTH_DIGEST');
+        if ($digest) return $digest;
+
+        // most other servers
+        $digest = $this->httpRequest->getHeader('Authorization');
+
+        if ($digest && strpos(strtolower($digest),'digest')===0) {
+            return substr($digest,7);
+        } else {
+            return null;
+        }
+
+    }
+
+
+    /**
+     * Parses the different pieces of the digest string into an array.
+     * 
+     * This method returns false if an incomplete digest was supplied
+     *
+     * @param string $digest 
+     * @return mixed 
+     */
+    protected function parseDigest($digest) {
+
+        // protect against missing data
+        $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
+        $data = array();
+
+        preg_match_all('@(\w+)=(?:(?:")([^"]+)"|([^\s,$]+))@', $digest, $matches, PREG_SET_ORDER);
+
+        foreach ($matches as $m) {
+            $data[$m[1]] = $m[2] ? $m[2] : $m[3];
+            unset($needed_parts[$m[1]]);
+        }
+
+        return $needed_parts ? false : $data; 
+
+    }
+
+}
diff --git a/3rdparty/Sabre/HTTP/Request.php b/3rdparty/Sabre/HTTP/Request.php
new file mode 100644
index 0000000000000000000000000000000000000000..95a64171aab612faed7a2459f7bbedb4d983d3a7
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Request.php
@@ -0,0 +1,243 @@
+<?php
+
+/**
+ * HTTP Request information
+ *
+ * This object can be used to easily access information about an HTTP request.
+ * It can additionally be used to create 'mock' requests.
+ *
+ * This class mostly operates indepentend, but because of the nature of a single 
+ * request per run it can operate as a singleton. For more information check out 
+ * the behaviour around 'defaultInputStream'.
+ *
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_Request {
+
+    /**
+     * PHP's $_SERVER data
+     * 
+     * @var string 
+     */
+    protected $_SERVER;
+
+    /**
+     * The request body, if any.
+     *
+     * This is stored in the form of a stream resource.
+     *
+     * @var resource 
+     */
+    protected $body = null;
+
+    /**
+     * This will be set as the 'default' inputStream for a specific HTTP request
+     * We sometimes need to retain, or rebuild this if we need multiple runs 
+     * of parsing the original HTTP request.
+     * 
+     * @var resource 
+     */
+    static $defaultInputStream=null;
+
+    /**
+     * Sets up the object
+     *
+     * The serverData array can be used to override usage of PHP's 
+     * global _SERVER variable. 
+     * 
+     * @param array $serverData 
+     */
+    public function __construct($serverData = null) {
+
+       if ($serverData) $this->_SERVER = $serverData;
+       else $this->_SERVER =& $_SERVER;
+
+    }
+
+    /**
+     * Returns the value for a specific http header.
+     *
+     * This method returns null if the header did not exist.
+     * 
+     * @param string $name 
+     * @return string 
+     */
+    public function getHeader($name) {
+
+        $name = strtoupper(str_replace(array('-'),array('_'),$name));
+        if (isset($this->_SERVER['HTTP_' . $name])) {
+            return $this->_SERVER['HTTP_' . $name];
+        } 
+
+        // There's a few headers that seem to end up in the top-level 
+        // server array.
+        switch($name) {
+            case 'CONTENT_TYPE' :
+            case 'CONTENT_LENGTH' :
+                if (isset($this->_SERVER[$name])) {
+                    return $this->_SERVER[$name];
+                }
+                break;
+
+        }
+        return;
+
+    }
+
+    /**
+     * Returns all (known) HTTP headers.
+     *
+     * All headers are converted to lower-case, and additionally all underscores
+     * are automatically converted to dashes 
+     * 
+     * @return array 
+     */
+    public function getHeaders() {
+
+        $hdrs = array();
+        foreach($this->_SERVER as $key=>$value) {
+
+            switch($key) {
+                case 'CONTENT_LENGTH' :
+                case 'CONTENT_TYPE' :
+                    $hdrs[strtolower(str_replace('_','-',$key))] = $value;
+                    break;
+                default :
+                    if (strpos($key,'HTTP_')===0) {
+                        $hdrs[substr(strtolower(str_replace('_','-',$key)),5)] = $value;
+                    }
+                    break;
+            }
+
+        }
+
+        return $hdrs;
+
+    }
+
+    /**
+     * Returns the HTTP request method
+     *
+     * This is for example POST or GET 
+     *
+     * @return string 
+     */
+    public function getMethod() {
+
+        return $this->_SERVER['REQUEST_METHOD'];
+
+    }
+
+    /**
+     * Returns the requested uri
+     *
+     * @return string 
+     */
+    public function getUri() {
+       
+        return $this->_SERVER['REQUEST_URI'];
+
+    }
+
+    /**
+     * Will return protocol + the hostname + the uri 
+     * 
+     * @return void
+     */
+    public function getAbsoluteUri() {
+
+        // Checking if the request was made through HTTPS. The last in line is for IIS
+        $protocol = isset($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']) && ($this->_SERVER['HTTPS']!='off');
+        return ($protocol?'https':'http') . '://'  . $this->getHeader('Host') . $this->getUri();
+
+    }
+
+    /**
+     * Returns everything after the ? from the current url 
+     * 
+     * @return string 
+     */
+    public function getQueryString() {
+
+        return isset($this->_SERVER['QUERY_STRING'])?$this->_SERVER['QUERY_STRING']:'';
+
+    }
+
+    /**
+     * Returns the HTTP request body body 
+     *
+     * This method returns a readable stream resource.
+     * If the asString parameter is set to true, a string is sent instead. 
+     *
+     * @param bool asString
+     * @return resource 
+     */
+    public function getBody($asString = false) {
+
+        if (is_null($this->body)) {
+            if (!is_null(self::$defaultInputStream)) {
+                $this->body = self::$defaultInputStream;
+            } else {
+                $this->body = fopen('php://input','r');
+                self::$defaultInputStream = $this->body;
+            }
+        }
+        if ($asString) {
+            $body = stream_get_contents($this->body);
+            return $body;
+        } else {
+            return $this->body;
+        }
+
+    }
+
+    /**
+     * Sets the contents of the HTTP request body 
+     * 
+     * This method can either accept a string, or a readable stream resource.
+     *
+     * If the setAsDefaultInputStream is set to true, it means for this run of the 
+     * script the supplied body will be used instead of php://input.
+     *
+     * @param mixed $body 
+     * @param bool $setAsDefaultInputStream
+     * @return void
+     */
+    public function setBody($body,$setAsDefaultInputStream = false) {
+
+        if(is_resource($body)) {
+            $this->body = $body;
+        } else {
+
+            $stream = fopen('php://temp','r+');
+            fputs($stream,$body);
+            rewind($stream);
+            // String is assumed
+            $this->body = $stream;
+        }
+        if ($setAsDefaultInputStream) {
+            self::$defaultInputStream = $this->body;
+        }
+
+    }
+
+    /**
+     * Returns a specific item from the _SERVER array. 
+     *
+     * Do not rely on this feature, it is for internal use only.
+     *
+     * @param string $field 
+     * @return string 
+     */
+    public function getRawServerValue($field) {
+
+        return isset($this->_SERVER[$field])?$this->_SERVER[$field]:null;
+
+    }
+
+}
+
diff --git a/3rdparty/Sabre/HTTP/Response.php b/3rdparty/Sabre/HTTP/Response.php
new file mode 100644
index 0000000000000000000000000000000000000000..c8c77251a178dee2bfd5a6e0a1e3eeafcb4be285
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Response.php
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * Sabre_HTTP_Response 
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_Response {
+
+    /**
+     * Returns a full HTTP status message for an HTTP status code 
+     * 
+     * @param int $code 
+     * @return string
+     */
+    public function getStatusMessage($code) {
+
+        $msg = array(
+            100 => 'Continue',
+            101 => 'Switching Protocols',
+            102 => 'Processing',
+            200 => 'Ok',
+            201 => 'Created',
+            202 => 'Accepted',
+            203 => 'Non-Authorative Information',
+            204 => 'No Content',
+            205 => 'Reset Content',
+            206 => 'Partial Content',
+            207 => 'Multi-Status', // RFC 4918
+            208 => 'Already Reported', // RFC 5842
+            226 => 'IM Used', // RFC 3229
+            300 => 'Multiple Choices',
+            301 => 'Moved Permanently',
+            302 => 'Found',
+            303 => 'See Other',
+            304 => 'Not Modified',
+            305 => 'Use Proxy',
+            306 => 'Reserved',
+            307 => 'Temporary Redirect',
+            400 => 'Bad request',
+            401 => 'Unauthorized',
+            402 => 'Payment Required',
+            403 => 'Forbidden',
+            404 => 'Not Found',
+            405 => 'Method Not Allowed',
+            406 => 'Not Acceptable',
+            407 => 'Proxy Authentication Required',
+            408 => 'Request Timeout',
+            409 => 'Conflict',
+            410 => 'Gone',
+            411 => 'Length Required',
+            412 => 'Precondition failed',
+            413 => 'Request Entity Too Large',
+            414 => 'Request-URI Too Long',
+            415 => 'Unsupported Media Type',
+            416 => 'Requested Range Not Satisfiable',
+            417 => 'Expectation Failed',
+            418 => 'I\'m a teapot', // RFC 2324
+            422 => 'Unprocessable Entity', // RFC 4918
+            423 => 'Locked', // RFC 4918
+            424 => 'Failed Dependency', // RFC 4918
+            426 => 'Upgrade required',
+            500 => 'Internal Server Error',
+            501 => 'Not Implemented',
+            502 => 'Bad Gateway',
+            503 => 'Service Unavailable',
+            504 => 'Gateway Timeout',
+            505 => 'HTTP Version not supported',
+            506 => 'Variant Also Negotiates',
+            507 => 'Unsufficient Storage', // RFC 4918
+            508 => 'Loop Detected', // RFC 5842
+            509 => 'Bandwidth Limit Exceeded', // non-standard
+            510 => 'Not extended',
+       ); 
+
+       return 'HTTP/1.1 ' . $code . ' ' . $msg[$code];
+
+    }
+
+    /**
+     * Sends an HTTP status header to the client 
+     * 
+     * @param int $code HTTP status code 
+     * @return void
+     */
+    public function sendStatus($code) {
+
+        if (!headers_sent()) 
+            return header($this->getStatusMessage($code));
+        else return false;
+
+    }
+
+    /**
+     * Sets an HTTP header for the response
+     * 
+     * @param string $name 
+     * @param string $value 
+     * @return void
+     */
+    public function setHeader($name, $value, $replace = true) {
+
+        $value = str_replace(array("\r","\n"),array('\r','\n'),$value);
+        if (!headers_sent()) 
+            return header($name . ': ' . $value, $replace);
+        else return false;
+
+    }
+
+    /**
+     * Sets a bunch of HTTP Headers
+     *
+     * headersnames are specified as keys, value in the array value
+     * 
+     * @param array $headers 
+     * @return void
+     */
+    public function setHeaders(array $headers) {
+
+        foreach($headers as $key=>$value)
+            $this->setHeader($key, $value);
+
+    }
+
+    /**
+     * Sends the entire response body
+     *
+     * This method can accept either an open filestream, or a string.
+     * 
+     * @param mixed $body 
+     * @return void
+     */
+    public function sendBody($body) {
+
+        if (is_resource($body)) {
+        
+            fpassthru($body);
+
+        } else {
+
+            // We assume a string
+            echo $body;
+
+        }
+
+    }
+
+}
diff --git a/3rdparty/Sabre/HTTP/Util.php b/3rdparty/Sabre/HTTP/Util.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a6bd7df487bd297982d0a8a00d4e0692fd07aba
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Util.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * HTTP utility methods 
+ * 
+ * @package Sabre
+ * @subpackage HTTP
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @author Paul Voegler
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_Util {
+
+    /**
+     * Parses a RFC2616-compatible date string
+     *
+     * This method returns false if the date is invalid
+     * 
+     * @param string $dateHeader 
+     * @return bool|DateTime 
+     */
+    static function parseHTTPDate($dateHeader) {
+
+        //RFC 2616 section 3.3.1 Full Date
+        //Only the format is checked, valid ranges are checked by strtotime below
+        $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
+        $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
+        $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
+        $time = '[0-2]\d(\:[0-5]\d){2}';
+        $date3 = $month . ' ([1-3]\d| \d)';
+        $date2 = '[0-3]\d\-' . $month . '\-\d\d';
+        //4-digit year cannot begin with 0 - unix timestamp begins in 1970
+        $date1 = '[0-3]\d ' . $month . ' [1-9]\d{3}';
+
+        //ANSI C's asctime() format
+        //4-digit year cannot begin with 0 - unix timestamp begins in 1970
+        $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
+        //RFC 850, obsoleted by RFC 1036
+        $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
+        //RFC 822, updated by RFC 1123
+        $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
+        //allowed date formats by RFC 2616
+        $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
+        
+        //allow for space around the string and strip it
+        $dateHeader = trim($dateHeader, ' ');
+        if (!preg_match('/^' . $HTTP_date . '$/', $dateHeader))
+            return false;
+
+        //append implicit GMT timezone to ANSI C time format
+        if (strpos($dateHeader, ' GMT') === false)
+            $dateHeader .= ' GMT';
+
+
+        $realDate = strtotime($dateHeader);
+        //strtotime can return -1 or false in case of error
+        if ($realDate !== false && $realDate >= 0)
+            return new DateTime('@' . $realDate, new DateTimeZone('UTC'));
+
+        return false;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/HTTP/Version.php b/3rdparty/Sabre/HTTP/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8d1bb754293225ac8e95e6da8b1faef429a7e25
--- /dev/null
+++ b/3rdparty/Sabre/HTTP/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the Sabre_HTTP version constants.
+ * 
+ * @package Sabre
+ * @subpackage HTTP 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_HTTP_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.4.1';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/VObject/Component.php b/3rdparty/Sabre/VObject/Component.php
new file mode 100644
index 0000000000000000000000000000000000000000..3d5a3d75635c80beeae49e9fde10d9925b75229e
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Component.php
@@ -0,0 +1,247 @@
+<?php
+
+/**
+ * VObject Component
+ *
+ * This class represents a VCALENDAR/VCARD component. A component is for example
+ * VEVENT, VTODO and also VCALENDAR. It starts with BEGIN:COMPONENTNAME and 
+ * ends with END:COMPONENTNAME
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Component extends Sabre_VObject_Element {
+
+    /**
+     * Name, for example VEVENT 
+     * 
+     * @var string 
+     */
+    public $name;
+
+    /**
+     * Children properties and components 
+     * 
+     * @var array
+     */
+    public $children = array();
+
+
+    /**
+     * Creates a new component.
+     *
+     * By default this object will iterate over its own children, but this can 
+     * be overridden with the iterator argument
+     * 
+     * @param string $name 
+     * @param Sabre_VObject_ElementList $iterator
+     */
+    public function __construct($name, Sabre_VObject_ElementList $iterator = null) {
+
+        $this->name = strtoupper($name);
+        if (!is_null($iterator)) $this->iterator = $iterator;
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    public function serialize() {
+
+        $str = "BEGIN:" . $this->name . "\r\n";
+        foreach($this->children as $child) $str.=$child->serialize();
+        $str.= "END:" . $this->name . "\r\n";
+        
+        return $str;
+
+    }
+
+
+    /**
+     * Adds a new componenten or element
+     *
+     * You can call this method with the following syntaxes:
+     *
+     * add(Sabre_VObject_Element $element)
+     * add(string $name, $value)
+     *
+     * The first version adds an Element
+     * The second adds a property as a string. 
+     * 
+     * @param mixed $item 
+     * @param mixed $itemValue 
+     * @return void
+     */
+    public function add($item, $itemValue = null) {
+
+        if ($item instanceof Sabre_VObject_Element) {
+            if (!is_null($itemValue)) {
+                throw new InvalidArgumentException('The second argument must not be specified, when passing a VObject');
+            }
+            $this->children[] = $item;
+        } elseif(is_string($item)) {
+
+            if (!is_scalar($itemValue)) {
+                throw new InvalidArgumentException('The second argument must be scalar');
+            }
+            $this->children[] = new Sabre_VObject_Property($item,$itemValue);
+
+        } else {
+            
+            throw new InvalidArgumentException('The first argument must either be a Sabre_VObject_Element or a string');
+
+        }
+
+    }
+
+    /**
+     * Returns an iterable list of children 
+     * 
+     * @return Sabre_VObject_ElementList 
+     */
+    public function children() {
+
+        return new Sabre_VObject_ElementList($this->children);
+
+    }
+
+    /**
+     * Returns an array with elements that match the specified name.
+     *
+     * This function is also aware of MIME-Directory groups (as they appear in 
+     * vcards). This means that if a property is grouped as "HOME.EMAIL", it 
+     * will also be returned when searching for just "EMAIL". If you want to 
+     * search for a property in a specific group, you can select on the entire 
+     * string ("HOME.EMAIL"). If you want to search on a specific property that 
+     * has not been assigned a group, specify ".EMAIL".
+     *
+     * Keys are retained from the 'children' array, which may be confusing in 
+     * certain cases. 
+     *
+     * @param string $name 
+     * @return array 
+     */
+    public function select($name) {
+
+        $group = null;
+        $name = strtoupper($name);
+        if (strpos($name,'.')!==false) {
+            list($group,$name) = explode('.', $name, 2);
+        }
+
+        $result = array();
+        foreach($this->children as $key=>$child) {
+
+            if (
+                strtoupper($child->name) === $name &&
+                (is_null($group) || ( $child instanceof Sabre_VObject_Property && strtoupper($child->group) === $group))
+            ) {
+                
+                $result[$key] = $child;
+
+            }
+        }
+
+        reset($result);
+        return $result;
+
+    }
+
+    /* Magic property accessors {{{ */
+
+    /**
+     * Using 'get' you will either get a propery or component, 
+     *
+     * If there were no child-elements found with the specified name,
+     * null is returned.
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function __get($name) {
+
+        $matches = $this->select($name);
+        if (count($matches)===0) {
+            return null;
+        } else {
+            $firstMatch = current($matches);
+            $firstMatch->setIterator(new Sabre_VObject_ElementList(array_values($matches)));
+            return $firstMatch;
+        }
+
+    }
+
+    /**
+     * This method checks if a sub-element with the specified name exists. 
+     * 
+     * @param string $name 
+     * @return bool 
+     */
+    public function __isset($name) {
+
+        $matches = $this->select($name);
+        return count($matches)>0;
+
+    }
+
+    /**
+     * Using the setter method you can add properties or subcomponents
+     *
+     * You can either pass a Sabre_VObject_Component, Sabre_VObject_Property
+     * object, or a string to automatically create a Property.
+     *
+     * If the item already exists, it will be removed. If you want to add
+     * a new item with the same name, always use the add() method.
+     * 
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     */
+    public function __set($name, $value) {
+
+        $matches = $this->select($name);
+        $overWrite = count($matches)?key($matches):null;
+
+        if ($value instanceof Sabre_VObject_Component || $value instanceof Sabre_VObject_Property) {
+            if (!is_null($overWrite)) {
+                $this->children[$overWrite] = $value;
+            } else {
+                $this->children[] = $value;
+            }
+        } elseif (is_scalar($value)) {
+            if (!is_null($overWrite)) {
+                $this->children[$overWrite] = new Sabre_VObject_Property($name,$value);
+            } else {
+                $this->children[] = new Sabre_VObject_Property($name,$value);
+            }
+        } else {
+            throw new InvalidArgumentException('You must pass a Sabre_VObject_Component, Sabre_VObject_Property or scalar type');
+        }
+
+    }
+
+    /**
+     * Removes all properties and components within this component. 
+     * 
+     * @param string $name 
+     * @return void
+     */
+    public function __unset($name) {
+
+        $matches = $this->select($name);
+        foreach($matches as $k=>$child) {
+
+            unset($this->children[$k]);
+
+        }
+
+    }
+
+    /* }}} */
+
+}
diff --git a/3rdparty/Sabre/VObject/Element.php b/3rdparty/Sabre/VObject/Element.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d2b0aaacd1ee5ce9bde985eb00b058f8151da55
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Element.php
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * Base class for all elements
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_VObject_Element extends Sabre_VObject_Node {
+
+
+}
diff --git a/3rdparty/Sabre/VObject/Element/DateTime.php b/3rdparty/Sabre/VObject/Element/DateTime.php
new file mode 100644
index 0000000000000000000000000000000000000000..63af858dd68ff60a21e9b3ae03ce85853be8c2a5
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Element/DateTime.php
@@ -0,0 +1,218 @@
+<?php
+
+/**
+ * DateTime property 
+ *
+ * This element is used for iCalendar properties such as the DTSTART property. 
+ * It basically provides a few helper functions that make it easier to deal 
+ * with these. It supports both DATE-TIME and DATE values.
+ *
+ * In order to use this correctly, you must call setDateTime and getDateTime to 
+ * retrieve and modify dates respectively.
+ *
+ * If you use the 'value' or properties directly, this object does not keep 
+ * reference and results might appear incorrectly.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Element_DateTime extends Sabre_VObject_Property {
+
+    /**
+     * Local 'floating' time
+     */
+    const LOCAL = 1;
+
+    /**
+     * UTC-based time
+     */
+    const UTC = 2;
+
+    /**
+     * Local time plus timezone
+     */
+    const LOCALTZ = 3;
+
+    /**
+     * Only a date, time is ignored
+     */
+    const DATE = 4;
+
+    /**
+     * DateTime representation
+     *
+     * @var DateTime
+     */
+    protected $dateTime;
+
+    /**
+     * dateType 
+     * 
+     * @var int 
+     */
+    protected $dateType;
+
+    /**
+     * Updates the Date and Time. 
+     * 
+     * @param DateTime $dt 
+     * @param int $dateType 
+     * @return void
+     */
+    public function setDateTime(DateTime $dt, $dateType = self::LOCALTZ) {
+
+        switch($dateType) {
+
+            case self::LOCAL :
+                $this->setValue($dt->format('Ymd\\THis'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATETIME'); 
+                break;
+            case self::UTC :
+                $dt->setTimeZone(new DateTimeZone('UTC'));
+                $this->setValue($dt->format('Ymd\\THis\\Z'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATETIME');
+                break;
+            case self::LOCALTZ :
+                $this->setValue($dt->format('Ymd\\THis'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATETIME');
+                $this->offsetSet('TZID', $dt->getTimeZone()->getName());
+                break; 
+            case self::DATE :
+                $this->setValue($dt->format('Ymd'));
+                $this->offsetUnset('VALUE');
+                $this->offsetUnset('TZID');
+                $this->offsetSet('VALUE','DATE');
+                break;
+            default :
+                throw new InvalidArgumentException('You must pass a valid dateType constant');
+
+        }
+        $this->dateTime = $dt;
+        $this->dateType = $dateType;
+
+    }
+
+    /**
+     * Returns the current DateTime value.
+     *
+     * If no value was set, this method returns null.
+     *
+     * @return DateTime|null 
+     */
+    public function getDateTime() {
+
+        if ($this->dateTime)
+            return $this->dateTime;
+
+        list(
+            $this->dateType,
+            $this->dateTime
+        ) = self::parseData($this->value, $this->offsetGet('TZID'));
+        return $this->dateTime;
+
+    }
+
+    /**
+     * Returns the type of Date format.
+     *
+     * This method returns one of the format constants. If no date was set, 
+     * this method will return null.
+     *
+     * @return int|null
+     */
+    public function getDateType() {
+
+        if ($this->dateType)
+            return $this->dateType;
+
+        list(
+            $this->dateType,
+            $this->dateTime,
+        ) = self::parseData($this->value, $this->offsetGet('TZID'));
+        return $this->dateType;
+
+    }
+
+    /**
+     * Parses the internal data structure to figure out what the current date 
+     * and time is.
+     *
+     * The returned array contains two elements:
+     *   1. A 'DateType' constant (as defined on this class), or null. 
+     *   2. A DateTime object (or null)
+     *
+     * @param string|null $propertyValue The string to parse (yymmdd or 
+     *    ymmddThhmmss, etc..)
+     * @param string|null $tzid The value of the 'TZID' property.
+     * @return array 
+     */
+    static public function parseData($propertyValue, $tzid) {
+        
+
+        if (is_null($propertyValue)) {
+            return array(null, null);
+        }
+
+        $date = '(?P<year>[1-2][0-9]{3})(?P<month>[0-1][0-9])(?P<date>[0-3][0-9])';
+        $time = '(?P<hour>[0-2][0-9])(?P<minute>[0-5][0-9])(?P<second>[0-5][0-9])';
+        $regex = "/^$date(T$time(?P<isutc>Z)?)?$/";
+
+        if (!preg_match($regex, $propertyValue, $matches)) {
+            throw new InvalidArgumentException($propertyValue . ' is not a valid DateTime or Date string');
+        }
+
+        if (!isset($matches['hour'])) {
+            // Date-only
+            return array(
+                self::DATE,
+                new DateTime($matches['year'] . '-' . $matches['month'] . '-' . $matches['date'] . ' 00:00:00'),
+            );
+        }
+
+        $dateStr = 
+            $matches['year'] .'-' . 
+            $matches['month'] . '-' . 
+            $matches['date'] . ' ' .
+            $matches['hour'] . ':' .
+            $matches['minute'] . ':' .
+            $matches['second']; 
+
+        if (isset($matches['isutc'])) {
+            $dt = new DateTime($dateStr,new DateTimeZone('UTC'));
+            $dt->setTimeZone(new DateTimeZone('UTC'));
+            return array(
+                self::UTC,
+                $dt
+            );
+        }
+
+        if (!$tzid) {
+            return array(
+                self::LOCAL,
+                new DateTime($dateStr)
+            );
+        }
+
+        $tz = new DateTimeZone($tzid->value);
+        $dt = new DateTime($dateStr, $tz);
+        $dt->setTimeZone($tz);
+
+        return array(
+            self::LOCALTZ,
+            $dt
+        );
+
+    }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/VObject/Element/MultiDateTime.php b/3rdparty/Sabre/VObject/Element/MultiDateTime.php
new file mode 100644
index 0000000000000000000000000000000000000000..07f7e82c3467201f3fc7271e76ba4c454394b7ce
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Element/MultiDateTime.php
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * Multi-DateTime property 
+ *
+ * This element is used for iCalendar properties such as the EXDATE property. 
+ * It basically provides a few helper functions that make it easier to deal 
+ * with these. It supports both DATE-TIME and DATE values.
+ *
+ * In order to use this correctly, you must call setDateTimes and getDateTimes 
+ * to retrieve and modify dates respectively.
+ *
+ * If you use the 'value' or properties directly, this object does not keep 
+ * reference and results might appear incorrectly.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Element_MultiDateTime extends Sabre_VObject_Property {
+
+    /**
+     * DateTime representation
+     *
+     * @var DateTime[]
+     */
+    protected $dateTimes;
+
+    /**
+     * dateType
+     *
+     * This is one of the Sabre_VObject_Element_DateTime constants.
+     *
+     * @var int 
+     */
+    protected $dateType;
+
+    /**
+     * Updates the value 
+     * 
+     * @param array $dt Must be an array of DateTime objects. 
+     * @param int $dateType 
+     * @return void
+     */
+    public function setDateTimes(array $dt, $dateType = Sabre_VObject_Element_DateTime::LOCALTZ) {
+
+        foreach($dt as $i) 
+            if (!$i instanceof DateTime) 
+                throw new InvalidArgumentException('You must pass an array of DateTime objects');
+
+        $this->offsetUnset('VALUE');
+        $this->offsetUnset('TZID');
+        switch($dateType) {
+
+            case Sabre_VObject_Element_DateTime::LOCAL :
+                $val = array();
+                foreach($dt as $i) {
+                    $val[] = $i->format('Ymd\\THis');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATETIME'); 
+                break;
+            case Sabre_VObject_Element_DateTime::UTC :
+                $val = array();
+                foreach($dt as $i) {
+                    $i->setTimeZone(new DateTimeZone('UTC'));
+                    $val[] = $i->format('Ymd\\THis\\Z');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATETIME');
+                break;
+            case Sabre_VObject_Element_DateTime::LOCALTZ :
+                $val = array();
+                foreach($dt as $i) {
+                    $val[] = $i->format('Ymd\\THis');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATETIME');
+                $this->offsetSet('TZID', $dt[0]->getTimeZone()->getName());
+                break; 
+            case Sabre_VObject_Element_DateTime::DATE :
+                $val = array();
+                foreach($dt as $i) {
+                    $val[] = $i->format('Ymd');
+                }
+                $this->setValue(implode(',',$val));
+                $this->offsetSet('VALUE','DATE');
+                break;
+            default :
+                throw new InvalidArgumentException('You must pass a valid dateType constant');
+
+        }
+        $this->dateTimes = $dt;
+        $this->dateType = $dateType;
+
+    }
+
+    /**
+     * Returns the current DateTime value.
+     *
+     * If no value was set, this method returns null.
+     *
+     * @return array|null 
+     */
+    public function getDateTimes() {
+
+        if ($this->dateTimes)
+            return $this->dateTimes;
+
+        $dts = array();
+    
+        if (!$this->value) {
+            $this->dateTimes = null;
+            $this->dateType = null;
+            return null;
+        }
+
+        foreach(explode(',',$this->value) as $val) {
+            list(
+                $type,
+                $dt
+            ) = Sabre_VObject_Element_DateTime::parseData($val, $this->offsetGet('TZID'));
+            $dts[] = $dt;
+            $this->dateType = $type;
+        }
+        $this->dateTimes = $dts;
+        return $this->dateTimes;
+
+    }
+
+    /**
+     * Returns the type of Date format.
+     *
+     * This method returns one of the format constants. If no date was set, 
+     * this method will return null.
+     *
+     * @return int|null
+     */
+    public function getDateType() {
+
+        if ($this->dateType)
+            return $this->dateType;
+    
+        if (!$this->value) {
+            $this->dateTimes = null;
+            $this->dateType = null;
+            return null;
+        }
+
+        $dts = array();
+        foreach(explode(',',$this->value) as $val) {
+            list(
+                $type,
+                $dt
+            ) = Sabre_VObject_Element_DateTime::parseData($val, $this->offsetGet('TZID'));
+            $dts[] = $dt;
+            $this->dateType = $type; 
+        }
+        $this->dateTimes = $dts;
+        return $this->dateType;
+
+    }
+
+}
+
+?>
diff --git a/3rdparty/Sabre/VObject/ElementList.php b/3rdparty/Sabre/VObject/ElementList.php
new file mode 100644
index 0000000000000000000000000000000000000000..9922cd587bcdaa3603dc574c130c8bf0faa9a34d
--- /dev/null
+++ b/3rdparty/Sabre/VObject/ElementList.php
@@ -0,0 +1,172 @@
+<?php
+
+/**
+ * VObject ElementList
+ *
+ * This class represents a list of elements. Lists are the result of queries,
+ * such as doing $vcalendar->vevent where there's multiple VEVENT objects.
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_ElementList implements Iterator, Countable, ArrayAccess {
+
+    /**
+     * Inner elements 
+     * 
+     * @var array
+     */
+    protected $elements = array();
+
+    /**
+     * Creates the element list.
+     *
+     * @param array $elements 
+     */
+    public function __construct(array $elements) {
+
+        $this->elements = $elements;
+
+    } 
+
+    /* {{{ Iterator interface */
+
+    /**
+     * Current position  
+     * 
+     * @var int 
+     */
+    private $key = 0;
+
+    /**
+     * Returns current item in iteration 
+     * 
+     * @return Sabre_VObject_Element 
+     */
+    public function current() {
+
+        return $this->elements[$this->key];
+
+    }
+   
+    /**
+     * To the next item in the iterator 
+     * 
+     * @return void
+     */
+    public function next() {
+
+        $this->key++;
+
+    }
+
+    /**
+     * Returns the current iterator key 
+     * 
+     * @return int
+     */
+    public function key() {
+
+        return $this->key;
+
+    }
+
+    /**
+     * Returns true if the current position in the iterator is a valid one 
+     * 
+     * @return bool 
+     */
+    public function valid() {
+
+        return isset($this->elements[$this->key]);
+
+    }
+
+    /**
+     * Rewinds the iterator 
+     * 
+     * @return void 
+     */
+    public function rewind() {
+
+        $this->key = 0;
+
+    }
+
+    /* }}} */
+
+    /* {{{ Countable interface */
+
+    /**
+     * Returns the number of elements 
+     * 
+     * @return int 
+     */
+    public function count() {
+
+        return count($this->elements);
+
+    }
+
+    /* }}} */
+
+    /* {{{ ArrayAccess Interface */
+
+    
+    /**
+     * Checks if an item exists through ArrayAccess.
+     *
+     * @param int $offset 
+     * @return bool 
+     */
+    public function offsetExists($offset) {
+
+        return isset($this->elements[$offset]);
+
+    }
+
+    /**
+     * Gets an item through ArrayAccess.
+     *
+     * @param int $offset 
+     * @return mixed 
+     */
+    public function offsetGet($offset) {
+
+        return $this->elements[$offset];
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * @param int $offset 
+     * @param mixed $value 
+     * @return void
+     */
+    public function offsetSet($offset,$value) {
+
+        throw new LogicException('You can not add new objects to an ElementList');
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @return void
+     */
+    public function offsetUnset($offset) {
+
+        throw new LogicException('You can not remove objects from an ElementList');
+
+    }
+
+    /* }}} */
+
+}
diff --git a/3rdparty/Sabre/VObject/Node.php b/3rdparty/Sabre/VObject/Node.php
new file mode 100644
index 0000000000000000000000000000000000000000..efc7f76da79894a366358e82d0b8f7e1191a36cc
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Node.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * Base class for all nodes 
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Countable {
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    abstract function serialize();
+
+    /**
+     * Iterator override 
+     * 
+     * @var Sabre_VObject_ElementList 
+     */
+    protected $iterator = null;
+
+    /* {{{ IteratorAggregator interface */
+
+    /**
+     * Returns the iterator for this object 
+     * 
+     * @return Sabre_VObject_ElementList 
+     */
+    public function getIterator() {
+
+        if (!is_null($this->iterator)) 
+            return $this->iterator;
+
+        return new Sabre_VObject_ElementList(array($this));
+
+    }
+
+    /**
+     * Sets the overridden iterator
+     *
+     * Note that this is not actually part of the iterator interface
+     * 
+     * @param Sabre_VObject_ElementList $iterator 
+     * @return void
+     */
+    public function setIterator(Sabre_VObject_ElementList $iterator) {
+
+        $this->iterator = $iterator;
+
+    }
+
+    /* }}} */
+
+    /* {{{ Countable interface */
+
+    /**
+     * Returns the number of elements 
+     * 
+     * @return int 
+     */
+    public function count() {
+
+        $it = $this->getIterator();
+        return $it->count();
+
+    }
+
+    /* }}} */
+
+    /* {{{ ArrayAccess Interface */
+
+    
+    /**
+     * Checks if an item exists through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     * 
+     * @param int $offset 
+     * @return bool 
+     */
+    public function offsetExists($offset) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetExists($offset);
+
+    }
+
+    /**
+     * Gets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @return mixed 
+     */
+    public function offsetGet($offset) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetGet($offset);
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @param mixed $value 
+     * @return void
+     */
+    public function offsetSet($offset,$value) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetSet($offset,$value);
+
+    }
+
+    /**
+     * Sets an item through ArrayAccess.
+     *
+     * This method just forwards the request to the inner iterator
+     *
+     * @param int $offset 
+     * @return void
+     */
+    public function offsetUnset($offset) {
+
+        $iterator = $this->getIterator();
+        return $iterator->offsetUnset($offset);
+
+    }
+
+    /* }}} */
+
+}
diff --git a/3rdparty/Sabre/VObject/Parameter.php b/3rdparty/Sabre/VObject/Parameter.php
new file mode 100644
index 0000000000000000000000000000000000000000..9ebab6ec69ba89d929efd85a7f5544d48444c126
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Parameter.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * VObject Parameter
+ *
+ * This class represents a parameter. A parameter is always tied to a property.
+ * In the case of:
+ *   DTSTART;VALUE=DATE:20101108 
+ * VALUE=DATE would be the parameter name and value.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Parameter extends Sabre_VObject_Node {
+
+    /**
+     * Parameter name 
+     * 
+     * @var string 
+     */
+    public $name;
+
+    /**
+     * Parameter value 
+     * 
+     * @var string 
+     */
+    public $value;
+
+    /**
+     * Sets up the object 
+     * 
+     * @param string $name 
+     * @param string $value 
+     */
+    public function __construct($name, $value = null) {
+
+        $this->name = strtoupper($name);
+        $this->value = $value;
+
+    } 
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    public function serialize() {
+
+        $src = array(
+            '\\',
+            "\n",
+            ';',
+            ',',
+        );
+        $out = array(
+            '\\\\',
+            '\n',
+            '\;',
+            '\,',
+        );
+
+        return $this->name . '=' . str_replace($src, $out, $this->value);
+
+    }
+
+    /**
+     * Called when this object is being cast to a string 
+     * 
+     * @return string 
+     */
+    public function __toString() {
+
+        return $this->value;
+
+    }
+
+}
diff --git a/3rdparty/Sabre/VObject/ParseException.php b/3rdparty/Sabre/VObject/ParseException.php
new file mode 100644
index 0000000000000000000000000000000000000000..ed4ef2e859275a5e3bf24015bf5870b632a35b2c
--- /dev/null
+++ b/3rdparty/Sabre/VObject/ParseException.php
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * Exception thrown by Sabre_VObject_Reader if an invalid object was attempted to be parsed.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_ParseException extends Exception { }
diff --git a/3rdparty/Sabre/VObject/Property.php b/3rdparty/Sabre/VObject/Property.php
new file mode 100644
index 0000000000000000000000000000000000000000..201e6356ad6c09adb6af1ac53da26db5fc8cc8c4
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Property.php
@@ -0,0 +1,244 @@
+<?php
+
+/**
+ * VObject Property
+ *
+ * A property in VObject is usually in the form PARAMNAME:paramValue.
+ * An example is : SUMMARY:Weekly meeting 
+ *
+ * Properties can also have parameters:
+ * SUMMARY;LANG=en:Weekly meeting.
+ *
+ * Parameters can be accessed using the ArrayAccess interface. 
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Property extends Sabre_VObject_Element {
+
+    /**
+     * Propertyname 
+     * 
+     * @var string 
+     */
+    public $name;
+
+    /**
+     * Group name
+     * 
+     * This may be something like 'HOME' for vcards.
+     *
+     * @var string 
+     */
+    public $group;
+
+    /**
+     * Property parameters 
+     * 
+     * @var array 
+     */
+    public $parameters = array();
+
+    /**
+     * Property value 
+     * 
+     * @var string 
+     */
+    public $value;
+
+    /**
+     * Creates a new property object
+     * 
+     * By default this object will iterate over its own children, but this can 
+     * be overridden with the iterator argument
+     * 
+     * @param string $name 
+     * @param string $value
+     * @param Sabre_VObject_ElementList $iterator
+     */
+    public function __construct($name, $value = null, $iterator = null) {
+
+        $name = strtoupper($name);
+        $group = null;
+        if (strpos($name,'.')!==false) {
+            list($group, $name) = explode('.', $name);
+        }
+        $this->name = $name;
+        $this->group = $group;
+        if (!is_null($iterator)) $this->iterator = $iterator;
+        $this->setValue($value);
+
+    }
+
+    /**
+     * Updates the internal value 
+     * 
+     * @param string $value 
+     * @return void
+     */
+    public function setValue($value) {
+
+        $this->value = $value;
+
+    }
+
+    /**
+     * Turns the object back into a serialized blob. 
+     * 
+     * @return string 
+     */
+    public function serialize() {
+
+        $str = $this->name;
+        if ($this->group) $str = $this->group . '.' . $this->name;
+
+        if (count($this->parameters)) {
+            foreach($this->parameters as $param) {
+                
+                $str.=';' . $param->serialize();
+
+            }
+        }
+        $src = array(
+            '\\',
+            "\n",
+        );
+        $out = array(
+            '\\\\',
+            '\n',
+        );
+        $str.=':' . str_replace($src, $out, $this->value);
+
+        $out = '';
+        while(strlen($str)>0) {
+            if (strlen($str)>75) {
+                $out.= substr($str,0,75) . "\r\n";
+                $str = ' ' . substr($str,75);
+            } else {
+                $out.=$str . "\r\n";
+                $str='';
+                break;
+            }
+        }
+
+        return $out;
+
+    }
+
+    /* ArrayAccess interface {{{ */
+
+    /**
+     * Checks if an array element exists
+     * 
+     * @param mixed $name 
+     * @return bool 
+     */
+    public function offsetExists($name) {
+
+        if (is_int($name)) return parent::offsetExists($name);
+
+        $name = strtoupper($name);
+
+        foreach($this->parameters as $parameter) {
+            if ($parameter->name == $name) return true;
+        }
+        return false;
+
+    }
+
+    /**
+     * Returns a parameter, or parameter list. 
+     * 
+     * @param string $name 
+     * @return Sabre_VObject_Element 
+     */
+    public function offsetGet($name) {
+
+        if (is_int($name)) return parent::offsetGet($name);
+        $name = strtoupper($name);
+        
+        $result = array();
+        foreach($this->parameters as $parameter) {
+            if ($parameter->name == $name)
+                $result[] = $parameter;
+        }
+
+        if (count($result)===0) {
+            return null;
+        } elseif (count($result)===1) {
+            return $result[0];
+        } else {
+            $result[0]->setIterator(new Sabre_VObject_ElementList($result));
+            return $result[0];
+        }
+
+    }
+
+    /**
+     * Creates a new parameter 
+     * 
+     * @param string $name
+     * @param mixed $value
+     * @return void
+     */
+    public function offsetSet($name, $value) {
+
+        if (is_int($name)) return parent::offsetSet($name, $value);
+
+        if (is_scalar($value)) {
+            if (!is_string($name)) 
+                throw new InvalidArgumentException('A parameter name must be specified. This means you cannot use the $array[]="string" to add parameters.');
+
+            $this->offsetUnset($name);
+            $this->parameters[] = new Sabre_VObject_Parameter($name, $value);
+
+        } elseif ($value instanceof Sabre_VObject_Parameter) {
+            if (!is_null($name))
+                throw new InvalidArgumentException('Don\'t specify a parameter name if you\'re passing a Sabre_VObject_Parameter. Add using $array[]=$parameterObject.');
+            
+            $this->parameters[] = $value;
+        } else {
+            throw new InvalidArgumentException('You can only add parameters to the property object');
+        }
+
+    }
+
+    /**
+     * Removes one or more parameters with the specified name 
+     * 
+     * @param string $name 
+     * @return void 
+     */
+    public function offsetUnset($name) {
+
+        if (is_int($name)) return parent::offsetUnset($name, $value);
+        $name = strtoupper($name);
+        
+        $result = array();
+        foreach($this->parameters as $key=>$parameter) {
+            if ($parameter->name == $name) {
+                unset($this->parameters[$key]);
+            }
+
+        }
+
+    }
+
+    /* }}} */
+
+    /**
+     * Called when this object is being cast to a string 
+     * 
+     * @return string 
+     */
+    public function __toString() {
+
+        return $this->value;
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/VObject/Reader.php b/3rdparty/Sabre/VObject/Reader.php
new file mode 100644
index 0000000000000000000000000000000000000000..9c20e33cea0520b0276ff5dd104483530ffa6489
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Reader.php
@@ -0,0 +1,195 @@
+<?php
+
+/**
+ * VCALENDAR/VCARD reader
+ *
+ * This class reads the vobject file, and returns a full element tree.
+ *
+ *
+ * TODO: this class currently completely works 'statically'. This is pointless, 
+ * and defeats OOP principals. Needs refaxtoring in a future version.
+ * 
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Reader {
+
+    /**
+     * This array contains a list of Property names that are automatically 
+     * mapped to specific class names.
+     *
+     * Adding to this list allows you to specify custom property classes, 
+     * adding extra functionality. 
+     * 
+     * @var array
+     */
+    static public $elementMap = array(
+        'DTSTART'   => 'Sabre_VObject_Element_DateTime',
+        'DTEND'     => 'Sabre_VObject_Element_DateTime',
+        'COMPLETED' => 'Sabre_VObject_Element_DateTime',
+        'DUE'       => 'Sabre_VObject_Element_DateTime',
+        'EXDATE'    => 'Sabre_VObject_Element_MultiDateTime',
+    );
+
+    /**
+     * Parses the file and returns the top component 
+     * 
+     * @param string $data 
+     * @return Sabre_VObject_Element 
+     */
+    static function read($data) {
+
+        // Detecting line endings
+        if (strpos($data,"\r\n")!==false) {
+            $newLine = "\r\n";
+        } elseif (strpos($data,"\r")) {
+            $newLine = "\r";
+        } else {
+            $newLine = "\n";
+        }
+
+        $lines = explode($newLine, $data);
+
+        // Unfolding lines
+        $lines2 = array();
+        foreach($lines as $line) {
+
+            // Skipping empty lines
+            if (!$line) continue;
+
+            if ($line[0]===" " || $line[0]==="\t") {
+                $lines2[count($lines2)-1].=substr($line,1);
+            } else {
+                $lines2[] = $line;
+            }
+
+        }
+
+        unset($lines);
+        
+        reset($lines2);
+
+        return self::readLine($lines2);
+       
+    }
+
+    /**
+     * Reads and parses a single line.
+     *
+     * This method receives the full array of lines. The array pointer is used
+     * to traverse.
+     * 
+     * @param array $lines 
+     * @return Sabre_VObject_Element 
+     */
+    static private function readLine(&$lines) {
+
+        $line = current($lines);
+        $lineNr = key($lines);
+        next($lines);
+
+        // Components
+        if (stripos($line,"BEGIN:")===0) {
+
+            // This is a component
+            $obj = new Sabre_VObject_Component(strtoupper(substr($line,6)));
+
+            $nextLine = current($lines);
+
+            while(stripos($nextLine,"END:")!==0) {
+
+                $obj->children[] = self::readLine($lines);
+                $nextLine = current($lines);
+
+                if ($nextLine===false) 
+                    throw new Sabre_VObject_ParseException('Invalid VObject. Document ended prematurely.');
+
+            }
+
+            // Checking component name of the 'END:' line. 
+            if (substr($nextLine,4)!==$obj->name) {
+                throw new Sabre_VObject_ParseException('Invalid VObject, expected: "END:' . $obj->name . '" got: "' . $nextLine . '"');
+            }
+            next($lines);
+
+            return $obj;
+
+        }
+
+        // Properties
+        //$result = preg_match('/(?P<name>[A-Z0-9-]+)(?:;(?P<parameters>^(?<!:):))(.*)$/',$line,$matches);
+
+
+        $token = '[A-Z0-9-\.]+';
+        $parameters = "(?:;(?P<parameters>([^:^\"]|\"([^\"]*)\")*))?";
+        $regex = "/^(?P<name>$token)$parameters:(?P<value>.*)$/i";
+
+        $result = preg_match($regex,$line,$matches);
+
+        if (!$result) {
+            throw new Sabre_VObject_ParseException('Invalid VObject, line ' . ($lineNr+1) . ' did not follow the icalendar/vcard format');
+        }
+
+        $propertyName = strtoupper($matches['name']);
+        $propertyValue = stripcslashes($matches['value']);
+
+        if (isset(self::$elementMap[$propertyName])) {
+            $className = self::$elementMap[$propertyName];
+        } else {
+            $className = 'Sabre_VObject_Property';
+        }
+
+        $obj = new $className($propertyName, $propertyValue);
+
+        if ($matches['parameters']) {
+
+            $obj->parameters = self::readParameters($matches['parameters']);
+        } 
+
+        return $obj;
+
+
+    }
+
+    /**
+     * Reads a parameter list from a property 
+     *
+     * This method returns an array of Sabre_VObject_Parameter
+     *
+     * @param string $parameters 
+     * @return array 
+     */
+    static private function readParameters($parameters) {
+
+        $token = '[A-Z0-9-]+';
+
+        $paramValue = '(?P<paramValue>[^\"^;]*|"[^"]*")';
+
+        $regex = "/(?<=^|;)(?P<paramName>$token)(=$paramValue(?=$|;))?/i";
+        preg_match_all($regex, $parameters, $matches,  PREG_SET_ORDER);
+
+        $params = array();
+        foreach($matches as $match) {
+
+            $value = isset($match['paramValue'])?$match['paramValue']:null;
+
+            if (isset($value[0])) {
+                // Stripping quotes, if needed
+                if ($value[0] === '"') $value = substr($value,1,strlen($value)-2);
+            } else {
+                $value = '';
+            }
+
+            $params[] = new Sabre_VObject_Parameter($match['paramName'], stripcslashes($value));
+
+        }
+
+        return $params;
+
+    }
+
+
+}
diff --git a/3rdparty/Sabre/VObject/Version.php b/3rdparty/Sabre/VObject/Version.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c3fe67b1f4eb26f54694d2cca4cb8513837d9c2
--- /dev/null
+++ b/3rdparty/Sabre/VObject/Version.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * This class contains the version number for the VObject package
+ * 
+ * @package Sabre
+ * @subpackage VObject 
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/)
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class Sabre_VObject_Version {
+
+    /**
+     * Full version number
+     */
+    const VERSION = '1.2.0';
+
+    /**
+     * Stability : alpha, beta, stable
+     */
+    const STABILITY = 'stable';
+
+}
diff --git a/3rdparty/Sabre/VObject/includes.php b/3rdparty/Sabre/VObject/includes.php
new file mode 100644
index 0000000000000000000000000000000000000000..f21010fb275a84ea9a672e2c2dd6a26a184e37cb
--- /dev/null
+++ b/3rdparty/Sabre/VObject/includes.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * VObject includes 
+ *
+ * This file automatically includes all VObject classes 
+ *
+ * @package Sabre
+ * @subpackage VObject
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+
+include dirname(__FILE__) . '/ParseException.php';
+
+include dirname(__FILE__) . '/Node.php';
+include dirname(__FILE__) . '/Element.php';
+include dirname(__FILE__) . '/ElementList.php';
+include dirname(__FILE__) . '/Parameter.php';
+include dirname(__FILE__) . '/Property.php';
+include dirname(__FILE__) . '/Component.php';
+
+include dirname(__FILE__) . '/Element/DateTime.php';
+include dirname(__FILE__) . '/Element/MultiDateTime.php';
+
+include dirname(__FILE__) . '/Reader.php';
+include dirname(__FILE__) . '/Version.php';
diff --git a/3rdparty/Sabre/autoload.php b/3rdparty/Sabre/autoload.php
new file mode 100644
index 0000000000000000000000000000000000000000..0649df655b028c91f6229fe475e04e0df7865faa
--- /dev/null
+++ b/3rdparty/Sabre/autoload.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * SabreDAV's PHP autoloader
+ *
+ * If you love the autoloader, and don't care as much about performance, this
+ * file register a new autoload function using spl_autoload_register.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+
+function Sabre_autoload($className) {
+
+    if(strpos($className,'Sabre_')===0) {
+
+        include dirname(__FILE__) . '/' . str_replace('_','/',substr($className,6)) . '.php';
+
+    }
+
+}
+
+spl_autoload_register('Sabre_autoload');
+
diff --git a/3rdparty/System.php b/3rdparty/System.php
new file mode 100644
index 0000000000000000000000000000000000000000..97de96b14caea8c8d773ee9359fc48d4bff7be73
--- /dev/null
+++ b/3rdparty/System.php
@@ -0,0 +1,540 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 5                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available through the world-wide-web at the following url:           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Authors: Tomas V.V.Cox <cox@idecnet.com>                             |
+// +----------------------------------------------------------------------+
+//
+// $Id: System.php,v 1.36 2004/06/15 16:33:46 pajoye Exp $
+//
+
+require_once( 'PEAR.php');
+require_once( 'Console/Getopt.php');
+
+$GLOBALS['_System_temp_files'] = array();
+
+/**
+* System offers cross plattform compatible system functions
+*
+* Static functions for different operations. Should work under
+* Unix and Windows. The names and usage has been taken from its respectively
+* GNU commands. The functions will return (bool) false on error and will
+* trigger the error with the PHP trigger_error() function (you can silence
+* the error by prefixing a '@' sign after the function call).
+*
+* Documentation on this class you can find in:
+* http://pear.php.net/manual/
+*
+* Example usage:
+* if (!@System::rm('-r file1 dir1')) {
+*    print "could not delete file1 or dir1";
+* }
+*
+* In case you need to to pass file names with spaces,
+* pass the params as an array:
+*
+* System::rm(array('-r', $file1, $dir1));
+*
+* @package  System
+* @author   Tomas V.V.Cox <cox@idecnet.com>
+* @version  $Revision: 1.36 $
+* @access   public
+* @see      http://pear.php.net/manual/
+*/
+class System
+{
+    /**
+    * returns the commandline arguments of a function
+    *
+    * @param    string  $argv           the commandline
+    * @param    string  $short_options  the allowed option short-tags
+    * @param    string  $long_options   the allowed option long-tags
+    * @return   array   the given options and there values
+    * @access private
+    */
+    function _parseArgs($argv, $short_options, $long_options = null)
+    {
+        if (!is_array($argv) && $argv !== null) {
+            $argv = preg_split('/\s+/', $argv);
+        }
+        return Console_Getopt::getopt2($argv, $short_options);
+    }
+
+    /**
+    * Output errors with PHP trigger_error(). You can silence the errors
+    * with prefixing a "@" sign to the function call: @System::mkdir(..);
+    *
+    * @param mixed $error a PEAR error or a string with the error message
+    * @return bool false
+    * @access private
+    */
+    function raiseError($error)
+    {
+        if (PEAR::isError($error)) {
+            $error = $error->getMessage();
+        }
+        trigger_error($error, E_USER_WARNING);
+        return false;
+    }
+
+    /**
+    * Creates a nested array representing the structure of a directory
+    *
+    * System::_dirToStruct('dir1', 0) =>
+    *   Array
+    *    (
+    *    [dirs] => Array
+    *        (
+    *            [0] => dir1
+    *        )
+    *
+    *    [files] => Array
+    *        (
+    *            [0] => dir1/file2
+    *            [1] => dir1/file3
+    *        )
+    *    )
+    * @param    string  $sPath      Name of the directory
+    * @param    integer $maxinst    max. deep of the lookup
+    * @param    integer $aktinst    starting deep of the lookup
+    * @return   array   the structure of the dir
+    * @access   private
+    */
+
+    function _dirToStruct($sPath, $maxinst, $aktinst = 0)
+    {
+        $struct = array('dirs' => array(), 'files' => array());
+        if (($dir = @opendir($sPath)) === false) {
+            System::raiseError("Could not open dir $sPath");
+            return $struct; // XXX could not open error
+        }
+        $struct['dirs'][] = $sPath; // XXX don't add if '.' or '..' ?
+        $list = array();
+        while ($file = readdir($dir)) {
+            if ($file != '.' && $file != '..') {
+                $list[] = $file;
+            }
+        }
+        closedir($dir);
+        sort($list);
+        if ($aktinst < $maxinst || $maxinst == 0) {
+            foreach($list as $val) {
+                $path = $sPath . DIRECTORY_SEPARATOR . $val;
+                if (is_dir($path)) {
+                    $tmp = System::_dirToStruct($path, $maxinst, $aktinst+1);
+                    $struct = array_merge_recursive($tmp, $struct);
+                } else {
+                    $struct['files'][] = $path;
+                }
+            }
+        }
+        return $struct;
+    }
+
+    /**
+    * Creates a nested array representing the structure of a directory and files
+    *
+    * @param    array $files Array listing files and dirs
+    * @return   array
+    * @see System::_dirToStruct()
+    */
+    function _multipleToStruct($files)
+    {
+        $struct = array('dirs' => array(), 'files' => array());
+        settype($files, 'array');
+        foreach ($files as $file) {
+            if (is_dir($file)) {
+                $tmp = System::_dirToStruct($file, 0);
+                $struct = array_merge_recursive($tmp, $struct);
+            } else {
+                $struct['files'][] = $file;
+            }
+        }
+        return $struct;
+    }
+
+    /**
+    * The rm command for removing files.
+    * Supports multiple files and dirs and also recursive deletes
+    *
+    * @param    string  $args   the arguments for rm
+    * @return   mixed   PEAR_Error or true for success
+    * @access   public
+    */
+    function rm($args)
+    {
+        $opts = System::_parseArgs($args, 'rf'); // "f" do nothing but like it :-)
+        if (PEAR::isError($opts)) {
+            return System::raiseError($opts);
+        }
+        foreach($opts[0] as $opt) {
+            if ($opt[0] == 'r') {
+                $do_recursive = true;
+            }
+        }
+        $ret = true;
+        if (isset($do_recursive)) {
+            $struct = System::_multipleToStruct($opts[1]);
+            foreach($struct['files'] as $file) {
+                if (!@unlink($file)) {
+                    $ret = false;
+                }
+            }
+            foreach($struct['dirs'] as $dir) {
+                if (!@rmdir($dir)) {
+                    $ret = false;
+                }
+            }
+        } else {
+            foreach ($opts[1] as $file) {
+                $delete = (is_dir($file)) ? 'rmdir' : 'unlink';
+                if (!@$delete($file)) {
+                    $ret = false;
+                }
+            }
+        }
+        return $ret;
+    }
+
+    /**
+    * Make directories. Note that we use call_user_func('mkdir') to avoid
+    * a problem with ZE2 calling System::mkDir instead of the native PHP func.
+    *
+    * @param    string  $args    the name of the director(y|ies) to create
+    * @return   bool    True for success
+    * @access   public
+    */
+    function mkDir($args)
+    {
+        $opts = System::_parseArgs($args, 'pm:');
+        if (PEAR::isError($opts)) {
+            return System::raiseError($opts);
+        }
+        $mode = 0777; // default mode
+        foreach($opts[0] as $opt) {
+            if ($opt[0] == 'p') {
+                $create_parents = true;
+            } elseif($opt[0] == 'm') {
+                // if the mode is clearly an octal number (starts with 0)
+                // convert it to decimal
+                if (strlen($opt[1]) && $opt[1]{0} == '0') {
+                    $opt[1] = octdec($opt[1]);
+                } else {
+                    // convert to int
+                    $opt[1] += 0;
+                }
+                $mode = $opt[1];
+            }
+        }
+        $ret = true;
+        if (isset($create_parents)) {
+            foreach($opts[1] as $dir) {
+                $dirstack = array();
+                while (!@is_dir($dir) && $dir != DIRECTORY_SEPARATOR) {
+                    array_unshift($dirstack, $dir);
+                    $dir = dirname($dir);
+                }
+                while ($newdir = array_shift($dirstack)) {
+                    if (!call_user_func('mkdir', $newdir, $mode)) {
+                        $ret = false;
+                    }
+                }
+            }
+        } else {
+            foreach($opts[1] as $dir) {
+                if (!@is_dir($dir) && !call_user_func('mkdir', $dir, $mode)) {
+                    $ret = false;
+                }
+            }
+        }
+        return $ret;
+    }
+
+    /**
+    * Concatenate files
+    *
+    * Usage:
+    * 1) $var = System::cat('sample.txt test.txt');
+    * 2) System::cat('sample.txt test.txt > final.txt');
+    * 3) System::cat('sample.txt test.txt >> final.txt');
+    *
+    * Note: as the class use fopen, urls should work also (test that)
+    *
+    * @param    string  $args   the arguments
+    * @return   boolean true on success
+    * @access   public
+    */
+    function &cat($args)
+    {
+        $ret = null;
+        $files = array();
+        if (!is_array($args)) {
+            $args = preg_split('/\s+/', $args);
+        }
+        for($i=0; $i < count($args); $i++) {
+            if ($args[$i] == '>') {
+                $mode = 'wb';
+                $outputfile = $args[$i+1];
+                break;
+            } elseif ($args[$i] == '>>') {
+                $mode = 'ab+';
+                $outputfile = $args[$i+1];
+                break;
+            } else {
+                $files[] = $args[$i];
+            }
+        }
+        if (isset($mode)) {
+            if (!$outputfd = fopen($outputfile, $mode)) {
+                $err = System::raiseError("Could not open $outputfile");
+                return $err;
+            }
+            $ret = true;
+        }
+        foreach ($files as $file) {
+            if (!$fd = fopen($file, 'r')) {
+                System::raiseError("Could not open $file");
+                continue;
+            }
+            while ($cont = fread($fd, 2048)) {
+                if (isset($outputfd)) {
+                    fwrite($outputfd, $cont);
+                } else {
+                    $ret .= $cont;
+                }
+            }
+            fclose($fd);
+        }
+        if (@is_resource($outputfd)) {
+            fclose($outputfd);
+        }
+        return $ret;
+    }
+
+    /**
+    * Creates temporary files or directories. This function will remove
+    * the created files when the scripts finish its execution.
+    *
+    * Usage:
+    *   1) $tempfile = System::mktemp("prefix");
+    *   2) $tempdir  = System::mktemp("-d prefix");
+    *   3) $tempfile = System::mktemp();
+    *   4) $tempfile = System::mktemp("-t /var/tmp prefix");
+    *
+    * prefix -> The string that will be prepended to the temp name
+    *           (defaults to "tmp").
+    * -d     -> A temporary dir will be created instead of a file.
+    * -t     -> The target dir where the temporary (file|dir) will be created. If
+    *           this param is missing by default the env vars TMP on Windows or
+    *           TMPDIR in Unix will be used. If these vars are also missing
+    *           c:\windows\temp or /tmp will be used.
+    *
+    * @param   string  $args  The arguments
+    * @return  mixed   the full path of the created (file|dir) or false
+    * @see System::tmpdir()
+    * @access  public
+    */
+    function mktemp($args = null)
+    {
+        static $first_time = true;
+        $opts = System::_parseArgs($args, 't:d');
+        if (PEAR::isError($opts)) {
+            return System::raiseError($opts);
+        }
+        foreach($opts[0] as $opt) {
+            if($opt[0] == 'd') {
+                $tmp_is_dir = true;
+            } elseif($opt[0] == 't') {
+                $tmpdir = $opt[1];
+            }
+        }
+        $prefix = (isset($opts[1][0])) ? $opts[1][0] : 'tmp';
+        if (!isset($tmpdir)) {
+            $tmpdir = System::tmpdir();
+        }
+        if (!System::mkDir("-p $tmpdir")) {
+            return false;
+        }
+        $tmp = tempnam($tmpdir, $prefix);
+        if (isset($tmp_is_dir)) {
+            unlink($tmp); // be careful possible race condition here
+            if (!call_user_func('mkdir', $tmp, 0700)) {
+                return System::raiseError("Unable to create temporary directory $tmpdir");
+            }
+        }
+        $GLOBALS['_System_temp_files'][] = $tmp;
+        if ($first_time) {
+            PEAR::registerShutdownFunc(array('System', '_removeTmpFiles'));
+            $first_time = false;
+        }
+        return $tmp;
+    }
+
+    /**
+    * Remove temporary files created my mkTemp. This function is executed
+    * at script shutdown time
+    *
+    * @access private
+    */
+    function _removeTmpFiles()
+    {
+        if (count($GLOBALS['_System_temp_files'])) {
+            $delete = $GLOBALS['_System_temp_files'];
+            array_unshift($delete, '-r');
+            System::rm($delete);
+        }
+    }
+
+    /**
+    * Get the path of the temporal directory set in the system
+    * by looking in its environments variables.
+    * Note: php.ini-recommended removes the "E" from the variables_order setting,
+    * making unavaible the $_ENV array, that s why we do tests with _ENV
+    *
+    * @return string The temporal directory on the system
+    */
+    function tmpdir()
+    {
+        if (OS_WINDOWS) {
+            if ($var = isset($_ENV['TEMP']) ? $_ENV['TEMP'] : getenv('TEMP')) {
+                return $var;
+            }
+            if ($var = isset($_ENV['TMP']) ? $_ENV['TMP'] : getenv('TMP')) {
+                return $var;
+            }
+            if ($var = isset($_ENV['windir']) ? $_ENV['windir'] : getenv('windir')) {
+                return $var;
+            }
+            return getenv('SystemRoot') . '\temp';
+        }
+        if ($var = isset($_ENV['TMPDIR']) ? $_ENV['TMPDIR'] : getenv('TMPDIR')) {
+            return $var;
+        }
+        return '/tmp';
+    }
+
+    /**
+    * The "which" command (show the full path of a command)
+    *
+    * @param string $program The command to search for
+    * @return mixed A string with the full path or false if not found
+    * @author Stig Bakken <ssb@php.net>
+    */
+    function which($program, $fallback = false)
+    {
+        // is_executable() is not available on windows
+        if (OS_WINDOWS) {
+            $pear_is_executable = 'is_file';
+        } else {
+            $pear_is_executable = 'is_executable';
+        }
+
+        // full path given
+        if (basename($program) != $program) {
+            return (@$pear_is_executable($program)) ? $program : $fallback;
+        }
+
+        // XXX FIXME honor safe mode
+        $path_delim = OS_WINDOWS ? ';' : ':';
+        $exe_suffixes = OS_WINDOWS ? array('.exe','.bat','.cmd','.com') : array('');
+        $path_elements = explode($path_delim, getenv('PATH'));
+        foreach ($exe_suffixes as $suff) {
+            foreach ($path_elements as $dir) {
+                $file = $dir . DIRECTORY_SEPARATOR . $program . $suff;
+                if (@is_file($file) && @$pear_is_executable($file)) {
+                    return $file;
+                }
+            }
+        }
+        return $fallback;
+    }
+
+    /**
+    * The "find" command
+    *
+    * Usage:
+    *
+    * System::find($dir);
+    * System::find("$dir -type d");
+    * System::find("$dir -type f");
+    * System::find("$dir -name *.php");
+    * System::find("$dir -name *.php -name *.htm*");
+    * System::find("$dir -maxdepth 1");
+    *
+    * Params implmented:
+    * $dir            -> Start the search at this directory
+    * -type d         -> return only directories
+    * -type f         -> return only files
+    * -maxdepth <n>   -> max depth of recursion
+    * -name <pattern> -> search pattern (bash style). Multiple -name param allowed
+    *
+    * @param  mixed Either array or string with the command line
+    * @return array Array of found files
+    *
+    */
+    function find($args)
+    {
+        if (!is_array($args)) {
+            $args = preg_split('/\s+/', $args, -1, PREG_SPLIT_NO_EMPTY);
+        }
+        $dir = array_shift($args);
+        $patterns = array();
+        $depth = 0;
+        $do_files = $do_dirs = true;
+        for ($i = 0; $i < count($args); $i++) {
+            switch ($args[$i]) {
+                case '-type':
+                    if (in_array($args[$i+1], array('d', 'f'))) {
+                        if ($args[$i+1] == 'd') {
+                             $do_files = false;
+                        } else {
+                            $do_dirs = false;
+                        }
+                    }
+                    $i++;
+                    break;
+                case '-name':
+                    $patterns[] = "(" . preg_replace(array('/\./', '/\*/'),
+                                                     array('\.', '.*'),
+                                                     $args[$i+1])
+                                      . ")";
+                    $i++;
+                    break;
+                case '-maxdepth':
+                    $depth = $args[$i+1];
+                    break;
+            }
+        }
+        $path = System::_dirToStruct($dir, $depth);
+        if ($do_files && $do_dirs) {
+            $files = array_merge($path['files'], $path['dirs']);
+        } elseif ($do_dirs) {
+            $files = $path['dirs'];
+        } else {
+            $files = $path['files'];
+        }
+        if (count($patterns)) {
+            $patterns = implode('|', $patterns);
+            $ret = array();
+            for ($i = 0; $i < count($files); $i++) {
+                if (preg_match("#^$patterns\$#", $files[$i])) {
+                    $ret[] = $files[$i];
+                }
+            }
+            return $ret;
+        }
+        return $files;
+    }
+}
+?>
diff --git a/3rdparty/XML/Parser.php b/3rdparty/XML/Parser.php
new file mode 100644
index 0000000000000000000000000000000000000000..38b4f8fe8a668055ecf77beb1e09c2d226b7dd2d
--- /dev/null
+++ b/3rdparty/XML/Parser.php
@@ -0,0 +1,667 @@
+<?php
+//
+// +----------------------------------------------------------------------+
+// | PHP Version 4                                                        |
+// +----------------------------------------------------------------------+
+// | Copyright (c) 1997-2004 The PHP Group                                |
+// +----------------------------------------------------------------------+
+// | This source file is subject to version 3.0 of the PHP license,       |
+// | that is bundled with this package in the file LICENSE, and is        |
+// | available at through the world-wide-web at                           |
+// | http://www.php.net/license/3_0.txt.                                  |
+// | If you did not receive a copy of the PHP license and are unable to   |
+// | obtain it through the world-wide-web, please send a note to          |
+// | license@php.net so we can mail you a copy immediately.               |
+// +----------------------------------------------------------------------+
+// | Author: Stig Bakken <ssb@fast.no>                                    |
+// |         Tomas V.V.Cox <cox@idecnet.com>                              |
+// |         Stephan Schmidt <schst@php-tools.net>                        |
+// +----------------------------------------------------------------------+
+//
+// $Id: Parser.php,v 1.25 2005/03/25 17:13:10 schst Exp $
+
+/**
+ * XML Parser class.
+ *
+ * This is an XML parser based on PHP's "xml" extension,
+ * based on the bundled expat library.
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author  Stig Bakken <ssb@fast.no>
+ * @author  Tomas V.V.Cox <cox@idecnet.com>
+ * @author  Stephan Schmidt <schst@php-tools.net>
+ */
+
+/**
+ * uses PEAR's error handling
+ */
+require_once('PEAR.php');
+
+/**
+ * resource could not be created
+ */
+define('XML_PARSER_ERROR_NO_RESOURCE', 200);
+
+/**
+ * unsupported mode
+ */
+define('XML_PARSER_ERROR_UNSUPPORTED_MODE', 201);
+
+/**
+ * invalid encoding was given
+ */
+define('XML_PARSER_ERROR_INVALID_ENCODING', 202);
+
+/**
+ * specified file could not be read
+ */
+define('XML_PARSER_ERROR_FILE_NOT_READABLE', 203);
+
+/**
+ * invalid input
+ */
+define('XML_PARSER_ERROR_INVALID_INPUT', 204);
+
+/**
+ * remote file cannot be retrieved in safe mode
+ */
+define('XML_PARSER_ERROR_REMOTE', 205);
+
+/**
+ * XML Parser class.
+ *
+ * This is an XML parser based on PHP's "xml" extension,
+ * based on the bundled expat library.
+ *
+ * Notes:
+ * - It requires PHP 4.0.4pl1 or greater
+ * - From revision 1.17, the function names used by the 'func' mode
+ *   are in the format "xmltag_$elem", for example: use "xmltag_name"
+ *   to handle the <name></name> tags of your xml file.
+ *
+ * @category XML
+ * @package XML_Parser
+ * @author  Stig Bakken <ssb@fast.no>
+ * @author  Tomas V.V.Cox <cox@idecnet.com>
+ * @author  Stephan Schmidt <schst@php-tools.net>
+ * @todo    create XML_Parser_Namespace to parse documents with namespaces
+ * @todo    create XML_Parser_Pull
+ * @todo    Tests that need to be made:
+ *          - mixing character encodings
+ *          - a test using all expat handlers
+ *          - options (folding, output charset)
+ *          - different parsing modes
+ */
+class XML_Parser extends PEAR
+{
+    // {{{ properties
+
+   /**
+     * XML parser handle
+     *
+     * @var  resource
+     * @see  xml_parser_create()
+     */
+    var $parser;
+
+    /**
+     * File handle if parsing from a file
+     *
+     * @var  resource
+     */
+    var $fp;
+
+    /**
+     * Whether to do case folding
+     *
+     * If set to true, all tag and attribute names will
+     * be converted to UPPER CASE.
+     *
+     * @var  boolean
+     */
+    var $folding = true;
+
+    /**
+     * Mode of operation, one of "event" or "func"
+     *
+     * @var  string
+     */
+    var $mode;
+
+    /**
+     * Mapping from expat handler function to class method.
+     *
+     * @var  array
+     */
+    var $handler = array(
+        'character_data_handler'            => 'cdataHandler',
+        'default_handler'                   => 'defaultHandler',
+        'processing_instruction_handler'    => 'piHandler',
+        'unparsed_entity_decl_handler'      => 'unparsedHandler',
+        'notation_decl_handler'             => 'notationHandler',
+        'external_entity_ref_handler'       => 'entityrefHandler'
+    );
+
+    /**
+     * source encoding
+     *
+     * @var string
+     */
+    var $srcenc;
+
+    /**
+     * target encoding
+     *
+     * @var string
+     */
+    var $tgtenc;
+
+    /**
+     * handler object
+     *
+     * @var object
+     */
+    var $_handlerObj;
+
+    // }}}
+
+    /**
+     * PHP5 constructor
+     *
+     * @param string $srcenc source charset encoding, use NULL (default) to use
+     *                       whatever the document specifies
+     * @param string $mode   how this parser object should work, "event" for
+     *                       startelement/endelement-type events, "func"
+     *                       to have it call functions named after elements
+     * @param string $tgenc  a valid target encoding
+     */
+    function __construct($srcenc = null, $mode = 'event', $tgtenc = null)
+    {
+        $this->PEAR('XML_Parser_Error');
+
+        $this->mode   = $mode;
+        $this->srcenc = $srcenc;
+        $this->tgtenc = $tgtenc;
+    }
+    // }}}
+
+    /**
+     * Sets the mode of the parser.
+     *
+     * Possible modes are:
+     * - func
+     * - event
+     *
+     * You can set the mode using the second parameter
+     * in the constructor.
+     *
+     * This method is only needed, when switching to a new
+     * mode at a later point.
+     *
+     * @access  public
+     * @param   string          mode, either 'func' or 'event'
+     * @return  boolean|object  true on success, PEAR_Error otherwise   
+     */
+    function setMode($mode)
+    {
+        if ($mode != 'func' && $mode != 'event') {
+            $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+        }
+
+        $this->mode = $mode;
+        return true;
+    }
+
+    /**
+     * Sets the object, that will handle the XML events
+     *
+     * This allows you to create a handler object independent of the
+     * parser object that you are using and easily switch the underlying
+     * parser.
+     *
+     * If no object will be set, XML_Parser assumes that you
+     * extend this class and handle the events in $this.
+     *
+     * @access  public
+     * @param   object      object to handle the events
+     * @return  boolean     will always return true
+     * @since   v1.2.0beta3
+     */
+    function setHandlerObj(&$obj)
+    {
+        $this->_handlerObj = &$obj;
+        return true;
+    }
+
+    /**
+     * Init the element handlers
+     *
+     * @access  private
+     */
+    function _initHandlers()
+    {
+        if (!is_resource($this->parser)) {
+            return false;
+        }
+
+        if (!is_object($this->_handlerObj)) {
+            $this->_handlerObj = &$this;
+        }
+        switch ($this->mode) {
+
+            case 'func':
+                xml_set_object($this->parser, $this->_handlerObj);
+                xml_set_element_handler($this->parser, array(&$this, 'funcStartHandler'), array(&$this, 'funcEndHandler'));
+                break;
+
+            case 'event':
+                xml_set_object($this->parser, $this->_handlerObj);
+                xml_set_element_handler($this->parser, 'startHandler', 'endHandler');
+                break;
+            default:
+                return $this->raiseError('Unsupported mode given', XML_PARSER_ERROR_UNSUPPORTED_MODE);
+                break;
+        }
+
+
+        /**
+         * set additional handlers for character data, entities, etc.
+         */
+        foreach ($this->handler as $xml_func => $method) {
+            if (method_exists($this->_handlerObj, $method)) {
+                $xml_func = 'xml_set_' . $xml_func;
+                $xml_func($this->parser, $method);
+            }
+		}
+    }
+
+    // {{{ _create()
+
+    /**
+     * create the XML parser resource
+     *
+     * Has been moved from the constructor to avoid
+     * problems with object references.
+     *
+     * Furthermore it allows us returning an error
+     * if something fails.
+     *
+     * @access   private
+     * @return   boolean|object     true on success, PEAR_Error otherwise
+     *
+     * @see xml_parser_create
+     */
+    function _create()
+    {
+        if ($this->srcenc === null) {
+            $xp = @xml_parser_create();
+        } else {
+            $xp = @xml_parser_create($this->srcenc);
+        }
+        if (is_resource($xp)) {
+            if ($this->tgtenc !== null) {
+                if (!@xml_parser_set_option($xp, XML_OPTION_TARGET_ENCODING,
+                                            $this->tgtenc)) {
+                    return $this->raiseError('invalid target encoding', XML_PARSER_ERROR_INVALID_ENCODING);
+                }
+            }
+            $this->parser = $xp;
+            $result = $this->_initHandlers($this->mode);
+            if ($this->isError($result)) {
+                return $result;
+            }
+            xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, $this->folding);
+
+            return true;
+        }
+        return $this->raiseError('Unable to create XML parser resource.', XML_PARSER_ERROR_NO_RESOURCE);
+    }
+
+    // }}}
+    // {{{ reset()
+
+    /**
+     * Reset the parser.
+     *
+     * This allows you to use one parser instance
+     * to parse multiple XML documents.
+     *
+     * @access   public
+     * @return   boolean|object     true on success, PEAR_Error otherwise
+     */
+    function reset()
+    {
+        $result = $this->_create();
+        if ($this->isError( $result )) {
+            return $result;
+        }
+        return true;
+    }
+
+    // }}}
+    // {{{ setInputFile()
+
+    /**
+     * Sets the input xml file to be parsed
+     *
+     * @param    string      Filename (full path)
+     * @return   resource    fopen handle of the given file
+     * @throws   XML_Parser_Error
+     * @see      setInput(), setInputString(), parse()
+     * @access   public
+     */
+    function setInputFile($file)
+    {
+        /**
+         * check, if file is a remote file
+         */
+        if (preg_match('[^(http|ftp)://]', substr($file, 0, 10))) {
+            if (!ini_get('allow_url_fopen')) {
+            	return $this->raiseError('Remote files cannot be parsed, as safe mode is enabled.', XML_PARSER_ERROR_REMOTE);
+            }
+        }
+        $fp = fopen($file, 'rb');
+        if (is_resource($fp)) {
+            $this->fp = $fp;
+            return $fp;
+        }
+        return $this->raiseError('File could not be opened.', XML_PARSER_ERROR_FILE_NOT_READABLE);
+    }
+
+    // }}}
+    // {{{ setInputString()
+    
+    /**
+     * XML_Parser::setInputString()
+     * 
+     * Sets the xml input from a string
+     * 
+     * @param string $data a string containing the XML document
+     * @return null
+     **/
+    function setInputString($data)
+    {
+        $this->fp = $data;
+        return null;
+    }
+    
+    // }}}
+    // {{{ setInput()
+
+    /**
+     * Sets the file handle to use with parse().
+     *
+     * You should use setInputFile() or setInputString() if you
+     * pass a string 
+     *
+     * @param    mixed  $fp  Can be either a resource returned from fopen(),
+     *                       a URL, a local filename or a string.
+     * @access   public
+     * @see      parse()
+     * @uses     setInputString(), setInputFile()
+     */
+    function setInput($fp)
+    {
+        if (is_resource($fp)) {
+            $this->fp = $fp;
+            return true;
+        }
+        // see if it's an absolute URL (has a scheme at the beginning)
+        elseif (eregi('^[a-z]+://', substr($fp, 0, 10))) {
+            return $this->setInputFile($fp);
+        }
+        // see if it's a local file
+        elseif (file_exists($fp)) {
+            return $this->setInputFile($fp);
+        }
+        // it must be a string
+        else {
+            $this->fp = $fp;
+            return true;
+        }
+
+        return $this->raiseError('Illegal input format', XML_PARSER_ERROR_INVALID_INPUT);
+    }
+
+    // }}}
+    // {{{ parse()
+
+    /**
+     * Central parsing function.
+     *
+     * @return   true|object PEAR error     returns true on success, or a PEAR_Error otherwise
+     * @access   public
+     */
+    function parse()
+    {
+        /**
+         * reset the parser
+         */
+        $result = $this->reset();
+        if ($this->isError($result)) {
+            return $result;
+        }
+        // if $this->fp was fopened previously
+        if (is_resource($this->fp)) {
+        
+            while ($data = fread($this->fp, 4096)) {
+                if (!$this->_parseString($data, feof($this->fp))) {
+                    $error = &$this->raiseError();
+                    $this->free();
+                    return $error;
+                }
+            }
+        // otherwise, $this->fp must be a string
+        } else {
+            if (!$this->_parseString($this->fp, true)) {
+                $error = &$this->raiseError();
+                $this->free();
+                return $error;
+            }
+        }
+        $this->free();
+
+        return true;
+    }
+
+    /**
+     * XML_Parser::_parseString()
+     * 
+     * @param string $data
+     * @param boolean $eof
+     * @return bool
+     * @access private
+     * @see parseString()
+     **/
+    function _parseString($data, $eof = false)
+    {
+        return xml_parse($this->parser, $data, $eof);
+    }
+    
+    // }}}
+    // {{{ parseString()
+
+    /**
+     * XML_Parser::parseString()
+     * 
+     * Parses a string.
+     *
+     * @param    string  $data XML data
+     * @param    boolean $eof  If set and TRUE, data is the last piece of data sent in this parser
+     * @throws   XML_Parser_Error
+     * @return   Pear Error|true   true on success or a PEAR Error
+     * @see      _parseString()
+     */
+    function parseString($data, $eof = false)
+    {
+        if (!isset($this->parser) || !is_resource($this->parser)) {
+            $this->reset();
+        }
+        
+        if (!$this->_parseString($data, $eof)) {
+           $error = &$this->raiseError();
+           $this->free();
+           return $error;
+        }
+
+        if ($eof === true) {
+            $this->free();
+        }
+        return true;
+    }
+    
+    /**
+     * XML_Parser::free()
+     * 
+     * Free the internal resources associated with the parser
+     * 
+     * @return null
+     **/
+    function free()
+    {
+        if (isset($this->parser) && is_resource($this->parser)) {
+            xml_parser_free($this->parser);
+            unset( $this->parser );
+        }
+        if (isset($this->fp) && is_resource($this->fp)) {
+            fclose($this->fp);
+        }
+        unset($this->fp);
+        return null;
+    }
+    
+    /**
+     * XML_Parser::raiseError()
+     * 
+     * Throws a XML_Parser_Error
+     * 
+     * @param string  $msg   the error message
+     * @param integer $ecode the error message code
+     * @return XML_Parser_Error 
+     **/
+    function raiseError($msg = null, $ecode = 0,$mode = null,
+                         $options = null,
+                         $userinfo = null,
+                         $error_class = null,
+                         $skipmsg = false)
+    {
+        $msg = !is_null($msg) ? $msg : $this->parser;
+        $err = new XML_Parser_Error($msg, $ecode);
+        return parent::raiseError($err);
+    }
+    
+    // }}}
+    // {{{ funcStartHandler()
+
+    function funcStartHandler($xp, $elem, $attribs)
+    {
+        $func = 'xmltag_' . $elem;
+        if (strchr($func, '.')) {
+            $func = str_replace('.', '_', $func);
+        }
+        if (method_exists($this->_handlerObj, $func)) {
+            call_user_func(array(&$this->_handlerObj, $func), $xp, $elem, $attribs);
+        } elseif (method_exists($this->_handlerObj, 'xmltag')) {
+            call_user_func(array(&$this->_handlerObj, 'xmltag'), $xp, $elem, $attribs);
+        }
+    }
+
+    // }}}
+    // {{{ funcEndHandler()
+
+    function funcEndHandler($xp, $elem)
+    {
+        $func = 'xmltag_' . $elem . '_';
+        if (strchr($func, '.')) {
+            $func = str_replace('.', '_', $func);
+        }
+        if (method_exists($this->_handlerObj, $func)) {
+            call_user_func(array(&$this->_handlerObj, $func), $xp, $elem);
+        } elseif (method_exists($this->_handlerObj, 'xmltag_')) {
+            call_user_func(array(&$this->_handlerObj, 'xmltag_'), $xp, $elem);
+        }
+    }
+
+    // }}}
+    // {{{ startHandler()
+
+    /**
+     *
+     * @abstract
+     */
+    function startHandler($xp, $elem, $attribs)
+    {
+        return NULL;
+    }
+
+    // }}}
+    // {{{ endHandler()
+
+    /**
+     *
+     * @abstract
+     */
+    function endHandler($xp, $elem)
+    {
+        return NULL;
+    }
+
+
+    // }}}me
+}
+
+/**
+ * error class, replaces PEAR_Error
+ *
+ * An instance of this class will be returned
+ * if an error occurs inside XML_Parser.
+ *
+ * There are three advantages over using the standard PEAR_Error:
+ * - All messages will be prefixed
+ * - check for XML_Parser error, using is_a( $error, 'XML_Parser_Error' )
+ * - messages can be generated from the xml_parser resource
+ *
+ * @package XML_Parser
+ * @access  public
+ * @see     PEAR_Error
+ */
+class XML_Parser_Error extends PEAR_Error
+{
+    // {{{ properties
+
+   /**
+    * prefix for all messages
+    *
+    * @var      string
+    */    
+    var $error_message_prefix = 'XML_Parser: ';
+
+    // }}}
+    // {{{ constructor()
+   /**
+    * construct a new error instance
+    *
+    * You may either pass a message or an xml_parser resource as first
+    * parameter. If a resource has been passed, the last error that
+    * happened will be retrieved and returned.
+    *
+    * @access   public
+    * @param    string|resource     message or parser resource
+    * @param    integer             error code
+    * @param    integer             error handling
+    * @param    integer             error level
+    */    
+    function XML_Parser_Error($msgorparser = 'unknown error', $code = 0, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE)
+    {
+        if (is_resource($msgorparser)) {
+            $code = xml_get_error_code($msgorparser);
+            $msgorparser = sprintf('%s at XML input line %d',
+                                   xml_error_string($code),
+                                   xml_get_current_line_number($msgorparser));
+        }
+        $this->PEAR_Error($msgorparser, $code, $mode, $level);
+    }
+    // }}}
+}
+?>
\ No newline at end of file
diff --git a/3rdparty/XML/RPC.php b/3rdparty/XML/RPC.php
new file mode 100644
index 0000000000000000000000000000000000000000..2cdb44f4aea2da2a05bb21a005f61c24276c4bdc
--- /dev/null
+++ b/3rdparty/XML/RPC.php
@@ -0,0 +1,1951 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * PHP implementation of the XML-RPC protocol
+ *
+ * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
+ * It has support for HTTP transport, proxies and authentication.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: License is granted to use or modify this software
+ * ("XML-RPC for PHP") for commercial or non-commercial use provided the
+ * copyright of the author is preserved in any distributed or derivative work.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED 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 AUTHOR 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.
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    CVS: $Id: RPC.php,v 1.83 2005/08/14 20:25:35 danielc Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
+
+if (!function_exists('xml_parser_create')) {
+    PEAR::loadExtension('xml');
+}
+
+/**#@+
+ * Error constants
+ */
+/**
+ * Parameter values don't match parameter types
+ */
+define('XML_RPC_ERROR_INVALID_TYPE', 101);
+/**
+ * Parameter declared to be numeric but the values are not
+ */
+define('XML_RPC_ERROR_NON_NUMERIC_FOUND', 102);
+/**
+ * Communication error
+ */
+define('XML_RPC_ERROR_CONNECTION_FAILED', 103);
+/**
+ * The array or struct has already been started
+ */
+define('XML_RPC_ERROR_ALREADY_INITIALIZED', 104);
+/**
+ * Incorrect parameters submitted
+ */
+define('XML_RPC_ERROR_INCORRECT_PARAMS', 105);
+/**
+ * Programming error by developer
+ */
+define('XML_RPC_ERROR_PROGRAMMING', 106);
+/**#@-*/
+
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_I4']
+ */
+$GLOBALS['XML_RPC_I4'] = 'i4';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Int']
+ */
+$GLOBALS['XML_RPC_Int'] = 'int';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Boolean']
+ */
+$GLOBALS['XML_RPC_Boolean'] = 'boolean';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Double']
+ */
+$GLOBALS['XML_RPC_Double'] = 'double';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_String']
+ */
+$GLOBALS['XML_RPC_String'] = 'string';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_DateTime']
+ */
+$GLOBALS['XML_RPC_DateTime'] = 'dateTime.iso8601';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Base64']
+ */
+$GLOBALS['XML_RPC_Base64'] = 'base64';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Array']
+ */
+$GLOBALS['XML_RPC_Array'] = 'array';
+
+/**
+ * Data types
+ * @global string $GLOBALS['XML_RPC_Struct']
+ */
+$GLOBALS['XML_RPC_Struct'] = 'struct';
+
+
+/**
+ * Data type meta-types
+ * @global array $GLOBALS['XML_RPC_Types']
+ */
+$GLOBALS['XML_RPC_Types'] = array(
+    $GLOBALS['XML_RPC_I4']       => 1,
+    $GLOBALS['XML_RPC_Int']      => 1,
+    $GLOBALS['XML_RPC_Boolean']  => 1,
+    $GLOBALS['XML_RPC_String']   => 1,
+    $GLOBALS['XML_RPC_Double']   => 1,
+    $GLOBALS['XML_RPC_DateTime'] => 1,
+    $GLOBALS['XML_RPC_Base64']   => 1,
+    $GLOBALS['XML_RPC_Array']    => 2,
+    $GLOBALS['XML_RPC_Struct']   => 3,
+);
+
+
+/**
+ * Error message numbers
+ * @global array $GLOBALS['XML_RPC_err']
+ */
+$GLOBALS['XML_RPC_err'] = array(
+    'unknown_method'      => 1,
+    'invalid_return'      => 2,
+    'incorrect_params'    => 3,
+    'introspect_unknown'  => 4,
+    'http_error'          => 5,
+    'not_response_object' => 6,
+    'invalid_request'     => 7,
+);
+
+/**
+ * Error message strings
+ * @global array $GLOBALS['XML_RPC_str']
+ */
+$GLOBALS['XML_RPC_str'] = array(
+    'unknown_method'      => 'Unknown method',
+    'invalid_return'      => 'Invalid return payload: enable debugging to examine incoming payload',
+    'incorrect_params'    => 'Incorrect parameters passed to method',
+    'introspect_unknown'  => 'Can\'t introspect: method unknown',
+    'http_error'          => 'Didn\'t receive 200 OK from remote server.',
+    'not_response_object' => 'The requested method didn\'t return an XML_RPC_Response object.',
+    'invalid_request'     => 'Invalid request payload',
+);
+
+
+/**
+ * Default XML encoding (ISO-8859-1, UTF-8 or US-ASCII)
+ * @global string $GLOBALS['XML_RPC_defencoding']
+ */
+$GLOBALS['XML_RPC_defencoding'] = 'UTF-8';
+
+/**
+ * User error codes start at 800
+ * @global int $GLOBALS['XML_RPC_erruser']
+ */
+$GLOBALS['XML_RPC_erruser'] = 800;
+
+/**
+ * XML parse error codes start at 100
+ * @global int $GLOBALS['XML_RPC_errxml']
+ */
+$GLOBALS['XML_RPC_errxml'] = 100;
+
+
+/**
+ * Compose backslashes for escaping regexp
+ * @global string $GLOBALS['XML_RPC_backslash']
+ */
+$GLOBALS['XML_RPC_backslash'] = chr(92) . chr(92);
+
+
+/**
+ * Valid parents of XML elements
+ * @global array $GLOBALS['XML_RPC_valid_parents']
+ */
+$GLOBALS['XML_RPC_valid_parents'] = array(
+    'BOOLEAN' => array('VALUE'),
+    'I4' => array('VALUE'),
+    'INT' => array('VALUE'),
+    'STRING' => array('VALUE'),
+    'DOUBLE' => array('VALUE'),
+    'DATETIME.ISO8601' => array('VALUE'),
+    'BASE64' => array('VALUE'),
+    'ARRAY' => array('VALUE'),
+    'STRUCT' => array('VALUE'),
+    'PARAM' => array('PARAMS'),
+    'METHODNAME' => array('METHODCALL'),
+    'PARAMS' => array('METHODCALL', 'METHODRESPONSE'),
+    'MEMBER' => array('STRUCT'),
+    'NAME' => array('MEMBER'),
+    'DATA' => array('ARRAY'),
+    'FAULT' => array('METHODRESPONSE'),
+    'VALUE' => array('MEMBER', 'DATA', 'PARAM', 'FAULT'),
+);
+
+
+/**
+ * Stores state during parsing
+ *
+ * quick explanation of components:
+ *   + ac     = accumulates values
+ *   + qt     = decides if quotes are needed for evaluation
+ *   + cm     = denotes struct or array (comma needed)
+ *   + isf    = indicates a fault
+ *   + lv     = indicates "looking for a value": implements the logic
+ *               to allow values with no types to be strings
+ *   + params = stores parameters in method calls
+ *   + method = stores method name
+ *
+ * @global array $GLOBALS['XML_RPC_xh']
+ */
+$GLOBALS['XML_RPC_xh'] = array();
+
+
+/**
+ * Start element handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_se($parser_resource, $name, $attrs)
+{
+    global $XML_RPC_xh, $XML_RPC_DateTime, $XML_RPC_String, $XML_RPC_valid_parents;
+    $parser = (int) $parser_resource;
+
+    // if invalid xmlrpc already detected, skip all processing
+    if ($XML_RPC_xh[$parser]['isf'] >= 2) {
+        return;
+    }
+
+    // check for correct element nesting
+    // top level element can only be of 2 types
+    if (count($XML_RPC_xh[$parser]['stack']) == 0) {
+        if ($name != 'METHODRESPONSE' && $name != 'METHODCALL') {
+            $XML_RPC_xh[$parser]['isf'] = 2;
+            $XML_RPC_xh[$parser]['isf_reason'] = 'missing top level xmlrpc element';
+            return;
+        }
+    } else {
+        // not top level element: see if parent is OK
+        if (!in_array($XML_RPC_xh[$parser]['stack'][0], $XML_RPC_valid_parents[$name])) {
+            $name = preg_replace('[^a-zA-Z0-9._-]', '', $name);
+            $XML_RPC_xh[$parser]['isf'] = 2;
+            $XML_RPC_xh[$parser]['isf_reason'] = "xmlrpc element $name cannot be child of {$XML_RPC_xh[$parser]['stack'][0]}";
+            return;
+        }
+    }
+
+    switch ($name) {
+    case 'STRUCT':
+        $XML_RPC_xh[$parser]['cm']++;
+
+        // turn quoting off
+        $XML_RPC_xh[$parser]['qt'] = 0;
+
+        $cur_val = array();
+        $cur_val['value'] = array();
+        $cur_val['members'] = 1;
+        array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
+        break;
+
+    case 'ARRAY':
+        $XML_RPC_xh[$parser]['cm']++;
+
+        // turn quoting off
+        $XML_RPC_xh[$parser]['qt'] = 0;
+
+        $cur_val = array();
+        $cur_val['value'] = array();
+        $cur_val['members'] = 0;
+        array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
+        break;
+
+    case 'NAME':
+        $XML_RPC_xh[$parser]['ac'] = '';
+        break;
+
+    case 'FAULT':
+        $XML_RPC_xh[$parser]['isf'] = 1;
+        break;
+
+    case 'PARAM':
+        $XML_RPC_xh[$parser]['valuestack'] = array();
+        break;
+
+    case 'VALUE':
+        $XML_RPC_xh[$parser]['lv'] = 1;
+        $XML_RPC_xh[$parser]['vt'] = $XML_RPC_String;
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        // look for a value: if this is still 1 by the
+        // time we reach the first data segment then the type is string
+        // by implication and we need to add in a quote
+        break;
+
+    case 'I4':
+    case 'INT':
+    case 'STRING':
+    case 'BOOLEAN':
+    case 'DOUBLE':
+    case 'DATETIME.ISO8601':
+    case 'BASE64':
+        $XML_RPC_xh[$parser]['ac'] = ''; // reset the accumulator
+
+        if ($name == 'DATETIME.ISO8601' || $name == 'STRING') {
+            $XML_RPC_xh[$parser]['qt'] = 1;
+
+            if ($name == 'DATETIME.ISO8601') {
+                $XML_RPC_xh[$parser]['vt'] = $XML_RPC_DateTime;
+            }
+
+        } elseif ($name == 'BASE64') {
+            $XML_RPC_xh[$parser]['qt'] = 2;
+        } else {
+            // No quoting is required here -- but
+            // at the end of the element we must check
+            // for data format errors.
+            $XML_RPC_xh[$parser]['qt'] = 0;
+        }
+        break;
+
+    case 'MEMBER':
+        $XML_RPC_xh[$parser]['ac'] = '';
+        break;
+
+    case 'DATA':
+    case 'METHODCALL':
+    case 'METHODNAME':
+    case 'METHODRESPONSE':
+    case 'PARAMS':
+        // valid elements that add little to processing
+        break;
+    }
+
+
+    // Save current element to stack
+    array_unshift($XML_RPC_xh[$parser]['stack'], $name);
+
+    if ($name != 'VALUE') {
+        $XML_RPC_xh[$parser]['lv'] = 0;
+    }
+}
+
+/**
+ * End element handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_ee($parser_resource, $name)
+{
+    global $XML_RPC_xh, $XML_RPC_Types, $XML_RPC_String;
+    $parser = (int) $parser_resource;
+
+    if ($XML_RPC_xh[$parser]['isf'] >= 2) {
+        return;
+    }
+
+    // push this element from stack
+    // NB: if XML validates, correct opening/closing is guaranteed and
+    // we do not have to check for $name == $curr_elem.
+    // we also checked for proper nesting at start of elements...
+    $curr_elem = array_shift($XML_RPC_xh[$parser]['stack']);
+
+    switch ($name) {
+    case 'STRUCT':
+    case 'ARRAY':
+    $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
+    $XML_RPC_xh[$parser]['value'] = $cur_val['value'];
+        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+        $XML_RPC_xh[$parser]['cm']--;
+        break;
+
+    case 'NAME':
+    $XML_RPC_xh[$parser]['valuestack'][0]['name'] = $XML_RPC_xh[$parser]['ac'];
+        break;
+
+    case 'BOOLEAN':
+        // special case here: we translate boolean 1 or 0 into PHP
+        // constants true or false
+        if ($XML_RPC_xh[$parser]['ac'] == '1') {
+            $XML_RPC_xh[$parser]['ac'] = 'true';
+        } else {
+            $XML_RPC_xh[$parser]['ac'] = 'false';
+        }
+
+        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+        // Drop through intentionally.
+
+    case 'I4':
+    case 'INT':
+    case 'STRING':
+    case 'DOUBLE':
+    case 'DATETIME.ISO8601':
+    case 'BASE64':
+        if ($XML_RPC_xh[$parser]['qt'] == 1) {
+            // we use double quotes rather than single so backslashification works OK
+            $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
+        } elseif ($XML_RPC_xh[$parser]['qt'] == 2) {
+            $XML_RPC_xh[$parser]['value'] = base64_decode($XML_RPC_xh[$parser]['ac']);
+        } elseif ($name == 'BOOLEAN') {
+            $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
+        } else {
+            // we have an I4, INT or a DOUBLE
+            // we must check that only 0123456789-.<space> are characters here
+            if (!ereg("^[+-]?[0123456789 \t\.]+$", $XML_RPC_xh[$parser]['ac'])) {
+                XML_RPC_Base::raiseError('Non-numeric value received in INT or DOUBLE',
+                                         XML_RPC_ERROR_NON_NUMERIC_FOUND);
+                $XML_RPC_xh[$parser]['value'] = XML_RPC_ERROR_NON_NUMERIC_FOUND;
+            } else {
+                // it's ok, add it on
+                $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
+            }
+        }
+
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        $XML_RPC_xh[$parser]['lv'] = 3; // indicate we've found a value
+        break;
+
+    case 'VALUE':
+        // deal with a string value
+        if (strlen($XML_RPC_xh[$parser]['ac']) > 0 &&
+            $XML_RPC_xh[$parser]['vt'] == $XML_RPC_String) {
+            $XML_RPC_xh[$parser]['value'] = $XML_RPC_xh[$parser]['ac'];
+        }
+
+        $temp = new XML_RPC_Value($XML_RPC_xh[$parser]['value'], $XML_RPC_xh[$parser]['vt']);
+
+        $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
+        if (is_array($cur_val)) {
+            if ($cur_val['members']==0) {
+                $cur_val['value'][] = $temp;
+            } else {
+                $XML_RPC_xh[$parser]['value'] = $temp;
+            }
+            array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
+        } else {
+            $XML_RPC_xh[$parser]['value'] = $temp;
+        }
+        break;
+
+    case 'MEMBER':
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+
+        $cur_val = array_shift($XML_RPC_xh[$parser]['valuestack']);
+        if (is_array($cur_val)) {
+            if ($cur_val['members']==1) {
+                $cur_val['value'][$cur_val['name']] = $XML_RPC_xh[$parser]['value'];
+            }
+            array_unshift($XML_RPC_xh[$parser]['valuestack'], $cur_val);
+        }
+        break;
+
+    case 'DATA':
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = 0;
+        break;
+
+    case 'PARAM':
+        $XML_RPC_xh[$parser]['params'][] = $XML_RPC_xh[$parser]['value'];
+        break;
+
+    case 'METHODNAME':
+    case 'RPCMETHODNAME':
+        $XML_RPC_xh[$parser]['method'] = ereg_replace("^[\n\r\t ]+", '',
+                                                      $XML_RPC_xh[$parser]['ac']);
+        break;
+    }
+
+    // if it's a valid type name, set the type
+    if (isset($XML_RPC_Types[strtolower($name)])) {
+        $XML_RPC_xh[$parser]['vt'] = strtolower($name);
+    }
+}
+
+/**
+ * Character data handler for the XML parser
+ *
+ * @return void
+ */
+function XML_RPC_cd($parser_resource, $data)
+{
+    global $XML_RPC_xh, $XML_RPC_backslash;
+    $parser = (int) $parser_resource;
+
+    if ($XML_RPC_xh[$parser]['lv'] != 3) {
+        // "lookforvalue==3" means that we've found an entire value
+        // and should discard any further character data
+
+        if ($XML_RPC_xh[$parser]['lv'] == 1) {
+            // if we've found text and we're just in a <value> then
+            // turn quoting on, as this will be a string
+            $XML_RPC_xh[$parser]['qt'] = 1;
+            // and say we've found a value
+            $XML_RPC_xh[$parser]['lv'] = 2;
+        }
+
+        // replace characters that eval would
+        // do special things with
+        if (!isset($XML_RPC_xh[$parser]['ac'])) {
+            $XML_RPC_xh[$parser]['ac'] = '';
+        }
+        $XML_RPC_xh[$parser]['ac'] .= $data;
+    }
+}
+
+/**
+ * The common methods and properties for all of the XML_RPC classes
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.4.0
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Base {
+
+    /**
+     * PEAR Error handling
+     *
+     * @return object  PEAR_Error object
+     */
+    function raiseError($msg, $code)
+    {
+        include_once 'PEAR.php';
+        if (is_object(@$this)) {
+            return PEAR::raiseError(get_class($this) . ': ' . $msg, $code);
+        } else {
+            return PEAR::raiseError('XML_RPC: ' . $msg, $code);
+        }
+    }
+
+    /**
+     * Tell whether something is a PEAR_Error object
+     *
+     * @param mixed $value  the item to check
+     *
+     * @return bool  whether $value is a PEAR_Error object or not
+     *
+     * @access public
+     */
+    function isError($value)
+    {
+        return is_a($value, 'PEAR_Error');
+    }
+}
+
+/**
+ * The methods and properties for submitting XML RPC requests
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.4.0
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Client extends XML_RPC_Base {
+
+    /**
+     * The path and name of the RPC server script you want the request to go to
+     * @var string
+     */
+    var $path = '';
+
+    /**
+     * The name of the remote server to connect to
+     * @var string
+     */
+    var $server = '';
+
+    /**
+     * The protocol to use in contacting the remote server
+     * @var string
+     */
+    var $protocol = 'http://';
+
+    /**
+     * The port for connecting to the remote server
+     *
+     * The default is 80 for http:// connections
+     * and 443 for https:// and ssl:// connections.
+     *
+     * @var integer
+     */
+    var $port = 80;
+
+    /**
+     * A user name for accessing the RPC server
+     * @var string
+     * @see XML_RPC_Client::setCredentials()
+     */
+    var $username = '';
+
+    /**
+     * A password for accessing the RPC server
+     * @var string
+     * @see XML_RPC_Client::setCredentials()
+     */
+    var $password = '';
+
+    /**
+     * The name of the proxy server to use, if any
+     * @var string
+     */
+    var $proxy = '';
+
+    /**
+     * The protocol to use in contacting the proxy server, if any
+     * @var string
+     */
+    var $proxy_protocol = 'http://';
+
+    /**
+     * The port for connecting to the proxy server
+     *
+     * The default is 8080 for http:// connections
+     * and 443 for https:// and ssl:// connections.
+     *
+     * @var integer
+     */
+    var $proxy_port = 8080;
+
+    /**
+     * A user name for accessing the proxy server
+     * @var string
+     */
+    var $proxy_user = '';
+
+    /**
+     * A password for accessing the proxy server
+     * @var string
+     */
+    var $proxy_pass = '';
+
+    /**
+     * The error number, if any
+     * @var integer
+     */
+    var $errno = 0;
+
+    /**
+     * The error message, if any
+     * @var string
+     */
+    var $errstr = '';
+
+    /**
+     * The current debug mode (1 = on, 0 = off)
+     * @var integer
+     */
+    var $debug = 0;
+
+    /**
+     * The HTTP headers for the current request.
+     * @var string
+     */
+    var $headers = '';
+
+
+    /**
+     * Sets the object's properties
+     *
+     * @param string  $path        the path and name of the RPC server script
+     *                              you want the request to go to
+     * @param string  $server      the URL of the remote server to connect to.
+     *                              If this parameter doesn't specify a
+     *                              protocol and $port is 443, ssl:// is
+     *                              assumed.
+     * @param integer $port        a port for connecting to the remote server.
+     *                              Defaults to 80 for http:// connections and
+     *                              443 for https:// and ssl:// connections.
+     * @param string  $proxy       the URL of the proxy server to use, if any.
+     *                              If this parameter doesn't specify a
+     *                              protocol and $port is 443, ssl:// is
+     *                              assumed.
+     * @param integer $proxy_port  a port for connecting to the remote server.
+     *                              Defaults to 8080 for http:// connections and
+     *                              443 for https:// and ssl:// connections.
+     * @param string  $proxy_user  a user name for accessing the proxy server
+     * @param string  $proxy_pass  a password for accessing the proxy server
+     *
+     * @return void
+     */
+    function XML_RPC_Client($path, $server, $port = 0,
+                            $proxy = '', $proxy_port = 0,
+                            $proxy_user = '', $proxy_pass = '')
+    {
+        $this->path       = $path;
+        $this->proxy_user = $proxy_user;
+        $this->proxy_pass = $proxy_pass;
+
+        preg_match('@^(http://|https://|ssl://)?(.*)$@', $server, $match);
+        if ($match[1] == '') {
+            if ($port == 443) {
+                $this->server   = $match[2];
+                $this->protocol = 'ssl://';
+                $this->port     = 443;
+            } else {
+                $this->server = $match[2];
+                if ($port) {
+                    $this->port = $port;
+                }
+            }
+        } elseif ($match[1] == 'http://') {
+            $this->server = $match[2];
+            if ($port) {
+                $this->port = $port;
+            }
+        } else {
+            $this->server   = $match[2];
+            $this->protocol = 'ssl://';
+            if ($port) {
+                $this->port = $port;
+            } else {
+                $this->port = 443;
+            }
+        }
+
+        if ($proxy) {
+            preg_match('@^(http://|https://|ssl://)?(.*)$@', $proxy, $match);
+            if ($match[1] == '') {
+                if ($proxy_port == 443) {
+                    $this->proxy          = $match[2];
+                    $this->proxy_protocol = 'ssl://';
+                    $this->proxy_port     = 443;
+                } else {
+                    $this->proxy = $match[2];
+                    if ($proxy_port) {
+                        $this->proxy_port = $proxy_port;
+                    }
+                }
+            } elseif ($match[1] == 'http://') {
+                $this->proxy = $match[2];
+                if ($proxy_port) {
+                    $this->proxy_port = $proxy_port;
+                }
+            } else {
+                $this->proxy          = $match[2];
+                $this->proxy_protocol = 'ssl://';
+                if ($proxy_port) {
+                    $this->proxy_port = $proxy_port;
+                } else {
+                    $this->proxy_port = 443;
+                }
+            }
+        }
+    }
+
+    /**
+     * Change the current debug mode
+     *
+     * @param int $in  where 1 = on, 0 = off
+     *
+     * @return void
+     */
+    function setDebug($in)
+    {
+        if ($in) {
+            $this->debug = 1;
+        } else {
+            $this->debug = 0;
+        }
+    }
+
+    /**
+     * Set username and password properties for connecting to the RPC server
+     *
+     * @param string $u  the user name
+     * @param string $p  the password
+     *
+     * @return void
+     *
+     * @see XML_RPC_Client::$username, XML_RPC_Client::$password
+     */
+    function setCredentials($u, $p)
+    {
+        $this->username = $u;
+        $this->password = $p;
+    }
+
+    /**
+     * Transmit the RPC request via HTTP 1.0 protocol
+     *
+     * @param object $msg       the XML_RPC_Message object
+     * @param int    $timeout   how many seconds to wait for the request
+     *
+     * @return object  an XML_RPC_Response object.  0 is returned if any
+     *                  problems happen.
+     *
+     * @see XML_RPC_Message, XML_RPC_Client::XML_RPC_Client(),
+     *      XML_RPC_Client::setCredentials()
+     */
+    function send($msg, $timeout = 0)
+    {
+        if (strtolower(get_class($msg)) != 'xml_rpc_message') {
+            $this->errstr = 'send()\'s $msg parameter must be an'
+                          . ' XML_RPC_Message object.';
+            $this->raiseError($this->errstr, XML_RPC_ERROR_PROGRAMMING);
+            return 0;
+        }
+        $msg->debug = $this->debug;
+        return $this->sendPayloadHTTP10($msg, $this->server, $this->port,
+                                        $timeout, $this->username,
+                                        $this->password);
+    }
+
+    /**
+     * Transmit the RPC request via HTTP 1.0 protocol
+     *
+     * Requests should be sent using XML_RPC_Client send() rather than
+     * calling this method directly.
+     *
+     * @param object $msg       the XML_RPC_Message object
+     * @param string $server    the server to send the request to
+     * @param int    $port      the server port send the request to
+     * @param int    $timeout   how many seconds to wait for the request
+     *                           before giving up
+     * @param string $username  a user name for accessing the RPC server
+     * @param string $password  a password for accessing the RPC server
+     *
+     * @return object  an XML_RPC_Response object.  0 is returned if any
+     *                  problems happen.
+     *
+     * @access protected
+     * @see XML_RPC_Client::send()
+     */
+    function sendPayloadHTTP10($msg, $server, $port, $timeout = 0,
+                               $username = '', $password = '')
+    {
+        /*
+         * If we're using a proxy open a socket to the proxy server
+         * instead to the xml-rpc server
+         */
+        if ($this->proxy) {
+            if ($this->proxy_protocol == 'http://') {
+                $protocol = '';
+            } else {
+                $protocol = $this->proxy_protocol;
+            }
+            if ($timeout > 0) {
+                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
+                                 $this->errno, $this->errstr, $timeout);
+            } else {
+                $fp = @fsockopen($protocol . $this->proxy, $this->proxy_port,
+                                 $this->errno, $this->errstr);
+            }
+        } else {
+            if ($this->protocol == 'http://') {
+                $protocol = '';
+            } else {
+                $protocol = $this->protocol;
+            }
+            if ($timeout > 0) {
+                $fp = @fsockopen($protocol . $server, $port,
+                                 $this->errno, $this->errstr, $timeout);
+            } else {
+                $fp = @fsockopen($protocol . $server, $port,
+                                 $this->errno, $this->errstr);
+            }
+        }
+
+        /*
+         * Just raising the error without returning it is strange,
+         * but keep it here for backwards compatibility.
+         */
+        if (!$fp && $this->proxy) {
+            $this->raiseError('Connection to proxy server '
+                              . $this->proxy . ':' . $this->proxy_port
+                              . ' failed. ' . $this->errstr,
+                              XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        } elseif (!$fp) {
+            $this->raiseError('Connection to RPC server '
+                              . $server . ':' . $port
+                              . ' failed. ' . $this->errstr,
+                              XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        }
+
+        if ($timeout) {
+            /*
+             * Using socket_set_timeout() because stream_set_timeout()
+             * was introduced in 4.3.0, but we need to support 4.2.0.
+             */
+            socket_set_timeout($fp, $timeout);
+        }
+
+        // Pre-emptive BC hacks for fools calling sendPayloadHTTP10() directly
+        if ($username != $this->username) {
+            $this->setCredentials($username, $password);
+        }
+
+        // Only create the payload if it was not created previously
+        if (empty($msg->payload)) {
+            $msg->createPayload();
+        }
+        $this->createHeaders($msg);
+
+        $op  = $this->headers . "\r\n\r\n";
+        $op .= $msg->payload;
+
+        if (!fputs($fp, $op, strlen($op))) {
+            $this->errstr = 'Write error';
+            return 0;
+        }
+        $resp = $msg->parseResponseFile($fp);
+
+        $meta = socket_get_status($fp);
+        if ($meta['timed_out']) {
+            fclose($fp);
+            $this->errstr = 'RPC server did not send response before timeout.';
+            $this->raiseError($this->errstr, XML_RPC_ERROR_CONNECTION_FAILED);
+            return 0;
+        }
+
+        fclose($fp);
+        return $resp;
+    }
+
+    /**
+     * Determines the HTTP headers and puts it in the $headers property
+     *
+     * @param object $msg       the XML_RPC_Message object
+     *
+     * @return boolean  TRUE if okay, FALSE if the message payload isn't set.
+     *
+     * @access protected
+     */
+    function createHeaders($msg)
+    {
+        if (empty($msg->payload)) {
+            return false;
+        }
+        if ($this->proxy) {
+            $this->headers = 'POST ' . $this->protocol . $this->server;
+            if ($this->proxy_port) {
+                $this->headers .= ':' . $this->port;
+            }
+        } else {
+           $this->headers = 'POST ';
+        }
+        $this->headers .= $this->path. " HTTP/1.0\r\n";
+
+        $this->headers .= "User-Agent: PEAR XML_RPC\r\n";
+        $this->headers .= 'Host: ' . $this->server . "\r\n";
+
+        if ($this->proxy && $this->proxy_user) {
+            $this->headers .= 'Proxy-Authorization: Basic '
+                     . base64_encode("$this->proxy_user:$this->proxy_pass")
+                     . "\r\n";
+        }
+
+        // thanks to Grant Rauscher <grant7@firstworld.net> for this
+        if ($this->username) {
+            $this->headers .= 'Authorization: Basic '
+                     . base64_encode("$this->username:$this->password")
+                     . "\r\n";
+        }
+
+        $this->headers .= "Content-Type: text/xml\r\n";
+        $this->headers .= 'Content-Length: ' . strlen($msg->payload);
+        return true;
+    }
+}
+
+/**
+ * The methods and properties for interpreting responses to XML RPC requests
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.4.0
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Response extends XML_RPC_Base
+{
+    var $xv;
+    var $fn;
+    var $fs;
+    var $hdrs;
+
+    /**
+     * @return void
+     */
+    function XML_RPC_Response($val, $fcode = 0, $fstr = '')
+    {
+        if ($fcode != 0) {
+            $this->fn = $fcode;
+            $this->fs = htmlspecialchars($fstr);
+        } else {
+            $this->xv = $val;
+        }
+    }
+
+    /**
+     * @return int  the error code
+     */
+    function faultCode()
+    {
+        if (isset($this->fn)) {
+            return $this->fn;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * @return string  the error string
+     */
+    function faultString()
+    {
+        return $this->fs;
+    }
+
+    /**
+     * @return mixed  the value
+     */
+    function value()
+    {
+        return $this->xv;
+    }
+
+    /**
+     * @return string  the error message in XML format
+     */
+    function serialize()
+    {
+        $rs = "<methodResponse>\n";
+        if ($this->fn) {
+            $rs .= "<fault>
+  <value>
+    <struct>
+      <member>
+        <name>faultCode</name>
+        <value><int>" . $this->fn . "</int></value>
+      </member>
+      <member>
+        <name>faultString</name>
+        <value><string>" . $this->fs . "</string></value>
+      </member>
+    </struct>
+  </value>
+</fault>";
+        } else {
+            $rs .= "<params>\n<param>\n" . $this->xv->serialize() .
+        "</param>\n</params>";
+        }
+        $rs .= "\n</methodResponse>";
+        return $rs;
+    }
+}
+
+/**
+ * The methods and properties for composing XML RPC messages
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.4.0
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Message extends XML_RPC_Base
+{
+    /**
+     * The current debug mode (1 = on, 0 = off)
+     * @var integer
+     */
+    var $debug = 0;
+
+    /**
+     * The encoding to be used for outgoing messages
+     *
+     * Defaults to the value of <var>$GLOBALS['XML_RPC_defencoding']</var>
+     *
+     * @var string
+     * @see XML_RPC_Message::setSendEncoding(),
+     *      $GLOBALS['XML_RPC_defencoding'], XML_RPC_Message::xml_header()
+     */
+    var $send_encoding = '';
+
+    /**
+     * The method presently being evaluated
+     * @var string
+     */
+    var $methodname = '';
+
+    /**
+     * @var array
+     */
+    var $params = array();
+
+    /**
+     * The XML message being generated
+     * @var string
+     */
+    var $payload = '';
+
+    /**
+     * @return void
+     */
+    function XML_RPC_Message($meth, $pars = 0)
+    {
+        $this->methodname = $meth;
+        if (is_array($pars) && sizeof($pars) > 0) {
+            for ($i = 0; $i < sizeof($pars); $i++) {
+                $this->addParam($pars[$i]);
+            }
+        }
+    }
+
+    /**
+     * Produces the XML declaration including the encoding attribute
+     *
+     * The encoding is determined by this class' <var>$send_encoding</var>
+     * property.  If the <var>$send_encoding</var> property is not set, use
+     * <var>$GLOBALS['XML_RPC_defencoding']</var>.
+     *
+     * @return string  the XML declaration and <methodCall> element
+     *
+     * @see XML_RPC_Message::setSendEncoding(),
+     *      XML_RPC_Message::$send_encoding, $GLOBALS['XML_RPC_defencoding']
+     */
+    function xml_header()
+    {
+        global $XML_RPC_defencoding;
+        if (!$this->send_encoding) {
+            $this->send_encoding = $XML_RPC_defencoding;
+        }
+        return '<?xml version="1.0" encoding="' . $this->send_encoding . '"?>'
+               . "\n<methodCall>\n";
+    }
+
+    /**
+     * @return string  the closing </methodCall> tag
+     */
+    function xml_footer()
+    {
+        return "</methodCall>\n";
+    }
+
+    /**
+     * @return void
+     *
+     * @uses XML_RPC_Message::xml_header(), XML_RPC_Message::xml_footer()
+     */
+    function createPayload()
+    {
+        $this->payload = $this->xml_header();
+        $this->payload .= '<methodName>' . $this->methodname . "</methodName>\n";
+        $this->payload .= "<params>\n";
+        for ($i = 0; $i < sizeof($this->params); $i++) {
+            $p = $this->params[$i];
+            $this->payload .= "<param>\n" . $p->serialize() . "</param>\n";
+        }
+        $this->payload .= "</params>\n";
+        $this->payload .= $this->xml_footer();
+        $this->payload = ereg_replace("[\r\n]+", "\r\n", $this->payload);
+    }
+
+    /**
+     * @return string  the name of the method
+     */
+    function method($meth = '')
+    {
+        if ($meth != '') {
+            $this->methodname = $meth;
+        }
+        return $this->methodname;
+    }
+
+    /**
+     * @return string  the payload
+     */
+    function serialize()
+    {
+        $this->createPayload();
+        return $this->payload;
+    }
+
+    /**
+     * @return void
+     */
+    function addParam($par)
+    {
+        $this->params[] = $par;
+    }
+
+    /**
+     * Obtains an XML_RPC_Value object for the given parameter
+     *
+     * @param int $i  the index number of the parameter to obtain
+     *
+     * @return object  the XML_RPC_Value object.
+     *                  If the parameter doesn't exist, an XML_RPC_Response object.
+     *
+     * @since Returns XML_RPC_Response object on error since Release 1.3.0
+     */
+    function getParam($i)
+    {
+        global $XML_RPC_err, $XML_RPC_str;
+
+        if (isset($this->params[$i])) {
+            return $this->params[$i];
+        } else {
+            $this->raiseError('The submitted request did not contain this parameter',
+                              XML_RPC_ERROR_INCORRECT_PARAMS);
+            return new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
+                                        $XML_RPC_str['incorrect_params']);
+        }
+    }
+
+    /**
+     * @return int  the number of parameters
+     */
+    function getNumParams()
+    {
+        return sizeof($this->params);
+    }
+
+    /**
+     * Sets the XML declaration's encoding attribute
+     *
+     * @param string $type  the encoding type (ISO-8859-1, UTF-8 or US-ASCII)
+     *
+     * @return void
+     *
+     * @see XML_RPC_Message::$send_encoding, XML_RPC_Message::xml_header()
+     * @since Method available since Release 1.2.0
+     */
+    function setSendEncoding($type)
+    {
+        $this->send_encoding = $type;
+    }
+
+    /**
+     * Determine the XML's encoding via the encoding attribute
+     * in the XML declaration
+     *
+     * If the encoding parameter is not set or is not ISO-8859-1, UTF-8
+     * or US-ASCII, $XML_RPC_defencoding will be returned.
+     *
+     * @param string $data  the XML that will be parsed
+     *
+     * @return string  the encoding to be used
+     *
+     * @link   http://php.net/xml_parser_create
+     * @since  Method available since Release 1.2.0
+     */
+    function getEncoding($data)
+    {
+        global $XML_RPC_defencoding;
+
+        if (preg_match('/<\?xml[^>]*\s*encoding\s*=\s*[\'"]([^"\']*)[\'"]/i',
+                       $data, $match))
+        {
+            $match[1] = trim(strtoupper($match[1]));
+            switch ($match[1]) {
+                case 'ISO-8859-1':
+                case 'UTF-8':
+                case 'US-ASCII':
+                    return $match[1];
+                    break;
+
+                default:
+                    return $XML_RPC_defencoding;
+            }
+        } else {
+            return $XML_RPC_defencoding;
+        }
+    }
+
+    /**
+     * @return object  a new XML_RPC_Response object
+     */
+    function parseResponseFile($fp)
+    {
+        $ipd = '';
+        while ($data = @fread($fp, 8192)) {
+            $ipd .= $data;
+        }
+        return $this->parseResponse($ipd);
+    }
+
+    /**
+     * @return object  a new XML_RPC_Response object
+     */
+    function parseResponse($data = '')
+    {
+        global $XML_RPC_xh, $XML_RPC_err, $XML_RPC_str, $XML_RPC_defencoding;
+
+        $encoding = $this->getEncoding($data);
+        $parser_resource = xml_parser_create($encoding);
+        $parser = (int) $parser_resource;
+
+        $XML_RPC_xh = array();
+        $XML_RPC_xh[$parser] = array();
+
+        $XML_RPC_xh[$parser]['cm'] = 0;
+        $XML_RPC_xh[$parser]['isf'] = 0;
+        $XML_RPC_xh[$parser]['ac'] = '';
+        $XML_RPC_xh[$parser]['qt'] = '';
+        $XML_RPC_xh[$parser]['stack'] = array();
+        $XML_RPC_xh[$parser]['valuestack'] = array();
+
+        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
+        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
+        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
+
+        $hdrfnd = 0;
+        if ($this->debug) {
+            print "\n<pre>---GOT---\n";
+            print isset($_SERVER['SERVER_PROTOCOL']) ? htmlspecialchars($data) : $data;
+            print "\n---END---</pre>\n";
+        }
+
+        // See if response is a 200 or a 100 then a 200, else raise error.
+        // But only do this if we're using the HTTP protocol.
+        if (ereg('^HTTP', $data) &&
+            !ereg('^HTTP/[0-9\.]+ 200 ', $data) &&
+            !preg_match('@^HTTP/[0-9\.]+ 10[0-9]([A-Za-z ]+)?[\r\n]+HTTP/[0-9\.]+ 200@', $data))
+        {
+                $errstr = substr($data, 0, strpos($data, "\n") - 1);
+                error_log('HTTP error, got response: ' . $errstr);
+                $r = new XML_RPC_Response(0, $XML_RPC_err['http_error'],
+                                          $XML_RPC_str['http_error'] . ' (' .
+                                          $errstr . ')');
+                xml_parser_free($parser_resource);
+                return $r;
+        }
+
+        // gotta get rid of headers here
+        if (!$hdrfnd && ($brpos = strpos($data,"\r\n\r\n"))) {
+            $XML_RPC_xh[$parser]['ha'] = substr($data, 0, $brpos);
+            $data = substr($data, $brpos + 4);
+            $hdrfnd = 1;
+        }
+
+        /*
+         * be tolerant of junk after methodResponse
+         * (e.g. javascript automatically inserted by free hosts)
+         * thanks to Luca Mariano <luca.mariano@email.it>
+         */
+        $data = substr($data, 0, strpos($data, "</methodResponse>") + 17);
+
+        if (!xml_parse($parser_resource, $data, sizeof($data))) {
+            // thanks to Peter Kocks <peter.kocks@baygate.com>
+            if (xml_get_current_line_number($parser_resource) == 1) {
+                $errstr = 'XML error at line 1, check URL';
+            } else {
+                $errstr = sprintf('XML error: %s at line %d',
+                                  xml_error_string(xml_get_error_code($parser_resource)),
+                                  xml_get_current_line_number($parser_resource));
+            }
+            error_log($errstr);
+            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
+                                      $XML_RPC_str['invalid_return']);
+            xml_parser_free($parser_resource);
+            return $r;
+        }
+
+        xml_parser_free($parser_resource);
+
+        if ($this->debug) {
+            print "\n<pre>---PARSED---\n";
+            var_dump($XML_RPC_xh[$parser]['value']);
+            print "---END---</pre>\n";
+        }
+
+        if ($XML_RPC_xh[$parser]['isf'] > 1) {
+            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
+                                      $XML_RPC_str['invalid_return'].' '.$XML_RPC_xh[$parser]['isf_reason']);
+        } elseif (!is_object($XML_RPC_xh[$parser]['value'])) {
+            // then something odd has happened
+            // and it's time to generate a client side error
+            // indicating something odd went on
+            $r = new XML_RPC_Response(0, $XML_RPC_err['invalid_return'],
+                                      $XML_RPC_str['invalid_return']);
+        } else {
+            $v = $XML_RPC_xh[$parser]['value'];
+            $allOK=1;
+            if ($XML_RPC_xh[$parser]['isf']) {
+                $f = $v->structmem('faultCode');
+                $fs = $v->structmem('faultString');
+                $r = new XML_RPC_Response($v, $f->scalarval(),
+                                          $fs->scalarval());
+            } else {
+                $r = new XML_RPC_Response($v);
+            }
+        }
+        $r->hdrs = split("\r?\n", $XML_RPC_xh[$parser]['ha'][1]);
+        return $r;
+    }
+}
+
+/**
+ * The methods and properties that represent data in XML RPC format
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.4.0
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Value extends XML_RPC_Base
+{
+    var $me = array();
+    var $mytype = 0;
+
+    /**
+     * @return void
+     */
+    function XML_RPC_Value($val = -1, $type = '')
+    {
+        global $XML_RPC_Types;
+        $this->me = array();
+        $this->mytype = 0;
+        if ($val != -1 || $type != '') {
+            if ($type == '') {
+                $type = 'string';
+            }
+            if (!array_key_exists($type, $XML_RPC_Types)) {
+                // XXX
+                // need some way to report this error
+            } elseif ($XML_RPC_Types[$type] == 1) {
+                $this->addScalar($val, $type);
+            } elseif ($XML_RPC_Types[$type] == 2) {
+                $this->addArray($val);
+            } elseif ($XML_RPC_Types[$type] == 3) {
+                $this->addStruct($val);
+            }
+        }
+    }
+
+    /**
+     * @return int  returns 1 if successful or 0 if there are problems
+     */
+    function addScalar($val, $type = 'string')
+    {
+        global $XML_RPC_Types, $XML_RPC_Boolean;
+
+        if ($this->mytype == 1) {
+            $this->raiseError('Scalar can have only one value',
+                              XML_RPC_ERROR_INVALID_TYPE);
+            return 0;
+        }
+        $typeof = $XML_RPC_Types[$type];
+        if ($typeof != 1) {
+            $this->raiseError("Not a scalar type (${typeof})",
+                              XML_RPC_ERROR_INVALID_TYPE);
+            return 0;
+        }
+
+        if ($type == $XML_RPC_Boolean) {
+            if (strcasecmp($val, 'true') == 0
+                || $val == 1
+                || ($val == true && strcasecmp($val, 'false')))
+            {
+                $val = 1;
+            } else {
+                $val = 0;
+            }
+        }
+
+        if ($this->mytype == 2) {
+            // we're adding to an array here
+            $ar = $this->me['array'];
+            $ar[] = new XML_RPC_Value($val, $type);
+            $this->me['array'] = $ar;
+        } else {
+            // a scalar, so set the value and remember we're scalar
+            $this->me[$type] = $val;
+            $this->mytype = $typeof;
+        }
+        return 1;
+    }
+
+    /**
+     * @return int  returns 1 if successful or 0 if there are problems
+     */
+    function addArray($vals)
+    {
+        global $XML_RPC_Types;
+        if ($this->mytype != 0) {
+            $this->raiseError(
+                    'Already initialized as a [' . $this->kindOf() . ']',
+                    XML_RPC_ERROR_ALREADY_INITIALIZED);
+            return 0;
+        }
+        $this->mytype = $XML_RPC_Types['array'];
+        $this->me['array'] = $vals;
+        return 1;
+    }
+
+    /**
+     * @return int  returns 1 if successful or 0 if there are problems
+     */
+    function addStruct($vals)
+    {
+        global $XML_RPC_Types;
+        if ($this->mytype != 0) {
+            $this->raiseError(
+                    'Already initialized as a [' . $this->kindOf() . ']',
+                    XML_RPC_ERROR_ALREADY_INITIALIZED);
+            return 0;
+        }
+        $this->mytype = $XML_RPC_Types['struct'];
+        $this->me['struct'] = $vals;
+        return 1;
+    }
+
+    /**
+     * @return void
+     */
+    function dump($ar)
+    {
+        reset($ar);
+        foreach ($ar as $key => $val) {
+            echo "$key => $val<br />";
+            if ($key == 'array') {
+                foreach ($val as $key2 => $val2) {
+                    echo "-- $key2 => $val2<br />";
+                }
+            }
+        }
+    }
+
+    /**
+     * @return string  the data type of the current value
+     */
+    function kindOf()
+    {
+        switch ($this->mytype) {
+        case 3:
+            return 'struct';
+
+        case 2:
+            return 'array';
+
+        case 1:
+            return 'scalar';
+
+        default:
+            return 'undef';
+        }
+    }
+
+    /**
+     * @return string  the data in XML format
+     */
+    function serializedata($typ, $val)
+    {
+        $rs = '';
+        global $XML_RPC_Types, $XML_RPC_Base64, $XML_RPC_String, $XML_RPC_Boolean;
+        if (!array_key_exists($typ, $XML_RPC_Types)) {
+            // XXX
+            // need some way to report this error
+            return;
+        }
+        switch ($XML_RPC_Types[$typ]) {
+        case 3:
+            // struct
+            $rs .= "<struct>\n";
+            reset($val);
+            foreach ($val as $key2 => $val2) {
+                $rs .= "<member><name>${key2}</name>\n";
+                $rs .= $this->serializeval($val2);
+                $rs .= "</member>\n";
+            }
+            $rs .= '</struct>';
+            break;
+
+        case 2:
+            // array
+            $rs .= "<array>\n<data>\n";
+            for ($i = 0; $i < sizeof($val); $i++) {
+                $rs .= $this->serializeval($val[$i]);
+            }
+            $rs .= "</data>\n</array>";
+            break;
+
+        case 1:
+            switch ($typ) {
+            case $XML_RPC_Base64:
+                $rs .= "<${typ}>" . base64_encode($val) . "</${typ}>";
+                break;
+            case $XML_RPC_Boolean:
+                $rs .= "<${typ}>" . ($val ? '1' : '0') . "</${typ}>";
+                break;
+            case $XML_RPC_String:
+                $rs .= "<${typ}>" . htmlspecialchars($val). "</${typ}>";
+                break;
+            default:
+                $rs .= "<${typ}>${val}</${typ}>";
+            }
+        }
+        return $rs;
+    }
+
+    /**
+     * @return string  the data in XML format
+     */
+    function serialize()
+    {
+        return $this->serializeval($this);
+    }
+
+    /**
+     * @return string  the data in XML format
+     */
+    function serializeval($o)
+    {
+        if (!is_object($o) || empty($o->me) || !is_array($o->me)) {
+            return '';
+        }
+        $ar = $o->me;
+        reset($ar);
+        list($typ, $val) = each($ar);
+        return '<value>' .  $this->serializedata($typ, $val) .  "</value>\n";
+    }
+
+    /**
+     * @return mixed  the contents of the element requested
+     */
+    function structmem($m)
+    {
+        return $this->me['struct'][$m];
+    }
+
+    /**
+     * @return void
+     */
+    function structreset()
+    {
+        reset($this->me['struct']);
+    }
+
+    /**
+     * @return  the key/value pair of the struct's current element
+     */
+    function structeach()
+    {
+        return each($this->me['struct']);
+    }
+
+    /**
+     * @return mixed  the current value
+     */
+    function getval()
+    {
+        // UNSTABLE
+        global $XML_RPC_BOOLEAN, $XML_RPC_Base64;
+
+        reset($this->me);
+        $b = current($this->me);
+
+        // contributed by I Sofer, 2001-03-24
+        // add support for nested arrays to scalarval
+        // i've created a new method here, so as to
+        // preserve back compatibility
+
+        if (is_array($b)) {
+            foreach ($b as $id => $cont) {
+                $b[$id] = $cont->scalarval();
+            }
+        }
+
+        // add support for structures directly encoding php objects
+        if (is_object($b)) {
+            $t = get_object_vars($b);
+            foreach ($t as $id => $cont) {
+                $t[$id] = $cont->scalarval();
+            }
+            foreach ($t as $id => $cont) {
+                $b->$id = $cont;
+            }
+        }
+
+        // end contrib
+        return $b;
+    }
+
+    /**
+     * @return mixed
+     */
+    function scalarval()
+    {
+        global $XML_RPC_Boolean, $XML_RPC_Base64;
+        reset($this->me);
+        return current($this->me);
+    }
+
+    /**
+     * @return string
+     */
+    function scalartyp()
+    {
+        global $XML_RPC_I4, $XML_RPC_Int;
+        reset($this->me);
+        $a = key($this->me);
+        if ($a == $XML_RPC_I4) {
+            $a = $XML_RPC_Int;
+        }
+        return $a;
+    }
+
+    /**
+     * @return mixed  the struct's current element
+     */
+    function arraymem($m)
+    {
+        return $this->me['array'][$m];
+    }
+
+    /**
+     * @return int  the number of elements in the array
+     */
+    function arraysize()
+    {
+        reset($this->me);
+        list($a, $b) = each($this->me);
+        return sizeof($b);
+    }
+
+    /**
+     * Determines if the item submitted is an XML_RPC_Value object
+     *
+     * @param mixed $val  the variable to be evaluated
+     *
+     * @return bool  TRUE if the item is an XML_RPC_Value object
+     *
+     * @static
+     * @since Method available since Release 1.3.0
+     */
+    function isValue($val)
+    {
+        return (strtolower(get_class($val)) == 'xml_rpc_value');
+    }
+}
+
+/**
+ * Return an ISO8601 encoded string
+ *
+ * While timezones ought to be supported, the XML-RPC spec says:
+ *
+ * "Don't assume a timezone. It should be specified by the server in its
+ * documentation what assumptions it makes about timezones."
+ *
+ * This routine always assumes localtime unless $utc is set to 1, in which
+ * case UTC is assumed and an adjustment for locale is made when encoding.
+ *
+ * @return string  the formatted date
+ */
+function XML_RPC_iso8601_encode($timet, $utc = 0)
+{
+    if (!$utc) {
+        $t = strftime('%Y%m%dT%H:%M:%S', $timet);
+    } else {
+        if (function_exists('gmstrftime')) {
+            // gmstrftime doesn't exist in some versions
+            // of PHP
+            $t = gmstrftime('%Y%m%dT%H:%M:%S', $timet);
+        } else {
+            $t = strftime('%Y%m%dT%H:%M:%S', $timet - date('Z'));
+        }
+    }
+    return $t;
+}
+
+/**
+ * Convert a datetime string into a Unix timestamp
+ *
+ * While timezones ought to be supported, the XML-RPC spec says:
+ *
+ * "Don't assume a timezone. It should be specified by the server in its
+ * documentation what assumptions it makes about timezones."
+ *
+ * This routine always assumes localtime unless $utc is set to 1, in which
+ * case UTC is assumed and an adjustment for locale is made when encoding.
+ *
+ * @return int  the unix timestamp of the date submitted
+ */
+function XML_RPC_iso8601_decode($idate, $utc = 0)
+{
+    $t = 0;
+    if (ereg('([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})', $idate, $regs)) {
+        if ($utc) {
+            $t = gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
+        } else {
+            $t = mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]);
+        }
+    }
+    return $t;
+}
+
+/**
+ * Converts an XML_RPC_Value object into native PHP types
+ *
+ * @param object $XML_RPC_val  the XML_RPC_Value object to decode
+ *
+ * @return mixed  the PHP values
+ */
+function XML_RPC_decode($XML_RPC_val)
+{
+    $kind = $XML_RPC_val->kindOf();
+
+    if ($kind == 'scalar') {
+        return $XML_RPC_val->scalarval();
+
+    } elseif ($kind == 'array') {
+        $size = $XML_RPC_val->arraysize();
+        $arr = array();
+        for ($i = 0; $i < $size; $i++) {
+            $arr[] = XML_RPC_decode($XML_RPC_val->arraymem($i));
+        }
+        return $arr;
+
+    } elseif ($kind == 'struct') {
+        $XML_RPC_val->structreset();
+        $arr = array();
+        while (list($key, $value) = $XML_RPC_val->structeach()) {
+            $arr[$key] = XML_RPC_decode($value);
+        }
+        return $arr;
+    }
+}
+
+/**
+ * Converts native PHP types into an XML_RPC_Value object
+ *
+ * @param mixed $php_val  the PHP value or variable you want encoded
+ *
+ * @return object  the XML_RPC_Value object
+ */
+function XML_RPC_encode($php_val)
+{
+    global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double, $XML_RPC_String,
+           $XML_RPC_Array, $XML_RPC_Struct;
+
+    $type = gettype($php_val);
+    $XML_RPC_val = new XML_RPC_Value;
+
+    switch ($type) {
+    case 'array':
+        if (empty($php_val)) {
+            $XML_RPC_val->addArray($php_val);
+            break;
+        }
+        $tmp = array_diff(array_keys($php_val), range(0, count($php_val)-1));
+        if (empty($tmp)) {
+           $arr = array();
+           foreach ($php_val as $k => $v) {
+               $arr[$k] = XML_RPC_encode($v);
+           }
+           $XML_RPC_val->addArray($arr);
+           break;
+        }
+        // fall though if it's not an enumerated array
+
+    case 'object':
+        $arr = array();
+        foreach ($php_val as $k => $v) {
+            $arr[$k] = XML_RPC_encode($v);
+        }
+        $XML_RPC_val->addStruct($arr);
+        break;
+
+    case 'integer':
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_Int);
+        break;
+
+    case 'double':
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_Double);
+        break;
+
+    case 'string':
+    case 'NULL':
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_String);
+        break;
+
+    case 'boolean':
+        // Add support for encoding/decoding of booleans, since they
+        // are supported in PHP
+        // by <G_Giunta_2001-02-29>
+        $XML_RPC_val->addScalar($php_val, $XML_RPC_Boolean);
+        break;
+
+    case 'unknown type':
+    default:
+        $XML_RPC_val = false;
+    }
+    return $XML_RPC_val;
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
+?>
diff --git a/3rdparty/XML/RPC/Server.php b/3rdparty/XML/RPC/Server.php
new file mode 100644
index 0000000000000000000000000000000000000000..5c5c04b1f7afa34ea462ef219fb99add9a7ebcfd
--- /dev/null
+++ b/3rdparty/XML/RPC/Server.php
@@ -0,0 +1,624 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Server commands for our PHP implementation of the XML-RPC protocol
+ *
+ * This is a PEAR-ified version of Useful inc's XML-RPC for PHP.
+ * It has support for HTTP transport, proxies and authentication.
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: License is granted to use or modify this software
+ * ("XML-RPC for PHP") for commercial or non-commercial use provided the
+ * copyright of the author is preserved in any distributed or derivative work.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESSED 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 AUTHOR 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.
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    CVS: $Id: Server.php,v 1.29 2005/08/14 20:25:35 danielc Exp $
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+
+
+/**
+ * Pull in the XML_RPC class
+ */
+require_once 'XML/RPC.php';
+
+
+/**
+ * signature for system.listMethods: return = array,
+ * parameters = a string or nothing
+ * @global array $GLOBALS['XML_RPC_Server_listMethods_sig']
+ */
+$GLOBALS['XML_RPC_Server_listMethods_sig'] = array(
+    array($GLOBALS['XML_RPC_Array'],
+          $GLOBALS['XML_RPC_String']
+    ),
+    array($GLOBALS['XML_RPC_Array'])
+);
+
+/**
+ * docstring for system.listMethods
+ * @global string $GLOBALS['XML_RPC_Server_listMethods_doc']
+ */
+$GLOBALS['XML_RPC_Server_listMethods_doc'] = 'This method lists all the'
+        . ' methods that the XML-RPC server knows how to dispatch';
+
+/**
+ * signature for system.methodSignature: return = array,
+ * parameters = string
+ * @global array $GLOBALS['XML_RPC_Server_methodSignature_sig']
+ */
+$GLOBALS['XML_RPC_Server_methodSignature_sig'] = array(
+    array($GLOBALS['XML_RPC_Array'],
+          $GLOBALS['XML_RPC_String']
+    )
+);
+
+/**
+ * docstring for system.methodSignature
+ * @global string $GLOBALS['XML_RPC_Server_methodSignature_doc']
+ */
+$GLOBALS['XML_RPC_Server_methodSignature_doc'] = 'Returns an array of known'
+        . ' signatures (an array of arrays) for the method name passed. If'
+        . ' no signatures are known, returns a none-array (test for type !='
+        . ' array to detect missing signature)';
+
+/**
+ * signature for system.methodHelp: return = string,
+ * parameters = string
+ * @global array $GLOBALS['XML_RPC_Server_methodHelp_sig']
+ */
+$GLOBALS['XML_RPC_Server_methodHelp_sig'] = array(
+    array($GLOBALS['XML_RPC_String'],
+          $GLOBALS['XML_RPC_String']
+    )
+);
+
+/**
+ * docstring for methodHelp
+ * @global string $GLOBALS['XML_RPC_Server_methodHelp_doc']
+ */
+$GLOBALS['XML_RPC_Server_methodHelp_doc'] = 'Returns help text if defined'
+        . ' for the method passed, otherwise returns an empty string';
+
+/**
+ * dispatch map for the automatically declared XML-RPC methods.
+ * @global array $GLOBALS['XML_RPC_Server_dmap']
+ */
+$GLOBALS['XML_RPC_Server_dmap'] = array(
+    'system.listMethods' => array(
+        'function'  => 'XML_RPC_Server_listMethods',
+        'signature' => $GLOBALS['XML_RPC_Server_listMethods_sig'],
+        'docstring' => $GLOBALS['XML_RPC_Server_listMethods_doc']
+    ),
+    'system.methodHelp' => array(
+        'function'  => 'XML_RPC_Server_methodHelp',
+        'signature' => $GLOBALS['XML_RPC_Server_methodHelp_sig'],
+        'docstring' => $GLOBALS['XML_RPC_Server_methodHelp_doc']
+    ),
+    'system.methodSignature' => array(
+        'function'  => 'XML_RPC_Server_methodSignature',
+        'signature' => $GLOBALS['XML_RPC_Server_methodSignature_sig'],
+        'docstring' => $GLOBALS['XML_RPC_Server_methodSignature_doc']
+    )
+);
+
+/**
+ * @global string $GLOBALS['XML_RPC_Server_debuginfo']
+ */
+$GLOBALS['XML_RPC_Server_debuginfo'] = '';
+
+
+/**
+ * Lists all the methods that the XML-RPC server knows how to dispatch
+ *
+ * @return object  a new XML_RPC_Response object
+ */
+function XML_RPC_Server_listMethods($server, $m)
+{
+    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+
+    $v = new XML_RPC_Value();
+    $outAr = array();
+    foreach ($server->dmap as $key => $val) {
+        $outAr[] = new XML_RPC_Value($key, 'string');
+    }
+    foreach ($XML_RPC_Server_dmap as $key => $val) {
+        $outAr[] = new XML_RPC_Value($key, 'string');
+    }
+    $v->addArray($outAr);
+    return new XML_RPC_Response($v);
+}
+
+/**
+ * Returns an array of known signatures (an array of arrays)
+ * for the given method
+ *
+ * If no signatures are known, returns a none-array
+ * (test for type != array to detect missing signature)
+ *
+ * @return object  a new XML_RPC_Response object
+ */
+function XML_RPC_Server_methodSignature($server, $m)
+{
+    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+
+    $methName = $m->getParam(0);
+    $methName = $methName->scalarval();
+    if (strpos($methName, 'system.') === 0) {
+        $dmap = $XML_RPC_Server_dmap;
+        $sysCall = 1;
+    } else {
+        $dmap = $server->dmap;
+        $sysCall = 0;
+    }
+    //  print "<!-- ${methName} -->\n";
+    if (isset($dmap[$methName])) {
+        if ($dmap[$methName]['signature']) {
+            $sigs = array();
+            $thesigs = $dmap[$methName]['signature'];
+            for ($i = 0; $i < sizeof($thesigs); $i++) {
+                $cursig = array();
+                $inSig = $thesigs[$i];
+                for ($j = 0; $j < sizeof($inSig); $j++) {
+                    $cursig[] = new XML_RPC_Value($inSig[$j], 'string');
+                }
+                $sigs[] = new XML_RPC_Value($cursig, 'array');
+            }
+            $r = new XML_RPC_Response(new XML_RPC_Value($sigs, 'array'));
+        } else {
+            $r = new XML_RPC_Response(new XML_RPC_Value('undef', 'string'));
+        }
+    } else {
+        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
+                                  $XML_RPC_str['introspect_unknown']);
+    }
+    return $r;
+}
+
+/**
+ * Returns help text if defined for the method passed, otherwise returns
+ * an empty string
+ *
+ * @return object  a new XML_RPC_Response object
+ */
+function XML_RPC_Server_methodHelp($server, $m)
+{
+    global $XML_RPC_err, $XML_RPC_str, $XML_RPC_Server_dmap;
+
+    $methName = $m->getParam(0);
+    $methName = $methName->scalarval();
+    if (strpos($methName, 'system.') === 0) {
+        $dmap = $XML_RPC_Server_dmap;
+        $sysCall = 1;
+    } else {
+        $dmap = $server->dmap;
+        $sysCall = 0;
+    }
+
+    if (isset($dmap[$methName])) {
+        if ($dmap[$methName]['docstring']) {
+            $r = new XML_RPC_Response(new XML_RPC_Value($dmap[$methName]['docstring']),
+                                                        'string');
+        } else {
+            $r = new XML_RPC_Response(new XML_RPC_Value('', 'string'));
+        }
+    } else {
+        $r = new XML_RPC_Response(0, $XML_RPC_err['introspect_unknown'],
+                                     $XML_RPC_str['introspect_unknown']);
+    }
+    return $r;
+}
+
+/**
+ * @return void
+ */
+function XML_RPC_Server_debugmsg($m)
+{
+    global $XML_RPC_Server_debuginfo;
+    $XML_RPC_Server_debuginfo = $XML_RPC_Server_debuginfo . $m . "\n";
+}
+
+
+/**
+ * A server for receiving and replying to XML RPC requests
+ *
+ * <code>
+ * $server = new XML_RPC_Server(
+ *     array(
+ *         'isan8' =>
+ *             array(
+ *                 'function' => 'is_8',
+ *                 'signature' =>
+ *                      array(
+ *                          array('boolean', 'int'),
+ *                          array('boolean', 'int', 'boolean'),
+ *                          array('boolean', 'string'),
+ *                          array('boolean', 'string', 'boolean'),
+ *                      ),
+ *                 'docstring' => 'Is the value an 8?'
+ *             ),
+ *     ),
+ *     1,
+ *     0
+ * ); 
+ * </code>
+ *
+ * @category   Web Services
+ * @package    XML_RPC
+ * @author     Edd Dumbill <edd@usefulinc.com>
+ * @author     Stig Bakken <stig@php.net>
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Daniel Convissor <danielc@php.net>
+ * @copyright  1999-2001 Edd Dumbill, 2001-2005 The PHP Group
+ * @version    Release: 1.4.0
+ * @link       http://pear.php.net/package/XML_RPC
+ */
+class XML_RPC_Server
+{
+    /**
+     * The dispatch map, listing the methods this server provides.
+     * @var array
+     */
+    var $dmap = array();
+
+    /**
+     * The present response's encoding
+     * @var string
+     * @see XML_RPC_Message::getEncoding()
+     */
+    var $encoding = '';
+
+    /**
+     * Debug mode (0 = off, 1 = on)
+     * @var integer
+     */
+    var $debug = 0;
+
+    /**
+     * The response's HTTP headers
+     * @var string
+     */
+    var $server_headers = '';
+
+    /**
+     * The response's XML payload
+     * @var string
+     */
+    var $server_payload = '';
+
+
+    /**
+     * Constructor for the XML_RPC_Server class
+     *
+     * @param array $dispMap   the dispatch map. An associative array
+     *                          explaining each function. The keys of the main
+     *                          array are the procedure names used by the
+     *                          clients. The value is another associative array
+     *                          that contains up to three elements:
+     *                            + The 'function' element's value is the name
+     *                              of the function or method that gets called.
+     *                              To define a class' method: 'class::method'.
+     *                            + The 'signature' element (optional) is an
+     *                              array describing the return values and
+     *                              parameters
+     *                            + The 'docstring' element (optional) is a
+     *                              string describing what the method does
+     * @param int $serviceNow  should the HTTP response be sent now?
+     *                          (1 = yes, 0 = no)
+     * @param int $debug       should debug output be displayed?
+     *                          (1 = yes, 0 = no)
+     *
+     * @return void
+     */
+    function XML_RPC_Server($dispMap, $serviceNow = 1, $debug = 0)
+    {
+        global $HTTP_RAW_POST_DATA;
+
+        if ($debug) {
+            $this->debug = 1;
+        } else {
+            $this->debug = 0;
+        }
+
+        $this->dmap = $dispMap;
+
+        if ($serviceNow) {
+            $this->service();
+        } else {
+            $this->createServerPayload();
+            $this->createServerHeaders();
+        }
+    }
+
+    /**
+     * @return string  the debug information if debug debug mode is on
+     */
+    function serializeDebug()
+    {
+        global $XML_RPC_Server_debuginfo, $HTTP_RAW_POST_DATA;
+
+        if ($this->debug) {
+            XML_RPC_Server_debugmsg('vvv POST DATA RECEIVED BY SERVER vvv' . "\n"
+                                    . $HTTP_RAW_POST_DATA
+                                    . "\n" . '^^^ END POST DATA ^^^');
+        }
+
+        if ($XML_RPC_Server_debuginfo != '') {
+            return "<!-- PEAR XML_RPC SERVER DEBUG INFO:\n\n"
+                   . preg_replace('/-(?=-)/', '- ', $XML_RPC_Server_debuginfo)
+                   . "-->\n";
+        } else {
+            return '';
+        }
+    }
+
+    /**
+     * Sends the response
+     *
+     * The encoding and content-type are determined by
+     * XML_RPC_Message::getEncoding()
+     *
+     * @return void
+     *
+     * @uses XML_RPC_Server::createServerPayload(),
+     *       XML_RPC_Server::createServerHeaders()
+     */
+    function service()
+    {
+        if (!$this->server_payload) {
+            $this->createServerPayload();
+        }
+        if (!$this->server_headers) {
+            $this->createServerHeaders();
+        }
+        header($this->server_headers);
+        print $this->server_payload;
+    }
+
+    /**
+     * Generates the payload and puts it in the $server_payload property
+     *
+     * @return void
+     *
+     * @uses XML_RPC_Server::parseRequest(), XML_RPC_Server::$encoding,
+     *       XML_RPC_Response::serialize(), XML_RPC_Server::serializeDebug()
+     */
+    function createServerPayload()
+    {
+        $r = $this->parseRequest();
+        $this->server_payload = '<?xml version="1.0" encoding="'
+                              . $this->encoding . '"?>' . "\n"
+                              . $this->serializeDebug()
+                              . $r->serialize();
+    }
+
+    /**
+     * Determines the HTTP headers and puts them in the $server_headers
+     * property
+     *
+     * @return boolean  TRUE if okay, FALSE if $server_payload isn't set.
+     *
+     * @uses XML_RPC_Server::createServerPayload(),
+     *       XML_RPC_Server::$server_headers
+     */
+    function createServerHeaders()
+    {
+        if (!$this->server_payload) {
+            return false;
+        }
+        $this->server_headers = 'Content-Length: '
+                              . strlen($this->server_payload) . "\r\n"
+                              . 'Content-Type: text/xml;'
+                              . ' charset=' . $this->encoding;
+        return true;
+    }
+
+    /**
+     * @return array
+     */
+    function verifySignature($in, $sig)
+    {
+        for ($i = 0; $i < sizeof($sig); $i++) {
+            // check each possible signature in turn
+            $cursig = $sig[$i];
+            if (sizeof($cursig) == $in->getNumParams() + 1) {
+                $itsOK = 1;
+                for ($n = 0; $n < $in->getNumParams(); $n++) {
+                    $p = $in->getParam($n);
+                    // print "<!-- $p -->\n";
+                    if ($p->kindOf() == 'scalar') {
+                        $pt = $p->scalartyp();
+                    } else {
+                        $pt = $p->kindOf();
+                    }
+                    // $n+1 as first type of sig is return type
+                    if ($pt != $cursig[$n+1]) {
+                        $itsOK = 0;
+                        $pno = $n+1;
+                        $wanted = $cursig[$n+1];
+                        $got = $pt;
+                        break;
+                    }
+                }
+                if ($itsOK) {
+                    return array(1);
+                }
+            }
+        }
+        if (isset($wanted)) {
+            return array(0, "Wanted ${wanted}, got ${got} at param ${pno}");
+        } else {
+            $allowed = array();
+            foreach ($sig as $val) {
+                end($val);
+                $allowed[] = key($val);
+            }
+            $allowed = array_unique($allowed);
+            $last = count($allowed) - 1;
+            if ($last > 0) {
+                $allowed[$last] = 'or ' . $allowed[$last];
+            }
+            return array(0,
+                         'Signature permits ' . implode(', ', $allowed)
+                                . ' parameters but the request had '
+                                . $in->getNumParams());
+        }
+    }
+
+    /**
+     * @return object  a new XML_RPC_Response object
+     *
+     * @uses XML_RPC_Message::getEncoding(), XML_RPC_Server::$encoding
+     */
+    function parseRequest($data = '')
+    {
+        global $XML_RPC_xh, $HTTP_RAW_POST_DATA,
+                $XML_RPC_err, $XML_RPC_str, $XML_RPC_errxml,
+                $XML_RPC_defencoding, $XML_RPC_Server_dmap;
+
+        if ($data == '') {
+            $data = $HTTP_RAW_POST_DATA;
+        }
+
+        $this->encoding = XML_RPC_Message::getEncoding($data);
+        $parser_resource = xml_parser_create($this->encoding);
+        $parser = (int) $parser_resource;
+
+        $XML_RPC_xh[$parser] = array();
+        $XML_RPC_xh[$parser]['cm']     = 0;
+        $XML_RPC_xh[$parser]['isf']    = 0;
+        $XML_RPC_xh[$parser]['params'] = array();
+        $XML_RPC_xh[$parser]['method'] = '';
+        $XML_RPC_xh[$parser]['stack'] = array();	
+        $XML_RPC_xh[$parser]['valuestack'] = array();	
+
+        $plist = '';
+
+        // decompose incoming XML into request structure
+
+        xml_parser_set_option($parser_resource, XML_OPTION_CASE_FOLDING, true);
+        xml_set_element_handler($parser_resource, 'XML_RPC_se', 'XML_RPC_ee');
+        xml_set_character_data_handler($parser_resource, 'XML_RPC_cd');
+        if (!xml_parse($parser_resource, $data, 1)) {
+            // return XML error as a faultCode
+            $r = new XML_RPC_Response(0,
+                                      $XML_RPC_errxml+xml_get_error_code($parser_resource),
+                                      sprintf('XML error: %s at line %d',
+                                              xml_error_string(xml_get_error_code($parser_resource)),
+                                              xml_get_current_line_number($parser_resource)));
+            xml_parser_free($parser_resource);
+        } elseif ($XML_RPC_xh[$parser]['isf']>1) {
+            $r = new XML_RPC_Response(0,
+                                      $XML_RPC_err['invalid_request'],
+                                      $XML_RPC_str['invalid_request']
+                                      . ': '
+                                      . $XML_RPC_xh[$parser]['isf_reason']);
+            xml_parser_free($parser_resource);
+        } else {
+            xml_parser_free($parser_resource);
+            $m = new XML_RPC_Message($XML_RPC_xh[$parser]['method']);
+            // now add parameters in
+            for ($i = 0; $i < sizeof($XML_RPC_xh[$parser]['params']); $i++) {
+                // print '<!-- ' . $XML_RPC_xh[$parser]['params'][$i]. "-->\n";
+                $plist .= "$i - " . var_export($XML_RPC_xh[$parser]['params'][$i], true) . " \n";
+                $m->addParam($XML_RPC_xh[$parser]['params'][$i]);
+            }
+            XML_RPC_Server_debugmsg($plist);
+
+            // now to deal with the method
+            $methName = $XML_RPC_xh[$parser]['method'];
+            if (strpos($methName, 'system.') === 0) {
+                $dmap = $XML_RPC_Server_dmap;
+                $sysCall = 1;
+            } else {
+                $dmap = $this->dmap;
+                $sysCall = 0;
+            }
+
+            if (isset($dmap[$methName]['function'])
+                && is_string($dmap[$methName]['function'])
+                && strpos($dmap[$methName]['function'], '::') !== false)
+            {
+                $dmap[$methName]['function'] =
+                        explode('::', $dmap[$methName]['function']);
+            }
+
+            if (isset($dmap[$methName]['function'])
+                && is_callable($dmap[$methName]['function']))
+            {
+                // dispatch if exists
+                if (isset($dmap[$methName]['signature'])) {
+                    $sr = $this->verifySignature($m,
+                                                 $dmap[$methName]['signature'] );
+                }
+                if (!isset($dmap[$methName]['signature']) || $sr[0]) {
+                    // if no signature or correct signature
+                    if ($sysCall) {
+                        $r = call_user_func($dmap[$methName]['function'], $this, $m);
+                    } else {
+                        $r = call_user_func($dmap[$methName]['function'], $m);
+                    }
+                    if (!is_a($r, 'XML_RPC_Response')) {
+                        $r = new XML_RPC_Response(0, $XML_RPC_err['not_response_object'],
+                                                  $XML_RPC_str['not_response_object']);
+                    }
+                } else {
+                    $r = new XML_RPC_Response(0, $XML_RPC_err['incorrect_params'],
+                                              $XML_RPC_str['incorrect_params']
+                                              . ': ' . $sr[1]);
+                }
+            } else {
+                // else prepare error response
+                $r = new XML_RPC_Response(0, $XML_RPC_err['unknown_method'],
+                                          $XML_RPC_str['unknown_method']);
+            }
+        }
+        return $r;
+    }
+
+    /**
+     * Echos back the input packet as a string value
+     *
+     * @return void
+     *
+     * Useful for debugging.
+     */
+    function echoInput()
+    {
+        global $HTTP_RAW_POST_DATA;
+
+        $r = new XML_RPC_Response(0);
+        $r->xv = new XML_RPC_Value("'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string');
+        print $r->serialize();
+    }
+}
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * c-hanging-comment-ender-p: nil
+ * End:
+ */
+
+?>
diff --git a/3rdparty/css/chosen-sprite.png b/3rdparty/css/chosen-sprite.png
new file mode 100644
index 0000000000000000000000000000000000000000..f20db4439ea5c1038126bf326c8fd048b03a8226
Binary files /dev/null and b/3rdparty/css/chosen-sprite.png differ
diff --git a/3rdparty/css/chosen.css b/3rdparty/css/chosen.css
new file mode 100644
index 0000000000000000000000000000000000000000..247d07bf0217901f93d6a1c83d214db4cef6831f
--- /dev/null
+++ b/3rdparty/css/chosen.css
@@ -0,0 +1,340 @@
+/* @group Base */
+select.chzn-select {
+  visibility: hidden;
+  height: 28px !important;
+  min-height: 28px !important;
+}
+.chzn-container {
+  font-size: 13px;
+  position: relative;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
+.chzn-container .chzn-drop {
+  background: #fff;
+  border: 1px solid #aaa;
+  border-top: 0;
+  position: absolute;
+  top: 29px;
+  left: 0;
+  -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
+  -moz-box-shadow   : 0 4px 5px rgba(0,0,0,.15);
+  -o-box-shadow     : 0 4px 5px rgba(0,0,0,.15);
+  box-shadow        : 0 4px 5px rgba(0,0,0,.15);
+  z-index: 999;
+}
+/* @end */
+
+/* @group Single Chosen */
+.chzn-container-single .chzn-single {
+  background-color: #fff;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
+  background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+  background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+  background-image: -o-linear-gradient(top, #eeeeee 0%,#ffffff 50%);
+  background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 50%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
+  background-image: linear-gradient(top, #eeeeee 0%,#ffffff 50%);
+  -webkit-border-radius: 4px;
+  -moz-border-radius   : 4px;
+  border-radius        : 4px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  border: 1px solid #aaa;
+  display: block;
+  overflow: hidden;
+  white-space: nowrap;
+  position: relative;
+  height: 26px;
+  line-height: 26px;
+  padding: 0 0 0 8px;
+  color: #444;
+  text-decoration: none;
+}
+.chzn-container-single .chzn-single span {
+  margin-right: 26px;
+  display: block;
+  overflow: hidden;
+  white-space: nowrap;
+  -o-text-overflow: ellipsis;
+  -ms-text-overflow: ellipsis;
+  -moz-binding: url('/xml/ellipsis.xml#ellipsis');
+  text-overflow: ellipsis;
+}
+.chzn-container-single .chzn-single div {
+  -webkit-border-radius: 0 4px 4px 0;
+  -moz-border-radius   : 0 4px 4px 0;
+  border-radius        : 0 4px 4px 0;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  background: #ccc;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+  background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+  background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+  background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
+  background-image: -ms-linear-gradient(top, #cccccc 0%,#eeeeee 60%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cccccc', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #cccccc 0%,#eeeeee 60%);
+  border-left: 1px solid #aaa;
+  position: absolute;
+  right: 0;
+  top: 0;
+  display: block;
+  height: 100%;
+  width: 18px;
+}
+.chzn-container-single .chzn-single div b {
+  background: url('chosen-sprite.png') no-repeat 0 1px;
+  display: block;
+  width: 100%;
+  height: 100%;
+}
+.chzn-container-single .chzn-search {
+  padding: 3px 4px;
+  margin: 0;
+  white-space: nowrap;
+}
+.chzn-container-single .chzn-search input {
+  background: #fff url('chosen-sprite.png') no-repeat 100% -20px;
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  margin: 1px 0;
+  padding: 4px 20px 4px 5px;
+  outline: 0;
+  border: 1px solid #aaa;
+  font-family: sans-serif;
+  font-size: 1em;
+}
+.chzn-container-single .chzn-drop {
+  -webkit-border-radius: 0 0 4px 4px;
+  -moz-border-radius   : 0 0 4px 4px;
+  border-radius        : 0 0 4px 4px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+}
+/* @end */
+
+/* @group Multi Chosen */
+.chzn-container-multi .chzn-choices {
+  background-color: #fff;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background-image: -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+  background-image: -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  border: 1px solid #aaa;
+  margin: 0;
+  padding: 0;
+  cursor: text;
+  overflow: hidden;
+  height: auto !important;
+  height: 1%;
+  position: relative;
+}
+.chzn-container-multi .chzn-choices li {
+  float: left;
+  list-style: none;
+}
+.chzn-container-multi .chzn-choices .search-field {
+  white-space: nowrap;
+  margin: 0;
+  padding: 0;
+}
+.chzn-container-multi .chzn-choices .search-field input {
+  color: #666;
+  background: transparent !important;
+  border: 0 !important;
+  padding: 5px;
+  margin: 1px 0;
+  outline: 0;
+  -webkit-box-shadow: none;
+  -moz-box-shadow   : none;
+  -o-box-shadow     : none;
+  box-shadow        : none;
+}
+.chzn-container-multi .chzn-choices .search-field .default {
+  color: #999;
+}
+.chzn-container-multi .chzn-choices .search-choice {
+  -webkit-border-radius: 3px;
+  -moz-border-radius   : 3px;
+  border-radius        : 3px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  background-color: #e4e4e4;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
+  background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
+  background-image: -o-linear-gradient(bottom, #e4e4e4 0%, #eeeeee 70%);
+  background-image: -ms-linear-gradient(top, #e4e4e4 0%,#eeeeee 70%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e4e4e4', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #e4e4e4 0%,#eeeeee 70%);
+  color: #333;
+  border: 1px solid #b4b4b4;
+  line-height: 13px;
+  padding: 3px 19px 3px 6px;
+  margin: 3px 0 3px 5px;
+  position: relative;
+}
+.chzn-container-multi .chzn-choices .search-choice span {
+  cursor: default;
+}
+.chzn-container-multi .chzn-choices .search-choice-focus {
+  background: #d4d4d4;
+}
+.chzn-container-multi .chzn-choices .search-choice .search-choice-close {
+  display: block;
+  position: absolute;
+  right: 5px;
+  top: 6px;
+  width: 8px;
+  height: 9px;
+  font-size: 1px;
+  background: url(chosen-sprite.png) right top no-repeat;
+}
+.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
+  background-position: right -9px;
+}
+.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
+  background-position: right -9px;
+}
+/* @end */
+
+/* @group Results */
+.chzn-container .chzn-results {
+  margin: 0 4px 4px 0;
+  max-height: 190px;
+  padding: 0 0 0 4px;
+  position: relative;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+.chzn-container-multi .chzn-results {
+  margin: -1px 0 0;
+  padding: 0;
+}
+.chzn-container .chzn-results li {
+  line-height: 80%;
+  padding: 7px 7px 8px;
+  margin: 0;
+  list-style: none;
+}
+.chzn-container .chzn-results .active-result {
+  cursor: pointer;
+}
+.chzn-container .chzn-results .highlighted {
+  background: #3875d7;
+  color: #fff;
+}
+.chzn-container .chzn-results li em {
+  background: #feffde;
+  font-style: normal;
+}
+.chzn-container .chzn-results .highlighted em {
+  background: transparent;
+}
+.chzn-container .chzn-results .no-results {
+  background: #f4f4f4;
+}
+.chzn-container .chzn-results .group-result {
+  cursor: default;
+  color: #999;
+  font-weight: bold;
+}
+.chzn-container .chzn-results .group-option {
+  padding-left: 20px;
+}
+.chzn-container-multi .chzn-drop .result-selected {
+  display: none;
+}
+/* @end */
+
+/* @group Active  */
+.chzn-container-active .chzn-single {
+  -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+  -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
+  -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
+  box-shadow        : 0 0 5px rgba(0,0,0,.3);
+  border: 1px solid #5897fb;
+}
+.chzn-container-active .chzn-single-with-drop {
+  border: 1px solid #aaa;
+  -webkit-box-shadow: 0 1px 0 #fff inset;
+  -moz-box-shadow   : 0 1px 0 #fff inset;
+  -o-box-shadow     : 0 1px 0 #fff inset;
+  box-shadow        : 0 1px 0 #fff inset;
+  background-color: #eee;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+  background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+  background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
+  background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
+  -webkit-border-bottom-left-radius : 0;
+  -webkit-border-bottom-right-radius: 0;
+  -moz-border-radius-bottomleft : 0;
+  -moz-border-radius-bottomright: 0;
+  border-bottom-left-radius : 0;
+  border-bottom-right-radius: 0;
+}
+.chzn-container-active .chzn-single-with-drop div {
+  background: transparent;
+  border-left: none;
+}
+.chzn-container-active .chzn-single-with-drop div b {
+  background-position: -18px 1px;
+}
+.chzn-container-active .chzn-choices {
+  -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+  -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
+  -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
+  box-shadow        : 0 0 5px rgba(0,0,0,.3);
+  border: 1px solid #5897fb;
+}
+.chzn-container-active .chzn-choices .search-field input {
+  color: #111 !important;
+}
+/* @end */
+
+/* @group Right to Left */
+.chzn-rtl { direction:rtl;text-align: right; }
+.chzn-rtl .chzn-single { padding-left: 0; padding-right: 8px; }
+.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; }
+.chzn-rtl .chzn-single div { 
+  left: 0; right: auto; 
+  border-left: none; border-right: 1px solid #aaaaaa;
+  -webkit-border-radius: 4px 0 0 4px;
+  -moz-border-radius   : 4px 0 0 4px;
+  border-radius        : 4px 0 0 4px; 
+}
+.chzn-rtl .chzn-choices li { float: right; }
+.chzn-rtl .chzn-choices .search-choice { padding: 3px 6px 3px 19px; margin: 3px 5px 3px 0; }
+.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 5px; right: auto; background-position: right top;}
+.chzn-rtl.chzn-container-single .chzn-results { margin-left: 4px; margin-right: 0; padding-left: 0; padding-right: 4px; }
+.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 20px; }
+.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
+.chzn-rtl .chzn-search input {
+  background: url('chosen-sprite.png') no-repeat -38px -20px, #ffffff;
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);  
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  padding: 4px 5px 4px 20px;
+}
+/* @end */
\ No newline at end of file
diff --git a/3rdparty/css/chosen/chosen-sprite.png b/3rdparty/css/chosen/chosen-sprite.png
new file mode 100644
index 0000000000000000000000000000000000000000..f20db4439ea5c1038126bf326c8fd048b03a8226
Binary files /dev/null and b/3rdparty/css/chosen/chosen-sprite.png differ
diff --git a/3rdparty/css/chosen/chosen.css b/3rdparty/css/chosen/chosen.css
new file mode 100644
index 0000000000000000000000000000000000000000..247d07bf0217901f93d6a1c83d214db4cef6831f
--- /dev/null
+++ b/3rdparty/css/chosen/chosen.css
@@ -0,0 +1,340 @@
+/* @group Base */
+select.chzn-select {
+  visibility: hidden;
+  height: 28px !important;
+  min-height: 28px !important;
+}
+.chzn-container {
+  font-size: 13px;
+  position: relative;
+  display: inline-block;
+  zoom: 1;
+  *display: inline;
+}
+.chzn-container .chzn-drop {
+  background: #fff;
+  border: 1px solid #aaa;
+  border-top: 0;
+  position: absolute;
+  top: 29px;
+  left: 0;
+  -webkit-box-shadow: 0 4px 5px rgba(0,0,0,.15);
+  -moz-box-shadow   : 0 4px 5px rgba(0,0,0,.15);
+  -o-box-shadow     : 0 4px 5px rgba(0,0,0,.15);
+  box-shadow        : 0 4px 5px rgba(0,0,0,.15);
+  z-index: 999;
+}
+/* @end */
+
+/* @group Single Chosen */
+.chzn-container-single .chzn-single {
+  background-color: #fff;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white));
+  background-image: -webkit-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+  background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%);
+  background-image: -o-linear-gradient(top, #eeeeee 0%,#ffffff 50%);
+  background-image: -ms-linear-gradient(top, #eeeeee 0%,#ffffff 50%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff',GradientType=0 );
+  background-image: linear-gradient(top, #eeeeee 0%,#ffffff 50%);
+  -webkit-border-radius: 4px;
+  -moz-border-radius   : 4px;
+  border-radius        : 4px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  border: 1px solid #aaa;
+  display: block;
+  overflow: hidden;
+  white-space: nowrap;
+  position: relative;
+  height: 26px;
+  line-height: 26px;
+  padding: 0 0 0 8px;
+  color: #444;
+  text-decoration: none;
+}
+.chzn-container-single .chzn-single span {
+  margin-right: 26px;
+  display: block;
+  overflow: hidden;
+  white-space: nowrap;
+  -o-text-overflow: ellipsis;
+  -ms-text-overflow: ellipsis;
+  -moz-binding: url('/xml/ellipsis.xml#ellipsis');
+  text-overflow: ellipsis;
+}
+.chzn-container-single .chzn-single div {
+  -webkit-border-radius: 0 4px 4px 0;
+  -moz-border-radius   : 0 4px 4px 0;
+  border-radius        : 0 4px 4px 0;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  background: #ccc;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+  background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+  background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+  background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%);
+  background-image: -ms-linear-gradient(top, #cccccc 0%,#eeeeee 60%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#cccccc', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #cccccc 0%,#eeeeee 60%);
+  border-left: 1px solid #aaa;
+  position: absolute;
+  right: 0;
+  top: 0;
+  display: block;
+  height: 100%;
+  width: 18px;
+}
+.chzn-container-single .chzn-single div b {
+  background: url('chosen-sprite.png') no-repeat 0 1px;
+  display: block;
+  width: 100%;
+  height: 100%;
+}
+.chzn-container-single .chzn-search {
+  padding: 3px 4px;
+  margin: 0;
+  white-space: nowrap;
+}
+.chzn-container-single .chzn-search input {
+  background: #fff url('chosen-sprite.png') no-repeat 100% -20px;
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat 100% -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  margin: 1px 0;
+  padding: 4px 20px 4px 5px;
+  outline: 0;
+  border: 1px solid #aaa;
+  font-family: sans-serif;
+  font-size: 1em;
+}
+.chzn-container-single .chzn-drop {
+  -webkit-border-radius: 0 0 4px 4px;
+  -moz-border-radius   : 0 0 4px 4px;
+  border-radius        : 0 0 4px 4px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+}
+/* @end */
+
+/* @group Multi Chosen */
+.chzn-container-multi .chzn-choices {
+  background-color: #fff;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background-image: -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+  background-image: -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  border: 1px solid #aaa;
+  margin: 0;
+  padding: 0;
+  cursor: text;
+  overflow: hidden;
+  height: auto !important;
+  height: 1%;
+  position: relative;
+}
+.chzn-container-multi .chzn-choices li {
+  float: left;
+  list-style: none;
+}
+.chzn-container-multi .chzn-choices .search-field {
+  white-space: nowrap;
+  margin: 0;
+  padding: 0;
+}
+.chzn-container-multi .chzn-choices .search-field input {
+  color: #666;
+  background: transparent !important;
+  border: 0 !important;
+  padding: 5px;
+  margin: 1px 0;
+  outline: 0;
+  -webkit-box-shadow: none;
+  -moz-box-shadow   : none;
+  -o-box-shadow     : none;
+  box-shadow        : none;
+}
+.chzn-container-multi .chzn-choices .search-field .default {
+  color: #999;
+}
+.chzn-container-multi .chzn-choices .search-choice {
+  -webkit-border-radius: 3px;
+  -moz-border-radius   : 3px;
+  border-radius        : 3px;
+  -moz-background-clip   : padding;
+  -webkit-background-clip: padding-box;
+  background-clip        : padding-box;
+  background-color: #e4e4e4;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
+  background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%);
+  background-image: -o-linear-gradient(bottom, #e4e4e4 0%, #eeeeee 70%);
+  background-image: -ms-linear-gradient(top, #e4e4e4 0%,#eeeeee 70%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e4e4e4', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #e4e4e4 0%,#eeeeee 70%);
+  color: #333;
+  border: 1px solid #b4b4b4;
+  line-height: 13px;
+  padding: 3px 19px 3px 6px;
+  margin: 3px 0 3px 5px;
+  position: relative;
+}
+.chzn-container-multi .chzn-choices .search-choice span {
+  cursor: default;
+}
+.chzn-container-multi .chzn-choices .search-choice-focus {
+  background: #d4d4d4;
+}
+.chzn-container-multi .chzn-choices .search-choice .search-choice-close {
+  display: block;
+  position: absolute;
+  right: 5px;
+  top: 6px;
+  width: 8px;
+  height: 9px;
+  font-size: 1px;
+  background: url(chosen-sprite.png) right top no-repeat;
+}
+.chzn-container-multi .chzn-choices .search-choice .search-choice-close:hover {
+  background-position: right -9px;
+}
+.chzn-container-multi .chzn-choices .search-choice-focus .search-choice-close {
+  background-position: right -9px;
+}
+/* @end */
+
+/* @group Results */
+.chzn-container .chzn-results {
+  margin: 0 4px 4px 0;
+  max-height: 190px;
+  padding: 0 0 0 4px;
+  position: relative;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+.chzn-container-multi .chzn-results {
+  margin: -1px 0 0;
+  padding: 0;
+}
+.chzn-container .chzn-results li {
+  line-height: 80%;
+  padding: 7px 7px 8px;
+  margin: 0;
+  list-style: none;
+}
+.chzn-container .chzn-results .active-result {
+  cursor: pointer;
+}
+.chzn-container .chzn-results .highlighted {
+  background: #3875d7;
+  color: #fff;
+}
+.chzn-container .chzn-results li em {
+  background: #feffde;
+  font-style: normal;
+}
+.chzn-container .chzn-results .highlighted em {
+  background: transparent;
+}
+.chzn-container .chzn-results .no-results {
+  background: #f4f4f4;
+}
+.chzn-container .chzn-results .group-result {
+  cursor: default;
+  color: #999;
+  font-weight: bold;
+}
+.chzn-container .chzn-results .group-option {
+  padding-left: 20px;
+}
+.chzn-container-multi .chzn-drop .result-selected {
+  display: none;
+}
+/* @end */
+
+/* @group Active  */
+.chzn-container-active .chzn-single {
+  -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+  -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
+  -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
+  box-shadow        : 0 0 5px rgba(0,0,0,.3);
+  border: 1px solid #5897fb;
+}
+.chzn-container-active .chzn-single-with-drop {
+  border: 1px solid #aaa;
+  -webkit-box-shadow: 0 1px 0 #fff inset;
+  -moz-box-shadow   : 0 1px 0 #fff inset;
+  -o-box-shadow     : 0 1px 0 #fff inset;
+  box-shadow        : 0 1px 0 #fff inset;
+  background-color: #eee;
+  background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee));
+  background-image: -webkit-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+  background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%);
+  background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%);
+  background-image: -ms-linear-gradient(top, #ffffff 0%,#eeeeee 50%);
+  filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#eeeeee',GradientType=0 );
+  background-image: linear-gradient(top, #ffffff 0%,#eeeeee 50%);
+  -webkit-border-bottom-left-radius : 0;
+  -webkit-border-bottom-right-radius: 0;
+  -moz-border-radius-bottomleft : 0;
+  -moz-border-radius-bottomright: 0;
+  border-bottom-left-radius : 0;
+  border-bottom-right-radius: 0;
+}
+.chzn-container-active .chzn-single-with-drop div {
+  background: transparent;
+  border-left: none;
+}
+.chzn-container-active .chzn-single-with-drop div b {
+  background-position: -18px 1px;
+}
+.chzn-container-active .chzn-choices {
+  -webkit-box-shadow: 0 0 5px rgba(0,0,0,.3);
+  -moz-box-shadow   : 0 0 5px rgba(0,0,0,.3);
+  -o-box-shadow     : 0 0 5px rgba(0,0,0,.3);
+  box-shadow        : 0 0 5px rgba(0,0,0,.3);
+  border: 1px solid #5897fb;
+}
+.chzn-container-active .chzn-choices .search-field input {
+  color: #111 !important;
+}
+/* @end */
+
+/* @group Right to Left */
+.chzn-rtl { direction:rtl;text-align: right; }
+.chzn-rtl .chzn-single { padding-left: 0; padding-right: 8px; }
+.chzn-rtl .chzn-single span { margin-left: 26px; margin-right: 0; }
+.chzn-rtl .chzn-single div { 
+  left: 0; right: auto; 
+  border-left: none; border-right: 1px solid #aaaaaa;
+  -webkit-border-radius: 4px 0 0 4px;
+  -moz-border-radius   : 4px 0 0 4px;
+  border-radius        : 4px 0 0 4px; 
+}
+.chzn-rtl .chzn-choices li { float: right; }
+.chzn-rtl .chzn-choices .search-choice { padding: 3px 6px 3px 19px; margin: 3px 5px 3px 0; }
+.chzn-rtl .chzn-choices .search-choice .search-choice-close { left: 5px; right: auto; background-position: right top;}
+.chzn-rtl.chzn-container-single .chzn-results { margin-left: 4px; margin-right: 0; padding-left: 0; padding-right: 4px; }
+.chzn-rtl .chzn-results .group-option { padding-left: 0; padding-right: 20px; }
+.chzn-rtl.chzn-container-active .chzn-single-with-drop div { border-right: none; }
+.chzn-rtl .chzn-search input {
+  background: url('chosen-sprite.png') no-repeat -38px -20px, #ffffff;
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee));
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -webkit-linear-gradient(center bottom, white 85%, #eeeeee 99%);  
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, -ms-linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  background: url('chosen-sprite.png') no-repeat -38px -20px, linear-gradient(top, #ffffff 85%,#eeeeee 99%);
+  padding: 4px 5px 4px 20px;
+}
+/* @end */
\ No newline at end of file
diff --git a/3rdparty/js/chosen/LICENSE.md b/3rdparty/js/chosen/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..80109bba80284211160f8c23f2c0d84cade96c34
--- /dev/null
+++ b/3rdparty/js/chosen/LICENSE.md
@@ -0,0 +1,24 @@
+# Chosen, a Select Box Enhancer for jQuery and Protoype
+## by Patrick Filler for [Harvest](http://getharvest.com)
+
+Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License)
+
+Copyright (c) 2011 by Harvest
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/3rdparty/js/chosen/README.md b/3rdparty/js/chosen/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..cee8ed1cc08839b0a8f05bd3547bf879fc190e7d
--- /dev/null
+++ b/3rdparty/js/chosen/README.md
@@ -0,0 +1,46 @@
+# Chosen
+
+Chosen is a library for making long, unwieldy select boxes more user friendly.
+
+- jQuery support: 1.4+
+- Prototype support: 1.7+
+
+For documentation, usage, and examples, see:  
+http://harvesthq.github.com/chosen
+
+### Contributing to Chosen
+
+Contributions and pull requests are very welcome. Please follow these guidelines when submitting new code.
+
+1. Make all changes in Coffeescript files, **not** JavaScript files.
+2. For feature changes, update both jQuery *and* Prototype versions
+3. Use 'cake build' to generate Chosen's JavaScript file and minified version.
+4. Don't touch the VERSION file
+5. Submit a Pull Request using GitHub.
+
+### Using CoffeeScript & Cake
+
+First, make sure you have the proper CoffeeScript / Cake set-up in place.
+
+1. Install Coffeescript: the [CoffeeScript documentation](http://jashkenas.github.com/coffee-script/) provides easy-to-follow instructions.
+2. Install UglifyJS: <code>npm -g install uglify-js</code>
+3. Verify that your $NODE_PATH is properly configured using <code>echo $NODE_PATH</code>
+
+Once you're configured, building the JavasScript from the command line is easy:
+
+    cake build                # build Chosen from source
+    cake watch                # watch coffee/ for changes and build Chosen
+    
+If you're interested, you can find the recipes in Cakefile.
+
+
+### Chosen Credits
+
+- Built by [Harvest](http://www.getharvest.com/)
+- Concept and development by [Patrick Filler](http://www.patrickfiller.com/)
+- Design and CSS by [Matthew Lettini](http://matthewlettini.com/)
+
+### Notable Forks
+
+- [Chosen for MooTools](https://github.com/julesjanssen/chosen), by Jules Janssen
+- [Chosen Drupal 7 Module](https://github.com/Polzme/chosen), by Pol Dell'Aiera
\ No newline at end of file
diff --git a/3rdparty/js/chosen/VERSION b/3rdparty/js/chosen/VERSION
new file mode 100644
index 0000000000000000000000000000000000000000..f374f6662e9a1983e9b8a534a3295df618772ffe
--- /dev/null
+++ b/3rdparty/js/chosen/VERSION
@@ -0,0 +1 @@
+0.9.1
diff --git a/3rdparty/js/chosen/chosen.jquery.js b/3rdparty/js/chosen/chosen.jquery.js
new file mode 100644
index 0000000000000000000000000000000000000000..5ea409d90e3f7fb594a545380bcc303533d304da
--- /dev/null
+++ b/3rdparty/js/chosen/chosen.jquery.js
@@ -0,0 +1,786 @@
+// Chosen, a Select Box Enhancer for jQuery and Protoype
+// by Patrick Filler for Harvest, http://getharvest.com
+// 
+// Version 0.9
+// Full source at https://github.com/harvesthq/chosen
+// Copyright (c) 2011 Harvest http://getharvest.com
+
+// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
+// This file is generated by `cake build`, do not edit it by hand.
+(function() {
+  /*
+  Chosen source: generate output using 'cake build'
+  Copyright (c) 2011 by Harvest
+  */  var $, Chosen, get_side_border_padding, root;
+  var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+  root = this;
+  $ = jQuery;
+  $.fn.extend({
+    chosen: function(data, options) {
+      return $(this).each(function(input_field) {
+        if (!($(this)).hasClass("chzn-done")) {
+          return new Chosen(this, data, options);
+        }
+      });
+    }
+  });
+  Chosen = (function() {
+    function Chosen(elmn) {
+      this.set_default_values();
+      this.form_field = elmn;
+      this.form_field_jq = $(this.form_field);
+      this.is_multiple = this.form_field.multiple;
+      this.is_rtl = this.form_field_jq.hasClass("chzn-rtl");
+      this.default_text_default = this.form_field.multiple ? "Select Some Options" : "Select an Option";
+      this.set_up_html();
+      this.register_observers();
+      this.form_field_jq.addClass("chzn-done");
+    }
+    Chosen.prototype.set_default_values = function() {
+      this.click_test_action = __bind(function(evt) {
+        return this.test_active_click(evt);
+      }, this);
+      this.active_field = false;
+      this.mouse_on_container = false;
+      this.results_showing = false;
+      this.result_highlighted = null;
+      this.result_single_selected = null;
+      return this.choices = 0;
+    };
+    Chosen.prototype.set_up_html = function() {
+      var container_div, dd_top, dd_width, sf_width;
+      this.container_id = this.form_field.id.length ? this.form_field.id.replace(/(:|\.)/g, '_') : this.generate_field_id();
+      this.container_id += "_chzn";
+      this.f_width = this.form_field_jq.width();
+      this.default_text = this.form_field_jq.data('placeholder') ? this.form_field_jq.data('placeholder') : this.default_text_default;
+      container_div = $("<div />", {
+        id: this.container_id,
+        "class": "chzn-container " + (this.is_rtl ? ' chzn-rtl' : void 0),
+        style: 'width: ' + this.f_width + 'px;'
+      });
+      if (this.is_multiple) {
+        container_div.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');
+      } else {
+        container_div.html('<a href="javascript:void(0)" class="chzn-single"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>');
+      }
+      this.form_field_jq.hide().after(container_div);
+      this.container = $('#' + this.container_id);
+      this.container.addClass("chzn-container-" + (this.is_multiple ? "multi" : "single"));
+      this.dropdown = this.container.find('div.chzn-drop').first();
+      dd_top = this.container.height();
+      dd_width = this.f_width - get_side_border_padding(this.dropdown);
+      this.dropdown.css({
+        "width": dd_width + "px",
+        "top": dd_top + "px"
+      });
+      this.search_field = this.container.find('input').first();
+      this.search_results = this.container.find('ul.chzn-results').first();
+      this.search_field_scale();
+      this.search_no_results = this.container.find('li.no-results').first();
+      if (this.is_multiple) {
+        this.search_choices = this.container.find('ul.chzn-choices').first();
+        this.search_container = this.container.find('li.search-field').first();
+      } else {
+        this.search_container = this.container.find('div.chzn-search').first();
+        this.selected_item = this.container.find('.chzn-single').first();
+        sf_width = dd_width - get_side_border_padding(this.search_container) - get_side_border_padding(this.search_field);
+        this.search_field.css({
+          "width": sf_width + "px"
+        });
+      }
+      this.results_build();
+      return this.set_tab_index();
+    };
+    Chosen.prototype.register_observers = function() {
+      this.container.click(__bind(function(evt) {
+        return this.container_click(evt);
+      }, this));
+      this.container.mouseenter(__bind(function(evt) {
+        return this.mouse_enter(evt);
+      }, this));
+      this.container.mouseleave(__bind(function(evt) {
+        return this.mouse_leave(evt);
+      }, this));
+      this.search_results.click(__bind(function(evt) {
+        return this.search_results_click(evt);
+      }, this));
+      this.search_results.mouseover(__bind(function(evt) {
+        return this.search_results_mouseover(evt);
+      }, this));
+      this.search_results.mouseout(__bind(function(evt) {
+        return this.search_results_mouseout(evt);
+      }, this));
+      this.form_field_jq.bind("liszt:updated", __bind(function(evt) {
+        return this.results_update_field(evt);
+      }, this));
+      this.search_field.blur(__bind(function(evt) {
+        return this.input_blur(evt);
+      }, this));
+      this.search_field.keyup(__bind(function(evt) {
+        return this.keyup_checker(evt);
+      }, this));
+      this.search_field.keydown(__bind(function(evt) {
+        return this.keydown_checker(evt);
+      }, this));
+      if (this.is_multiple) {
+        this.search_choices.click(__bind(function(evt) {
+          return this.choices_click(evt);
+        }, this));
+        return this.search_field.focus(__bind(function(evt) {
+          return this.input_focus(evt);
+        }, this));
+      } else {
+        return this.selected_item.focus(__bind(function(evt) {
+          return this.activate_field(evt);
+        }, this));
+      }
+    };
+    Chosen.prototype.container_click = function(evt) {
+      if (evt && evt.type === "click") {
+        evt.stopPropagation();
+      }
+      if (!this.pending_destroy_click) {
+        if (!this.active_field) {
+          if (this.is_multiple) {
+            this.search_field.val("");
+          }
+          $(document).click(this.click_test_action);
+          this.results_show();
+        } else if (!this.is_multiple && evt && ($(evt.target) === this.selected_item || $(evt.target).parents("a.chzn-single").length)) {
+          evt.preventDefault();
+          this.results_toggle();
+        }
+        return this.activate_field();
+      } else {
+        return this.pending_destroy_click = false;
+      }
+    };
+    Chosen.prototype.mouse_enter = function() {
+      return this.mouse_on_container = true;
+    };
+    Chosen.prototype.mouse_leave = function() {
+      return this.mouse_on_container = false;
+    };
+    Chosen.prototype.input_focus = function(evt) {
+      if (!this.active_field) {
+        return setTimeout((__bind(function() {
+          return this.container_click();
+        }, this)), 50);
+      }
+    };
+    Chosen.prototype.input_blur = function(evt) {
+      if (!this.mouse_on_container) {
+        this.active_field = false;
+        return setTimeout((__bind(function() {
+          return this.blur_test();
+        }, this)), 100);
+      }
+    };
+    Chosen.prototype.blur_test = function(evt) {
+      if (!this.active_field && this.container.hasClass("chzn-container-active")) {
+        return this.close_field();
+      }
+    };
+    Chosen.prototype.close_field = function() {
+      $(document).unbind("click", this.click_test_action);
+      if (!this.is_multiple) {
+        this.selected_item.attr("tabindex", this.search_field.attr("tabindex"));
+        this.search_field.attr("tabindex", -1);
+      }
+      this.active_field = false;
+      this.results_hide();
+      this.container.removeClass("chzn-container-active");
+      this.winnow_results_clear();
+      this.clear_backstroke();
+      this.show_search_field_default();
+      return this.search_field_scale();
+    };
+    Chosen.prototype.activate_field = function() {
+      if (!this.is_multiple && !this.active_field) {
+        this.search_field.attr("tabindex", this.selected_item.attr("tabindex"));
+        this.selected_item.attr("tabindex", -1);
+      }
+      this.container.addClass("chzn-container-active");
+      this.active_field = true;
+      this.search_field.val(this.search_field.val());
+      return this.search_field.focus();
+    };
+    Chosen.prototype.test_active_click = function(evt) {
+      if ($(evt.target).parents('#' + this.container_id).length) {
+        return this.active_field = true;
+      } else {
+        return this.close_field();
+      }
+    };
+    Chosen.prototype.results_build = function() {
+      var content, data, startTime, _i, _len, _ref;
+      startTime = new Date();
+      this.parsing = true;
+      this.results_data = root.SelectParser.select_to_array(this.form_field);
+      if (this.is_multiple && this.choices > 0) {
+        this.search_choices.find("li.search-choice").remove();
+        this.choices = 0;
+      } else if (!this.is_multiple) {
+        this.selected_item.find("span").text(this.default_text);
+      }
+      content = '';
+      _ref = this.results_data;
+      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+        data = _ref[_i];
+        if (data.group) {
+          content += this.result_add_group(data);
+        } else if (!data.empty) {
+          content += this.result_add_option(data);
+          if (data.selected && this.is_multiple) {
+            this.choice_build(data);
+          } else if (data.selected && !this.is_multiple) {
+            this.selected_item.find("span").text(data.text);
+          }
+        }
+      }
+      this.show_search_field_default();
+      this.search_field_scale();
+      this.search_results.html(content);
+      return this.parsing = false;
+    };
+    Chosen.prototype.result_add_group = function(group) {
+      if (!group.disabled) {
+        group.dom_id = this.container_id + "_g_" + group.array_index;
+        return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>';
+      } else {
+        return "";
+      }
+    };
+    Chosen.prototype.result_add_option = function(option) {
+      var classes;
+      if (!option.disabled) {
+        option.dom_id = this.container_id + "_o_" + option.array_index;
+        classes = option.selected && this.is_multiple ? [] : ["active-result"];
+        if (option.selected) {
+          classes.push("result-selected");
+        }
+        if (option.group_array_index != null) {
+          classes.push("group-option");
+        }
+        return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '">' + option.html + '</li>';
+      } else {
+        return "";
+      }
+    };
+    Chosen.prototype.results_update_field = function() {
+      this.result_clear_highlight();
+      this.result_single_selected = null;
+      return this.results_build();
+    };
+    Chosen.prototype.result_do_highlight = function(el) {
+      var high_bottom, high_top, maxHeight, visible_bottom, visible_top;
+      if (el.length) {
+        this.result_clear_highlight();
+        this.result_highlight = el;
+        this.result_highlight.addClass("highlighted");
+        maxHeight = parseInt(this.search_results.css("maxHeight"), 10);
+        visible_top = this.search_results.scrollTop();
+        visible_bottom = maxHeight + visible_top;
+        high_top = this.result_highlight.position().top + this.search_results.scrollTop();
+        high_bottom = high_top + this.result_highlight.outerHeight();
+        if (high_bottom >= visible_bottom) {
+          return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0);
+        } else if (high_top < visible_top) {
+          return this.search_results.scrollTop(high_top);
+        }
+      }
+    };
+    Chosen.prototype.result_clear_highlight = function() {
+      if (this.result_highlight) {
+        this.result_highlight.removeClass("highlighted");
+      }
+      return this.result_highlight = null;
+    };
+    Chosen.prototype.results_toggle = function() {
+      if (this.results_showing) {
+        return this.results_hide();
+      } else {
+        return this.results_show();
+      }
+    };
+    Chosen.prototype.results_show = function() {
+      var dd_top;
+      if (!this.is_multiple) {
+        this.selected_item.addClass("chzn-single-with-drop");
+        if (this.result_single_selected) {
+          this.result_do_highlight(this.result_single_selected);
+        }
+      }
+      dd_top = this.is_multiple ? this.container.height() : this.container.height() - 1;
+      this.dropdown.css({
+        "top": dd_top + "px",
+        "left": 0
+      });
+      this.results_showing = true;
+      this.search_field.focus();
+      this.search_field.val(this.search_field.val());
+      return this.winnow_results();
+    };
+    Chosen.prototype.results_hide = function() {
+      if (!this.is_multiple) {
+        this.selected_item.removeClass("chzn-single-with-drop");
+      }
+      this.result_clear_highlight();
+      this.dropdown.css({
+        "left": "-9000px"
+      });
+      return this.results_showing = false;
+    };
+    Chosen.prototype.set_tab_index = function(el) {
+      var ti;
+      if (this.form_field_jq.attr("tabindex")) {
+        ti = this.form_field_jq.attr("tabindex");
+        this.form_field_jq.attr("tabindex", -1);
+        if (this.is_multiple) {
+          return this.search_field.attr("tabindex", ti);
+        } else {
+          this.selected_item.attr("tabindex", ti);
+          return this.search_field.attr("tabindex", -1);
+        }
+      }
+    };
+    Chosen.prototype.show_search_field_default = function() {
+      if (this.is_multiple && this.choices < 1 && !this.active_field) {
+        this.search_field.val(this.default_text);
+        return this.search_field.addClass("default");
+      } else {
+        this.search_field.val("");
+        return this.search_field.removeClass("default");
+      }
+    };
+    Chosen.prototype.search_results_click = function(evt) {
+      var target;
+      target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
+      if (target.length) {
+        this.result_highlight = target;
+        return this.result_select();
+      }
+    };
+    Chosen.prototype.search_results_mouseover = function(evt) {
+      var target;
+      target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first();
+      if (target) {
+        return this.result_do_highlight(target);
+      }
+    };
+    Chosen.prototype.search_results_mouseout = function(evt) {
+      if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) {
+        return this.result_clear_highlight();
+      }
+    };
+    Chosen.prototype.choices_click = function(evt) {
+      evt.preventDefault();
+      if (this.active_field && !($(evt.target).hasClass("search-choice" || $(evt.target).parents('.search-choice').first)) && !this.results_showing) {
+        return this.results_show();
+      }
+    };
+    Chosen.prototype.choice_build = function(item) {
+      var choice_id, link;
+      choice_id = this.container_id + "_c_" + item.array_index;
+      this.choices += 1;
+      this.search_container.before('<li class="search-choice" id="' + choice_id + '"><span>' + item.html + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>');
+      link = $('#' + choice_id).find("a").first();
+      return link.click(__bind(function(evt) {
+        return this.choice_destroy_link_click(evt);
+      }, this));
+    };
+    Chosen.prototype.choice_destroy_link_click = function(evt) {
+      evt.preventDefault();
+      this.pending_destroy_click = true;
+      return this.choice_destroy($(evt.target));
+    };
+    Chosen.prototype.choice_destroy = function(link) {
+      this.choices -= 1;
+      this.show_search_field_default();
+      if (this.is_multiple && this.choices > 0 && this.search_field.val().length < 1) {
+        this.results_hide();
+      }
+      this.result_deselect(link.attr("rel"));
+      return link.parents('li').first().remove();
+    };
+    Chosen.prototype.result_select = function() {
+      var high, high_id, item, position;
+      if (this.result_highlight) {
+        high = this.result_highlight;
+        high_id = high.attr("id");
+        this.result_clear_highlight();
+        high.addClass("result-selected");
+        if (this.is_multiple) {
+          this.result_deactivate(high);
+        } else {
+          this.result_single_selected = high;
+        }
+        position = high_id.substr(high_id.lastIndexOf("_") + 1);
+        item = this.results_data[position];
+        item.selected = true;
+        this.form_field.options[item.options_index].selected = true;
+        if (this.is_multiple) {
+          this.choice_build(item);
+        } else {
+          this.selected_item.find("span").first().text(item.text);
+        }
+        this.results_hide();
+        this.search_field.val("");
+        this.form_field_jq.trigger("change");
+        return this.search_field_scale();
+      }
+    };
+    Chosen.prototype.result_activate = function(el) {
+      return el.addClass("active-result").show();
+    };
+    Chosen.prototype.result_deactivate = function(el) {
+      return el.removeClass("active-result").hide();
+    };
+    Chosen.prototype.result_deselect = function(pos) {
+      var result, result_data;
+      result_data = this.results_data[pos];
+      result_data.selected = false;
+      this.form_field.options[result_data.options_index].selected = false;
+      result = $("#" + this.container_id + "_o_" + pos);
+      result.removeClass("result-selected").addClass("active-result").show();
+      this.result_clear_highlight();
+      this.winnow_results();
+      this.form_field_jq.trigger("change");
+      return this.search_field_scale();
+    };
+    Chosen.prototype.results_search = function(evt) {
+      if (this.results_showing) {
+        return this.winnow_results();
+      } else {
+        return this.results_show();
+      }
+    };
+    Chosen.prototype.winnow_results = function() {
+      var found, option, part, parts, regex, result_id, results, searchText, startTime, startpos, text, zregex, _i, _j, _len, _len2, _ref;
+      startTime = new Date();
+      this.no_results_clear();
+      results = 0;
+      searchText = this.search_field.val() === this.default_text ? "" : $('<div/>').text($.trim(this.search_field.val())).html();
+      regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
+      zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i');
+      _ref = this.results_data;
+      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+        option = _ref[_i];
+        if (!option.disabled && !option.empty) {
+          if (option.group) {
+            $('#' + option.dom_id).hide();
+          } else if (!(this.is_multiple && option.selected)) {
+            found = false;
+            result_id = option.dom_id;
+            if (regex.test(option.html)) {
+              found = true;
+              results += 1;
+            } else if (option.html.indexOf(" ") >= 0 || option.html.indexOf("[") === 0) {
+              parts = option.html.replace(/\[|\]/g, "").split(" ");
+              if (parts.length) {
+                for (_j = 0, _len2 = parts.length; _j < _len2; _j++) {
+                  part = parts[_j];
+                  if (regex.test(part)) {
+                    found = true;
+                    results += 1;
+                  }
+                }
+              }
+            }
+            if (found) {
+              if (searchText.length) {
+                startpos = option.html.search(zregex);
+                text = option.html.substr(0, startpos + searchText.length) + '</em>' + option.html.substr(startpos + searchText.length);
+                text = text.substr(0, startpos) + '<em>' + text.substr(startpos);
+              } else {
+                text = option.html;
+              }
+              if ($("#" + result_id).html !== text) {
+                $("#" + result_id).html(text);
+              }
+              this.result_activate($("#" + result_id));
+              if (option.group_array_index != null) {
+                $("#" + this.results_data[option.group_array_index].dom_id).show();
+              }
+            } else {
+              if (this.result_highlight && result_id === this.result_highlight.attr('id')) {
+                this.result_clear_highlight();
+              }
+              this.result_deactivate($("#" + result_id));
+            }
+          }
+        }
+      }
+      if (results < 1 && searchText.length) {
+        return this.no_results(searchText);
+      } else {
+        return this.winnow_results_set_highlight();
+      }
+    };
+    Chosen.prototype.winnow_results_clear = function() {
+      var li, lis, _i, _len, _results;
+      this.search_field.val("");
+      lis = this.search_results.find("li");
+      _results = [];
+      for (_i = 0, _len = lis.length; _i < _len; _i++) {
+        li = lis[_i];
+        li = $(li);
+        _results.push(li.hasClass("group-result") ? li.show() : !this.is_multiple || !li.hasClass("result-selected") ? this.result_activate(li) : void 0);
+      }
+      return _results;
+    };
+    Chosen.prototype.winnow_results_set_highlight = function() {
+      var do_high;
+      if (!this.result_highlight) {
+        do_high = this.search_results.find(".active-result").first();
+        if (do_high) {
+          return this.result_do_highlight(do_high);
+        }
+      }
+    };
+    Chosen.prototype.no_results = function(terms) {
+      var no_results_html;
+      no_results_html = $('<li class="no-results">No results match "<span></span>"</li>');
+      no_results_html.find("span").first().html(terms);
+      return this.search_results.append(no_results_html);
+    };
+    Chosen.prototype.no_results_clear = function() {
+      return this.search_results.find(".no-results").remove();
+    };
+    Chosen.prototype.keydown_arrow = function() {
+      var first_active, next_sib;
+      if (!this.result_highlight) {
+        first_active = this.search_results.find("li.active-result").first();
+        if (first_active) {
+          this.result_do_highlight($(first_active));
+        }
+      } else if (this.results_showing) {
+        next_sib = this.result_highlight.nextAll("li.active-result").first();
+        if (next_sib) {
+          this.result_do_highlight(next_sib);
+        }
+      }
+      if (!this.results_showing) {
+        return this.results_show();
+      }
+    };
+    Chosen.prototype.keyup_arrow = function() {
+      var prev_sibs;
+      if (!this.results_showing && !this.is_multiple) {
+        return this.results_show();
+      } else if (this.result_highlight) {
+        prev_sibs = this.result_highlight.prevAll("li.active-result");
+        if (prev_sibs.length) {
+          return this.result_do_highlight(prev_sibs.first());
+        } else {
+          if (this.choices > 0) {
+            this.results_hide();
+          }
+          return this.result_clear_highlight();
+        }
+      }
+    };
+    Chosen.prototype.keydown_backstroke = function() {
+      if (this.pending_backstroke) {
+        this.choice_destroy(this.pending_backstroke.find("a").first());
+        return this.clear_backstroke();
+      } else {
+        this.pending_backstroke = this.search_container.siblings("li.search-choice").last();
+        return this.pending_backstroke.addClass("search-choice-focus");
+      }
+    };
+    Chosen.prototype.clear_backstroke = function() {
+      if (this.pending_backstroke) {
+        this.pending_backstroke.removeClass("search-choice-focus");
+      }
+      return this.pending_backstroke = null;
+    };
+    Chosen.prototype.keyup_checker = function(evt) {
+      var stroke, _ref;
+      stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
+      this.search_field_scale();
+      switch (stroke) {
+        case 8:
+          if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) {
+            return this.keydown_backstroke();
+          } else if (!this.pending_backstroke) {
+            this.result_clear_highlight();
+            return this.results_search();
+          }
+          break;
+        case 13:
+          evt.preventDefault();
+          if (this.results_showing) {
+            return this.result_select();
+          }
+          break;
+        case 27:
+          if (this.results_showing) {
+            return this.results_hide();
+          }
+          break;
+        case 9:
+        case 38:
+        case 40:
+        case 16:
+          break;
+        default:
+          return this.results_search();
+      }
+    };
+    Chosen.prototype.keydown_checker = function(evt) {
+      var stroke, _ref;
+      stroke = (_ref = evt.which) != null ? _ref : evt.keyCode;
+      this.search_field_scale();
+      if (stroke !== 8 && this.pending_backstroke) {
+        this.clear_backstroke();
+      }
+      switch (stroke) {
+        case 8:
+          this.backstroke_length = this.search_field.val().length;
+          break;
+        case 9:
+          this.mouse_on_container = false;
+          break;
+        case 13:
+          evt.preventDefault();
+          break;
+        case 38:
+          evt.preventDefault();
+          this.keyup_arrow();
+          break;
+        case 40:
+          this.keydown_arrow();
+          break;
+      }
+    };
+    Chosen.prototype.search_field_scale = function() {
+      var dd_top, div, h, style, style_block, styles, w, _i, _len;
+      if (this.is_multiple) {
+        h = 0;
+        w = 0;
+        style_block = "position:absolute; left: -1000px; top: -1000px; display:none;";
+        styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'];
+        for (_i = 0, _len = styles.length; _i < _len; _i++) {
+          style = styles[_i];
+          style_block += style + ":" + this.search_field.css(style) + ";";
+        }
+        div = $('<div />', {
+          'style': style_block
+        });
+        div.text(this.search_field.val());
+        $('body').append(div);
+        w = div.width() + 25;
+        div.remove();
+        if (w > this.f_width - 10) {
+          w = this.f_width - 10;
+        }
+        this.search_field.css({
+          'width': w + 'px'
+        });
+        dd_top = this.container.height();
+        return this.dropdown.css({
+          "top": dd_top + "px"
+        });
+      }
+    };
+    Chosen.prototype.generate_field_id = function() {
+      var new_id;
+      new_id = this.generate_random_id();
+      this.form_field.id = new_id;
+      return new_id;
+    };
+    Chosen.prototype.generate_random_id = function() {
+      var string;
+      string = "sel" + this.generate_random_char() + this.generate_random_char() + this.generate_random_char();
+      while ($("#" + string).length > 0) {
+        string += this.generate_random_char();
+      }
+      return string;
+    };
+    Chosen.prototype.generate_random_char = function() {
+      var chars, newchar, rand;
+      chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ";
+      rand = Math.floor(Math.random() * chars.length);
+      return newchar = chars.substring(rand, rand + 1);
+    };
+    return Chosen;
+  })();
+  get_side_border_padding = function(elmt) {
+    var side_border_padding;
+    return side_border_padding = elmt.outerWidth() - elmt.width();
+  };
+  root.get_side_border_padding = get_side_border_padding;
+}).call(this);
+(function() {
+  var SelectParser;
+  SelectParser = (function() {
+    function SelectParser() {
+      this.options_index = 0;
+      this.parsed = [];
+    }
+    SelectParser.prototype.add_node = function(child) {
+      if (child.nodeName === "OPTGROUP") {
+        return this.add_group(child);
+      } else {
+        return this.add_option(child);
+      }
+    };
+    SelectParser.prototype.add_group = function(group) {
+      var group_position, option, _i, _len, _ref, _results;
+      group_position = this.parsed.length;
+      this.parsed.push({
+        array_index: group_position,
+        group: true,
+        label: group.label,
+        children: 0,
+        disabled: group.disabled
+      });
+      _ref = group.childNodes;
+      _results = [];
+      for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+        option = _ref[_i];
+        _results.push(this.add_option(option, group_position, group.disabled));
+      }
+      return _results;
+    };
+    SelectParser.prototype.add_option = function(option, group_position, group_disabled) {
+      if (option.nodeName === "OPTION") {
+        if (option.text !== "") {
+          if (group_position != null) {
+            this.parsed[group_position].children += 1;
+          }
+          this.parsed.push({
+            array_index: this.parsed.length,
+            options_index: this.options_index,
+            value: option.value,
+            text: option.text,
+            html: option.innerHTML,
+            selected: option.selected,
+            disabled: group_disabled === true ? group_disabled : option.disabled,
+            group_array_index: group_position
+          });
+        } else {
+          this.parsed.push({
+            array_index: this.parsed.length,
+            options_index: this.options_index,
+            empty: true
+          });
+        }
+        return this.options_index += 1;
+      }
+    };
+    return SelectParser;
+  })();
+  SelectParser.select_to_array = function(select) {
+    var child, parser, _i, _len, _ref;
+    parser = new SelectParser();
+    _ref = select.childNodes;
+    for (_i = 0, _len = _ref.length; _i < _len; _i++) {
+      child = _ref[_i];
+      parser.add_node(child);
+    }
+    return parser.parsed;
+  };
+  this.SelectParser = SelectParser;
+}).call(this);
diff --git a/3rdparty/js/chosen/chosen.jquery.min.js b/3rdparty/js/chosen/chosen.jquery.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..1d6a6983d8a315e37e91538e4be880f0fdd79f0e
--- /dev/null
+++ b/3rdparty/js/chosen/chosen.jquery.min.js
@@ -0,0 +1,10 @@
+// Chosen, a Select Box Enhancer for jQuery and Protoype
+// by Patrick Filler for Harvest, http://getharvest.com
+// 
+// Version 0.9
+// Full source at https://github.com/harvesthq/chosen
+// Copyright (c) 2011 Harvest http://getharvest.com
+
+// MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md
+// This file is generated by `cake build`, do not edit it by hand.
+(function(){var a,b,c,d,e=function(a,b){return function(){return a.apply(b,arguments)}};d=this,a=jQuery,a.fn.extend({chosen:function(c,d){return a(this).each(function(e){if(!a(this).hasClass("chzn-done"))return new b(this,c,d)})}}),b=function(){function b(b){this.set_default_values(),this.form_field=b,this.form_field_jq=a(this.form_field),this.is_multiple=this.form_field.multiple,this.is_rtl=this.form_field_jq.hasClass("chzn-rtl"),this.default_text_default=this.form_field.multiple?"Select Some Options":"Select an Option",this.set_up_html(),this.register_observers(),this.form_field_jq.addClass("chzn-done")}b.prototype.set_default_values=function(){this.click_test_action=e(function(a){return this.test_active_click(a)},this),this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.result_single_selected=null;return this.choices=0},b.prototype.set_up_html=function(){var b,d,e,f;this.container_id=this.form_field.id.length?this.form_field.id.replace(/(:|\.)/g,"_"):this.generate_field_id(),this.container_id+="_chzn",this.f_width=this.form_field_jq.width(),this.default_text=this.form_field_jq.data("placeholder")?this.form_field_jq.data("placeholder"):this.default_text_default,b=a("<div />",{id:this.container_id,"class":"chzn-container "+(this.is_rtl?" chzn-rtl":void 0),style:"width: "+this.f_width+"px;"}),this.is_multiple?b.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'):b.html('<a href="javascript:void(0)" class="chzn-single"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" autocomplete="off" /></div><ul class="chzn-results"></ul></div>'),this.form_field_jq.hide().after(b),this.container=a("#"+this.container_id),this.container.addClass("chzn-container-"+(this.is_multiple?"multi":"single")),this.dropdown=this.container.find("div.chzn-drop").first(),d=this.container.height(),e=this.f_width-c(this.dropdown),this.dropdown.css({width:e+"px",top:d+"px"}),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chzn-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chzn-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chzn-search").first(),this.selected_item=this.container.find(".chzn-single").first(),f=e-c(this.search_container)-c(this.search_field),this.search_field.css({width:f+"px"})),this.results_build();return this.set_tab_index()},b.prototype.register_observers=function(){this.container.click(e(function(a){return this.container_click(a)},this)),this.container.mouseenter(e(function(a){return this.mouse_enter(a)},this)),this.container.mouseleave(e(function(a){return this.mouse_leave(a)},this)),this.search_results.click(e(function(a){return this.search_results_click(a)},this)),this.search_results.mouseover(e(function(a){return this.search_results_mouseover(a)},this)),this.search_results.mouseout(e(function(a){return this.search_results_mouseout(a)},this)),this.form_field_jq.bind("liszt:updated",e(function(a){return this.results_update_field(a)},this)),this.search_field.blur(e(function(a){return this.input_blur(a)},this)),this.search_field.keyup(e(function(a){return this.keyup_checker(a)},this)),this.search_field.keydown(e(function(a){return this.keydown_checker(a)},this));if(this.is_multiple){this.search_choices.click(e(function(a){return this.choices_click(a)},this));return this.search_field.focus(e(function(a){return this.input_focus(a)},this))}return this.selected_item.focus(e(function(a){return this.activate_field(a)},this))},b.prototype.container_click=function(b){b&&b.type==="click"&&b.stopPropagation();if(!this.pending_destroy_click){this.active_field?!this.is_multiple&&b&&(a(b.target)===this.selected_item||a(b.target).parents("a.chzn-single").length)&&(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(document).click(this.click_test_action),this.results_show());return this.activate_field()}return this.pending_destroy_click=!1},b.prototype.mouse_enter=function(){return this.mouse_on_container=!0},b.prototype.mouse_leave=function(){return this.mouse_on_container=!1},b.prototype.input_focus=function(a){if(!this.active_field)return setTimeout(e(function(){return this.container_click()},this),50)},b.prototype.input_blur=function(a){if(!this.mouse_on_container){this.active_field=!1;return setTimeout(e(function(){return this.blur_test()},this),100)}},b.prototype.blur_test=function(a){if(!this.active_field&&this.container.hasClass("chzn-container-active"))return this.close_field()},b.prototype.close_field=function(){a(document).unbind("click",this.click_test_action),this.is_multiple||(this.selected_item.attr("tabindex",this.search_field.attr("tabindex")),this.search_field.attr("tabindex",-1)),this.active_field=!1,this.results_hide(),this.container.removeClass("chzn-container-active"),this.winnow_results_clear(),this.clear_backstroke(),this.show_search_field_default();return this.search_field_scale()},b.prototype.activate_field=function(){!this.is_multiple&&!this.active_field&&(this.search_field.attr("tabindex",this.selected_item.attr("tabindex")),this.selected_item.attr("tabindex",-1)),this.container.addClass("chzn-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val());return this.search_field.focus()},b.prototype.test_active_click=function(b){return a(b.target).parents("#"+this.container_id).length?this.active_field=!0:this.close_field()},b.prototype.results_build=function(){var a,b,c,e,f,g;c=new Date,this.parsing=!0,this.results_data=d.SelectParser.select_to_array(this.form_field),this.is_multiple&&this.choices>0?(this.search_choices.find("li.search-choice").remove(),this.choices=0):this.is_multiple||this.selected_item.find("span").text(this.default_text),a="",g=this.results_data;for(e=0,f=g.length;e<f;e++)b=g[e],b.group?a+=this.result_add_group(b):b.empty||(a+=this.result_add_option(b),b.selected&&this.is_multiple?this.choice_build(b):b.selected&&!this.is_multiple&&this.selected_item.find("span").text(b.text));this.show_search_field_default(),this.search_field_scale(),this.search_results.html(a);return this.parsing=!1},b.prototype.result_add_group=function(b){if(!b.disabled){b.dom_id=this.container_id+"_g_"+b.array_index;return'<li id="'+b.dom_id+'" class="group-result">'+a("<div />").text(b.label).html()+"</li>"}return""},b.prototype.result_add_option=function(a){var b;if(!a.disabled){a.dom_id=this.container_id+"_o_"+a.array_index,b=a.selected&&this.is_multiple?[]:["active-result"],a.selected&&b.push("result-selected"),a.group_array_index!=null&&b.push("group-option");return'<li id="'+a.dom_id+'" class="'+b.join(" ")+'">'+a.html+"</li>"}return""},b.prototype.results_update_field=function(){this.result_clear_highlight(),this.result_single_selected=null;return this.results_build()},b.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight();if(b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(c<f)return this.search_results.scrollTop(c)}},b.prototype.result_clear_highlight=function(){this.result_highlight&&this.result_highlight.removeClass("highlighted");return this.result_highlight=null},b.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},b.prototype.results_show=function(){var a;this.is_multiple||(this.selected_item.addClass("chzn-single-with-drop"),this.result_single_selected&&this.result_do_highlight(this.result_single_selected)),a=this.is_multiple?this.container.height():this.container.height()-1,this.dropdown.css({top:a+"px",left:0}),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val());return this.winnow_results()},b.prototype.results_hide=function(){this.is_multiple||this.selected_item.removeClass("chzn-single-with-drop"),this.result_clear_highlight(),this.dropdown.css({left:"-9000px"});return this.results_showing=!1},b.prototype.set_tab_index=function(a){var b;if(this.form_field_jq.attr("tabindex")){b=this.form_field_jq.attr("tabindex"),this.form_field_jq.attr("tabindex",-1);if(this.is_multiple)return this.search_field.attr("tabindex",b);this.selected_item.attr("tabindex",b);return this.search_field.attr("tabindex",-1)}},b.prototype.show_search_field_default=function(){if(this.is_multiple&&this.choices<1&&!this.active_field){this.search_field.val(this.default_text);return this.search_field.addClass("default")}this.search_field.val("");return this.search_field.removeClass("default")},b.prototype.search_results_click=function(b){var c;c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first();if(c.length){this.result_highlight=c;return this.result_select()}},b.prototype.search_results_mouseover=function(b){var c;c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first();if(c)return this.result_do_highlight(c)},b.prototype.search_results_mouseout=function(b){if(a(b.target).hasClass("active-result"))return this.result_clear_highlight()},b.prototype.choices_click=function(b){b.preventDefault();if(this.active_field&&!a(b.target).hasClass("search-choice")&&!this.results_showing)return this.results_show()},b.prototype.choice_build=function(b){var c,d;c=this.container_id+"_c_"+b.array_index,this.choices+=1,this.search_container.before('<li class="search-choice" id="'+c+'"><span>'+b.html+'</span><a href="javascript:void(0)" class="search-choice-close" rel="'+b.array_index+'"></a></li>'),d=a("#"+c).find("a").first();return d.click(e(function(a){return this.choice_destroy_link_click(a)},this))},b.prototype.choice_destroy_link_click=function(b){b.preventDefault(),this.pending_destroy_click=!0;return this.choice_destroy(a(b.target))},b.prototype.choice_destroy=function(a){this.choices-=1,this.show_search_field_default(),this.is_multiple&&this.choices>0&&this.search_field.val().length<1&&this.results_hide(),this.result_deselect(a.attr("rel"));return a.parents("li").first().remove()},b.prototype.result_select=function(){var a,b,c,d;if(this.result_highlight){a=this.result_highlight,b=a.attr("id"),this.result_clear_highlight(),a.addClass("result-selected"),this.is_multiple?this.result_deactivate(a):this.result_single_selected=a,d=b.substr(b.lastIndexOf("_")+1),c=this.results_data[d],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.is_multiple?this.choice_build(c):this.selected_item.find("span").first().text(c.text),this.results_hide(),this.search_field.val(""),this.form_field_jq.trigger("change");return this.search_field_scale()}},b.prototype.result_activate=function(a){return a.addClass("active-result").show()},b.prototype.result_deactivate=function(a){return a.removeClass("active-result").hide()},b.prototype.result_deselect=function(b){var c,d;d=this.results_data[b],d.selected=!1,this.form_field.options[d.options_index].selected=!1,c=a("#"+this.container_id+"_o_"+b),c.removeClass("result-selected").addClass("active-result").show(),this.result_clear_highlight(),this.winnow_results(),this.form_field_jq.trigger("change");return this.search_field_scale()},b.prototype.results_search=function(a){return this.results_showing?this.winnow_results():this.results_show()},b.prototype.winnow_results=function(){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r;j=new Date,this.no_results_clear(),h=0,i=this.search_field.val()===this.default_text?"":a("<div/>").text(a.trim(this.search_field.val())).html(),f=new RegExp("^"+i.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),m=new RegExp(i.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i"),r=this.results_data;for(n=0,p=r.length;n<p;n++){c=r[n];if(!c.disabled&&!c.empty)if(c.group)a("#"+c.dom_id).hide();else if(!this.is_multiple||!c.selected){b=!1,g=c.dom_id;if(f.test(c.html))b=!0,h+=1;else if(c.html.indexOf(" ")>=0||c.html.indexOf("[")===0){e=c.html.replace(/\[|\]/g,"").split(" ");if(e.length)for(o=0,q=e.length;o<q;o++)d=e[o],f.test(d)&&(b=!0,h+=1)}b?(i.length?(k=c.html.search(m),l=c.html.substr(0,k+i.length)+"</em>"+c.html.substr(k+i.length),l=l.substr(0,k)+"<em>"+l.substr(k)):l=c.html,a("#"+g).html!==l&&a("#"+g).html(l),this.result_activate(a("#"+g)),c.group_array_index!=null&&a("#"+this.results_data[c.group_array_index].dom_id).show()):(this.result_highlight&&g===this.result_highlight.attr("id")&&this.result_clear_highlight(),this.result_deactivate(a("#"+g)))}}return h<1&&i.length?this.no_results(i):this.winnow_results_set_highlight()},b.prototype.winnow_results_clear=function(){var b,c,d,e,f;this.search_field.val(""),c=this.search_results.find("li"),f=[];for(d=0,e=c.length;d<e;d++)b=c[d],b=a(b),f.push(b.hasClass("group-result")?b.show():!this.is_multiple||!b.hasClass("result-selected")?this.result_activate(b):void 0);return f},b.prototype.winnow_results_set_highlight=function(){var a;if(!this.result_highlight){a=this.search_results.find(".active-result").first();if(a)return this.result_do_highlight(a)}},b.prototype.no_results=function(b){var c;c=a('<li class="no-results">No results match "<span></span>"</li>'),c.find("span").first().html(b);return this.search_results.append(c)},b.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},b.prototype.keydown_arrow=function(){var b,c;this.result_highlight?this.results_showing&&(c=this.result_highlight.nextAll("li.active-result").first(),c&&this.result_do_highlight(c)):(b=this.search_results.find("li.active-result").first(),b&&this.result_do_highlight(a(b)));if(!this.results_showing)return this.results_show()},b.prototype.keyup_arrow=function(){var a;if(!this.results_showing&&!this.is_multiple)return this.results_show();if(this.result_highlight){a=this.result_highlight.prevAll("li.active-result");if(a.length)return this.result_do_highlight(a.first());this.choices>0&&this.results_hide();return this.result_clear_highlight()}},b.prototype.keydown_backstroke=function(){if(this.pending_backstroke){this.choice_destroy(this.pending_backstroke.find("a").first());return this.clear_backstroke()}this.pending_backstroke=this.search_container.siblings("li.search-choice").last();return this.pending_backstroke.addClass("search-choice-focus")},b.prototype.clear_backstroke=function(){this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus");return this.pending_backstroke=null},b.prototype.keyup_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale();switch(b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0)return this.keydown_backstroke();if(!this.pending_backstroke){this.result_clear_highlight();return this.results_search()}break;case 13:a.preventDefault();if(this.results_showing)return this.result_select();break;case 27:if(this.results_showing)return this.results_hide();break;case 9:case 38:case 40:case 16:break;default:return this.results_search()}},b.prototype.keydown_checker=function(a){var b,c;b=(c=a.which)!=null?c:a.keyCode,this.search_field_scale(),b!==8&&this.pending_backstroke&&this.clear_backstroke();switch(b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.mouse_on_container=!1;break;case 13:a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:this.keydown_arrow()}},b.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(i=0,j=g.length;i<j;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";c=a("<div />",{style:f}),c.text(this.search_field.val()),a("body").append(c),h=c.width()+25,c.remove(),h>this.f_width-10&&(h=this.f_width-10),this.search_field.css({width:h+"px"}),b=this.container.height();return this.dropdown.css({top:b+"px"})}},b.prototype.generate_field_id=function(){var a;a=this.generate_random_id(),this.form_field.id=a;return a},b.prototype.generate_random_id=function(){var b;b="sel"+this.generate_random_char()+this.generate_random_char()+this.generate_random_char();while(a("#"+b).length>0)b+=this.generate_random_char();return b},b.prototype.generate_random_char=function(){var a,b,c;a="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZ",c=Math.floor(Math.random()*a.length);return b=a.substring(c,c+1)};return b}(),c=function(a){var b;return b=a.outerWidth()-a.width()},d.get_side_border_padding=c}).call(this),function(){var a;a=function(){function a(){this.options_index=0,this.parsed=[]}a.prototype.add_node=function(a){return a.nodeName==="OPTGROUP"?this.add_group(a):this.add_option(a)},a.prototype.add_group=function(a){var b,c,d,e,f,g;b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:a.label,children:0,disabled:a.disabled}),f=a.childNodes,g=[];for(d=0,e=f.length;d<e;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},a.prototype.add_option=function(a,b,c){if(a.nodeName==="OPTION"){a.text!==""?(b!=null&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0});return this.options_index+=1}};return a}(),a.select_to_array=function(b){var c,d,e,f,g;d=new a,g=b.childNodes;for(e=0,f=g.length;e<f;e++)c=g[e],d.add_node(c);return d.parsed},this.SelectParser=a}.call(this)
\ No newline at end of file
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000000000000000000000000000000000000..02439884d2f2937936e044d4926dc0d2d71b2dcc
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,14 @@
+ownCloud is written by:
+	Frank Karlitschek
+	Robin Appelman
+	Jakob Sack
+	Jan-Christoph Borchardt
+	Michael Gapczynski
+	Arthur Schiwon
+	…
+
+With help from many libraries and frameworks including:
+	Open Collaboration Services
+	SabreDAV
+	jQuery
+	…
diff --git a/COPYING-AGPL b/COPYING-AGPL
new file mode 100644
index 0000000000000000000000000000000000000000..dba13ed2ddf783ee8118c6a581dbf75305f816a3
--- /dev/null
+++ b/COPYING-AGPL
@@ -0,0 +1,661 @@
+                    GNU AFFERO GENERAL PUBLIC LICENSE
+                       Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+  A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate.  Many developers of free software are heartened and
+encouraged by the resulting cooperation.  However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+  The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community.  It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server.  Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+  An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals.  This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU Affero General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Remote Network Interaction; Use with the GNU General Public License.
+
+  Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software.  This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time.  Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU Affero General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU Affero General Public License for more details.
+
+    You should have received a copy of the GNU Affero General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source.  For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code.  There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+<http://www.gnu.org/licenses/>.
diff --git a/COPYING-README b/COPYING-README
new file mode 100644
index 0000000000000000000000000000000000000000..5f00323b7196d7c1e9b1485ef882d93fcc28824d
--- /dev/null
+++ b/COPYING-README
@@ -0,0 +1,9 @@
+Files in ownCloud are licensed under the Affero General Public License version 3,
+the text of which can be found in COPYING-AGPL, or any later version of the AGPL,
+unless otherwise noted.
+
+Components of ownCloud, including jQuery, are licensed under the MIT/X11 license.
+All unmodified files from these and other sources retain their original copyright
+and license notices: see the relevant individual files.
+
+Attribution information for ownCloud is contained in the AUTHORS file.
diff --git a/README b/README
new file mode 100644
index 0000000000000000000000000000000000000000..18b817ad3461095db27b44df9683b28a5a7ef2af
--- /dev/null
+++ b/README
@@ -0,0 +1,12 @@
+ownCloud is a personal cloud which runs on your own server.
+It is alpha software in development and should be treated accordingly.
+
+http://owncloud.org
+
+Installation instructions: http://owncloud.org/index.php/Installation
+Source code: http://projects.kde.org/owncloud
+
+Mailing list: https://mail.kde.org/mailman/listinfo/owncloud
+IRC channel: http://webchat.freenode.net/?channels=#owncloud
+Diaspora: https://joindiaspora.com/u/owncloud
+Identi.ca: http://identi.ca/owncloud
diff --git a/apps/.gitkeep b/apps/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f
--- /dev/null
+++ b/apps/.gitkeep
@@ -0,0 +1 @@
+ 
diff --git a/apps/bookmarks/addBm.php b/apps/bookmarks/addBm.php
new file mode 100644
index 0000000000000000000000000000000000000000..2dab33afb22d4351aba9f1696023c5e84f30eb2e
--- /dev/null
+++ b/apps/bookmarks/addBm.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( 'Location: '.OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+require_once('bookmarksHelper.php');
+
+OC_App::setActiveNavigationEntry( 'bookmarks_index' );
+
+OC_Util::addScript('bookmarks','addBm');
+OC_Util::addStyle('bookmarks', 'bookmarks');
+
+$tmpl = new OC_Template( 'bookmarks', 'addBm', 'user' );
+
+$url = isset($_GET['url']) ? urldecode($_GET['url']) : '';
+$metadata = getURLMetadata($url);
+
+$tmpl->assign('URL', htmlentities($metadata['url']));
+$tmpl->assign('TITLE', htmlentities($metadata['title']));
+$tmpl->assign('DESCRIPTION', htmlentities($metadata['description']));
+
+$tmpl->printPage();
\ No newline at end of file
diff --git a/apps/bookmarks/ajax/addBookmark.php b/apps/bookmarks/ajax/addBookmark.php
new file mode 100644
index 0000000000000000000000000000000000000000..78913f7a13248a861e0658ba377b11f6f2ac0dcd
--- /dev/null
+++ b/apps/bookmarks/ajax/addBookmark.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS=true;
+
+require_once('../../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
+if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
+	$_ut = "strftime('%s','now')";
+} else {
+	$_ut = "UNIX_TIMESTAMP()";
+}
+
+//FIXME: Detect when user adds a known URL
+$query = OC_DB::prepare("
+	INSERT INTO *PREFIX*bookmarks
+	(url, title, description, user_id, public, added, lastmodified)
+	VALUES (?, ?, ?, ?, 0, $_ut, $_ut)
+	");
+	
+	
+$params=array(
+	htmlspecialchars_decode($_GET["url"]),
+	htmlspecialchars_decode($_GET["title"]),
+	htmlspecialchars_decode($_GET["description"]),
+	OC_User::getUser()
+	);
+$query->execute($params);
+$b_id = OC_DB::insertid();
+
+if($b_id !== false) {
+	$query = OC_DB::prepare("
+		INSERT INTO *PREFIX*bookmarks_tags
+		(bookmark_id, tag)
+		VALUES (?, ?)
+		");
+		
+	$tags = explode(' ', urldecode($_GET["tags"]));
+	foreach ($tags as $tag) {
+		if(empty($tag)) {
+			//avoid saving blankspaces
+			continue;
+		}
+		$params = array($b_id, trim($tag));
+	    $query->execute($params);
+	}
+}
+
diff --git a/apps/bookmarks/ajax/delBookmark.php b/apps/bookmarks/ajax/delBookmark.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf1611fe5c1fff4d6dda7ce6155fd53bfd8d3251
--- /dev/null
+++ b/apps/bookmarks/ajax/delBookmark.php
@@ -0,0 +1,67 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS=true;
+
+require_once('../../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$params=array(
+	htmlspecialchars_decode($_GET["url"]),
+	OC_User::getUser()
+	);
+
+$query = OC_DB::prepare("
+	SELECT id FROM *PREFIX*bookmarks 
+	WHERE url LIKE ?
+		AND user_id = ?
+	");
+
+$id = $query->execute($params)->fetchOne();
+
+$query = OC_DB::prepare("
+	DELETE FROM *PREFIX*bookmarks
+	WHERE id = $id
+	");
+	
+$result = $query->execute();
+
+
+$query = OC_DB::prepare("
+	DELETE FROM *PREFIX*bookmarks_tags
+	WHERE bookmark_id = $id
+	");
+	
+$result = $query->execute();
+// var_dump($params);
+
+echo json_encode( array( "status" => "success", "data" => array()));
diff --git a/apps/bookmarks/ajax/getMeta.php b/apps/bookmarks/ajax/getMeta.php
new file mode 100644
index 0000000000000000000000000000000000000000..e9fe0d684dcc8fdbc27b553eb911b4a78c351b2e
--- /dev/null
+++ b/apps/bookmarks/ajax/getMeta.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS=true;
+
+require_once('../../../lib/base.php');
+
+// We send json data
+header( 'Content-Type: application/jsonrequest' );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => 'Authentication error' )));
+	exit();
+}
+
+// $metadata = array();
+
+require '../bookmarksHelper.php';
+$metadata = getURLMetadata(htmlspecialchars_decode($_GET["url"]));
+
+
+echo json_encode( array( 'status' => 'success', 'data' => $metadata));
diff --git a/apps/bookmarks/ajax/recordClick.php b/apps/bookmarks/ajax/recordClick.php
new file mode 100644
index 0000000000000000000000000000000000000000..116daea8bbb593267025e5caa611f07a065dc3d8
--- /dev/null
+++ b/apps/bookmarks/ajax/recordClick.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS=true;
+
+require_once('../../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Content-Type: application/jsonrequest" );
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$query = OC_DB::prepare("
+	UPDATE *PREFIX*bookmarks
+	SET clickcount = clickcount + 1
+	WHERE user_id = ?
+		AND url LIKE ?
+	");
+	
+$params=array(OC_User::getUser(), htmlspecialchars_decode($_GET["url"]));
+$bookmarks = $query->execute($params);
+
+header( "HTTP/1.1 204 No Content" );
+
diff --git a/apps/bookmarks/ajax/updateList.php b/apps/bookmarks/ajax/updateList.php
new file mode 100644
index 0000000000000000000000000000000000000000..ceecc5b7efaa80c12857f93f1181a6614dc5f9b6
--- /dev/null
+++ b/apps/bookmarks/ajax/updateList.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//no apps or filesystem
+$RUNTIME_NOSETUPFS=true;
+
+require_once('../../../lib/base.php');
+
+// We send json data
+header( 'Content-Type: application/jsonrequest' );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => 'Authentication error' )));
+	exit();
+}
+
+$params=array(OC_User::getUser());
+$CONFIG_DBTYPE = OC_Config::getValue( 'dbtype', 'sqlite' );
+
+//Filter for tag?
+$filterTag = isset($_GET['tag']) ? '%' . htmlspecialchars_decode($_GET['tag']) . '%' : false;
+if($filterTag){
+	$sqlFilterTag = 'HAVING tags LIKE ?';
+	$params[] = $filterTag;
+} else {
+	$sqlFilterTag = '';
+}
+
+$offset = isset($_GET['page']) ? intval($_GET['page']) * 10 : 0;
+$params[] = $offset;
+
+$sort = isset($_GET['sort']) ? ($_GET['sort']) : 'bookmarks_sorting_recent';
+if($sort == 'bookmarks_sorting_clicks') {
+	$sqlSort = 'clickcount DESC';
+} else {
+	$sqlSort = 'id DESC';
+}
+
+if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
+	$_gc_separator = ', \' \'';
+} else {
+	$_gc_separator = 'SEPARATOR \' \'';
+}
+
+$query = OC_DB::prepare('
+	SELECT url, title, description, 
+	CASE WHEN *PREFIX*bookmarks.id = *PREFIX*bookmarks_tags.bookmark_id
+			THEN GROUP_CONCAT( tag ' .$_gc_separator. ' )
+			ELSE \' \'
+		END
+		AS tags
+	FROM *PREFIX*bookmarks, *PREFIX*bookmarks_tags 
+	WHERE (*PREFIX*bookmarks.id = *PREFIX*bookmarks_tags.bookmark_id 
+			OR *PREFIX*bookmarks.id NOT IN (
+				SELECT *PREFIX*bookmarks_tags.bookmark_id FROM *PREFIX*bookmarks_tags
+			)
+		)
+		AND *PREFIX*bookmarks.user_id = ?
+	GROUP BY url
+	'.$sqlFilterTag.'
+	ORDER BY *PREFIX*bookmarks.'.$sqlSort.' 
+	LIMIT ?,  10');
+	
+$bookmarks = $query->execute($params)->fetchAll();
+
+echo json_encode( array( 'status' => 'success', 'data' => $bookmarks));
diff --git a/apps/bookmarks/appinfo/app.php b/apps/bookmarks/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..33b7ba61449fd4e90091d4d294497c3607d4445f
--- /dev/null
+++ b/apps/bookmarks/appinfo/app.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+OC_App::register( array( 'order' => 70, 'id' => 'bookmark', 'name' => 'Bookmarks' ));
+
+OC_App::addNavigationEntry( array( 'id' => 'bookmarks_index', 'order' => 70, 'href' => OC_Helper::linkTo( 'bookmarks', 'index.php' ), 'icon' => OC_Helper::imagePath( 'bookmarks', 'bookmarks.png' ), 'name' => 'Bookmarks' ));
+
diff --git a/apps/bookmarks/appinfo/database.xml b/apps/bookmarks/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c30db8bd0c8ec8f461aa43d4e97145c0fce59f11
--- /dev/null
+++ b/apps/bookmarks/appinfo/database.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+	<name>*dbname*</name>
+	<create>true</create>
+	<overwrite>false</overwrite>
+	<charset>latin1</charset>
+	<table>
+		<name>*dbprefix*bookmarks</name>
+		<declaration>
+			<field>
+				<name>id</name>
+				<type>integer</type>
+				<autoincrement>1</autoincrement>
+				<default>0</default>
+				<notnull>true</notnull>
+				<length>4</length>
+			</field>
+			<field>
+				<name>url</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>4096</length>
+			</field>
+			<field>
+				<name>title</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>140</length>
+			</field>
+			<field>
+				<name>description</name>
+				<type>text</type>
+				<default></default>
+				<notnull>false</notnull>
+				<length>255</length>
+			</field>
+			<field>
+				<name>user_id</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+			<field>
+				<name>public</name>
+				<type>integer</type>
+				<default>0</default>
+				<length>1</length>
+			</field>
+			<field>
+				<name>added</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+			<field>
+				<name>lastmodified</name>
+				<type>integer</type>
+				<default></default>
+				<notnull>false</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+			<field>
+				<name>clickcount</name>
+				<type>integer</type>
+				<default>0</default>
+				<notnull>true</notnull>
+				<unsigned>true</unsigned>
+				<length>4</length>
+			</field>
+			
+			<index>
+				<name>id</name>
+				<unique>true</unique>
+				<field>
+					<name>id</name>
+					<sorting>descending</sorting>
+				</field>
+			</index>
+<!--			<index>
+				<name>url</name>
+				<unique>true</unique>
+				<field>
+					<name>url</name>
+					<sorting>ascending</sorting>
+				</field>
+			</index>-->
+		</declaration>
+	</table>
+	
+	<table>
+		<name>*dbprefix*bookmarks_tags</name>
+		<declaration>
+			<field>
+				<name>bookmark_id</name>
+				<type>integer</type>
+				<length>64</length>
+			</field>
+			
+			<field>
+				<name>tag</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>255</length>
+			</field>
+			<index>
+				<name>bookmark_tag</name>
+				<unique>true</unique>
+				<field>
+					<name>bookmark_id</name>
+					<sorting>ascending</sorting>
+				</field>
+				<field>
+					<name>tag</name>
+					<sorting>ascending</sorting>
+				</field>
+			</index>
+		</declaration>
+	</table>
+</database>
+			
\ No newline at end of file
diff --git a/apps/bookmarks/appinfo/info.xml b/apps/bookmarks/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..23aa6c219a9fb6be93bb66bb4813ebe64e69fb1d
--- /dev/null
+++ b/apps/bookmarks/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>bookmarks</id>
+	<name>Bookmarks</name>
+	<description>Bookmark manager for ownCloud</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Arthur Schiwon</author>
+	<require>2</require>
+</info>
\ No newline at end of file
diff --git a/apps/bookmarks/bookmarksHelper.php b/apps/bookmarks/bookmarksHelper.php
new file mode 100644
index 0000000000000000000000000000000000000000..aee941a27b95ea69e2526512bd612e127c9c9221
--- /dev/null
+++ b/apps/bookmarks/bookmarksHelper.php
@@ -0,0 +1,23 @@
+<?php
+
+function getURLMetadata($url) {
+	//allow only http(s) and (s)ftp
+	$protocols = '/^[hs]{0,1}[tf]{0,1}tp[s]{0,1}\:\/\//i';
+	//if not (allowed) protocol is given, assume http
+	if(preg_match($protocols, $url) == 0) {
+		$url = 'http://' . $url;
+	} 
+	$metadata['url'] = $url;
+
+	$page = file_get_contents($url);
+	@preg_match( "/<title>(.*)<\/title>/si", $page, $match );
+	$metadata['title'] = htmlspecialchars_decode(@$match[1]); 
+
+	$meta = get_meta_tags($url);
+
+	if(array_key_exists('description', $meta)) {
+		$metadata['description'] = $meta['description'];
+	}
+	
+	return $metadata;
+}
\ No newline at end of file
diff --git a/apps/bookmarks/css/bookmarks.css b/apps/bookmarks/css/bookmarks.css
new file mode 100644
index 0000000000000000000000000000000000000000..96559172448b9a8da9d41eb66587a13b3beed23f
--- /dev/null
+++ b/apps/bookmarks/css/bookmarks.css
@@ -0,0 +1,86 @@
+#content { overflow: auto; }
+
+.bookmarks_headline {
+	font-size: large;
+	font-weight: bold;
+	margin-left: 2em;
+	padding: 2.5ex 0.5ex;
+}
+
+.bookmarks_menu {
+	margin-left: 1.5em;
+	padding: 0.5ex;
+}
+
+.bookmark_actions {
+	font-size: smaller;
+	color: #ff44ff;
+	padding-left: 4em;
+}
+
+.bookmark_actions span:hover {
+	cursor: pointer;
+	text-decoration: underline;
+}
+
+.bookmarks_sorting {
+	float: left;
+	margin-left: 2em;
+}
+
+.bookmarks_sorting li {
+	padding: 1ex 1em;
+	border: 1px solid gray;
+	-moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em;
+}
+
+.bookmarks_sorting_active {
+	font-weight: bold;
+}
+
+.bookmarks_add {
+	display: none;
+}
+
+.bookmarks_addBml {
+	text-decoration: underline;
+}
+
+.bookmarks_label {
+	width: 7em;
+	display: inline-block;
+	text-align: right;
+}
+
+.bookmarks_input {
+	width: 20em;
+}
+
+.bookmark_single {
+	margin-left: 2em;
+	margin-top: 3ex;
+	padding: 0.5ex;
+/* 	border-bottom: 1px solid black; */
+}
+
+.bookmark_single:hover {
+	background-color: #ccccff;
+}
+
+.bookmark_title {
+	font-size: larger;
+	color: blue;
+	text-decoration: underline;
+}
+
+.bookmark_url {
+	color: green;
+}
+
+.bookmark_tags {
+	color: #ff3333;
+}
+
+.clear {
+	clear:both;
+}
diff --git a/apps/bookmarks/img/bookmarks.png b/apps/bookmarks/img/bookmarks.png
new file mode 100644
index 0000000000000000000000000000000000000000..b92e4f50a42c68293d256e21cb44d31debbbb840
Binary files /dev/null and b/apps/bookmarks/img/bookmarks.png differ
diff --git a/apps/bookmarks/index.php b/apps/bookmarks/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba9f7cc0c618b5495c9dcba5b5b469eb020c212a
--- /dev/null
+++ b/apps/bookmarks/index.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+* ownCloud - bookmarks plugin
+*
+* @author Arthur Schiwon
+* @copyright 2011 Arthur Schiwon blizzz@arthur-schiwon.de
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+OC_App::setActiveNavigationEntry( 'bookmarks_index' );
+
+OC_Util::addScript('bookmarks','bookmarks');
+OC_Util::addStyle('bookmarks', 'bookmarks');
+
+$tmpl = new OC_Template( 'bookmarks', 'list', 'user' );
+
+$tmpl->printPage();
\ No newline at end of file
diff --git a/apps/bookmarks/js/addBm.js b/apps/bookmarks/js/addBm.js
new file mode 100644
index 0000000000000000000000000000000000000000..7c914f2338af8274e740cd666e6e4ada96603c11
--- /dev/null
+++ b/apps/bookmarks/js/addBm.js
@@ -0,0 +1,17 @@
+$(document).ready(function() {
+	$('#bookmark_add_submit').click(addBookmark);
+});
+
+function addBookmark(event) {
+	var url = $('#bookmark_add_url').val();
+	var title = $('#bookmark_add_title').val();
+	var description = $('#bookmark_add_description').val();
+	var tags = $('#bookmark_add_tags').val();
+	$.ajax({
+		url: 'ajax/addBookmark.php',
+		data: 'url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&description=' + encodeURI(description) + '&tags=' + encodeURI(tags),
+		success: function(data){ 
+			location.href='index.php';
+		}
+	});
+}
\ No newline at end of file
diff --git a/apps/bookmarks/js/bookmarks.js b/apps/bookmarks/js/bookmarks.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac87d967be81e902273306b4a2d99ad12d00fa07
--- /dev/null
+++ b/apps/bookmarks/js/bookmarks.js
@@ -0,0 +1,155 @@
+var bookmarks_page = 0;
+var bookmarks_loading = false;
+
+var bookmarks_sorting = 'bookmarks_sorting_recent';
+
+$(document).ready(function() {
+	$('.bookmarks_addBtn').click(function(event){
+		$('.bookmarks_add').slideToggle();
+	});
+	
+	$('#bookmark_add_submit').click(addBookmark);
+	$(window).scroll(updateOnBottom);
+	
+	$('#bookmark_add_url').focusout(getMetadata);
+	$('.' + bookmarks_sorting).addClass('bookmarks_sorting_active');
+	
+	$('.bookmarks_sorting li').click(function(event){changeSorting(this)});
+	
+	$('.bookmarks_list').empty();
+	getBookmarks();
+});
+
+function getBookmarks() {
+	if(bookmarks_loading) {
+		//have patience :)
+		return;
+	}
+	
+	$.ajax({
+		url: 'ajax/updateList.php',
+		data: 'tag=' + encodeURI($('#bookmarkFilterTag').val()) + '&page=' + bookmarks_page + '&sort=' + bookmarks_sorting,
+		success: function(bookmarks){
+			bookmarks_page += 1;
+			$('.bookmark_link').unbind('click', recordClick);
+			$('.bookmark_delete').unbind('click', delBookmark);
+	
+			for(var i in bookmarks.data) {
+				updateBookmarksList(bookmarks.data[i]);
+			}
+			$('.bookmark_link').click(recordClick);
+			$('.bookmark_delete').click(delBookmark);
+			bookmarks_loading = false;
+		}
+	});	
+}
+
+function getMetadata() {
+	var url = encodeEntities($('#bookmark_add_url').val())
+	$.ajax({
+		url: 'ajax/getMeta.php',
+		data: 'url=' + encodeURIComponent(url),
+		success: function(pageinfo){
+			$('#bookmark_add_url').val(pageinfo.data.url);
+			$('#bookmark_add_description').val(pageinfo.data.description);
+			$('#bookmark_add_title').val(pageinfo.data.title);
+		}
+	});
+}
+
+function changeSorting(sortEl) {
+	$('.' + bookmarks_sorting).removeClass('bookmarks_sorting_active');
+	bookmarks_sorting = sortEl.className;
+	$('.' + bookmarks_sorting).addClass('bookmarks_sorting_active');
+	
+	$('.bookmarks_list').empty();
+	bookmarks_page = 0;
+	bookmarks_loading = false;
+	getBookmarks();
+}
+
+function addBookmark(event) {
+	var url = encodeEntities($('#bookmark_add_url').val())
+	var title = encodeEntities($('#bookmark_add_title').val())
+	var description = encodeEntities($('#bookmark_add_description').val())
+	var tags = encodeEntities($('#bookmark_add_tags').val())
+	var taglist = tags.split(' ')
+	var tagshtml = '';
+	for ( var i=0, len=taglist.length; i<len; ++i ){
+		tagshtml += '<a class="bookmark_tags" href="?tag=' + encodeURI(taglist[i]) + '">' + taglist[i] + '</a> ';
+	}
+	$.ajax({
+		url: 'ajax/addBookmark.php',
+		data: 'url=' + encodeURI(url) + '&title=' + encodeURI(title) + '&description=' + encodeURI(description) + '&tags=' + encodeURI(tags),
+		success: function(data){ 
+			$('.bookmarks_add').slideToggle(); 
+			$('.bookmarks_add').children('p').children('.bookmarks_input').val(''); 
+			$('.bookmarks_list').prepend(
+			'<div class="bookmark_single">' +
+				'<p class="bookmark_title"><a href="' + url + '" target="_new" class="bookmark_link">' + title + '</a></p>' +
+				'<p class="bookmark_url">' + url + '</p>' +
+				'<p class="bookmark_description">' + description + '</p>' +
+				'<p>' + tagshtml + '</p>' +
+				'<p class="bookmark_actions"><span class="bookmark_delete">Delete</span></p>' +
+			'</div>'
+			);
+		}
+	});
+}
+
+function delBookmark(event) {
+	var record = $(this).parent().parent()
+	$.ajax({
+		url: 'ajax/delBookmark.php',
+		data: 'url=' + encodeURI($(this).parent().parent().children('.bookmark_url:first').text()),
+		success: function(data){ record.animate({ opacity: 'hide' }, 'fast'); }
+	});
+}
+
+function updateBookmarksList(bookmark) {
+	var tags = encodeEntities(bookmark.tags).split(' ');
+	var taglist = '';
+	for ( var i=0, len=tags.length; i<len; ++i ){
+		taglist = taglist + '<a class="bookmark_tags" href="?tag=' + encodeURI(tags[i]) + '">' + tags[i] + '</a> ';
+	}
+	if(!hasProtocol(bookmark.url)) {
+		bookmark.url = 'http://' + bookmark.url;
+	}
+	$('.bookmarks_list').append(
+		'<div class="bookmark_single">' +
+			'<p class="bookmark_title"><a href="' + encodeEntities(bookmark.url) + '" target="_new" class="bookmark_link">' + encodeEntities(bookmark.title) + '</a></p>' +
+			'<p class="bookmark_url">' + encodeEntities(bookmark.url) + '</p>' +
+			'<p class="bookmark_description">' + encodeEntities(bookmark.description) + '</p>' +
+			'<p>' + taglist + '</p>' +
+			'<p class="bookmark_actions"><span class="bookmark_delete">Delete</span></p>' +
+		'</div>'
+	);
+}
+
+function updateOnBottom() {
+	//check wether user is on bottom of the page
+	if ($('body').height() <= ($(window).height() + $(window).scrollTop())) {
+		getBookmarks();
+	}
+}
+
+function recordClick(event) {
+	$.ajax({
+		url: 'ajax/recordClick.php',
+		data: 'url=' + encodeURI($(this).attr('href')),
+	});	
+}
+
+function encodeEntities(s){
+	try {
+		return $('<div/>').text(s).html();
+		
+	} catch (ex) {
+		return "";
+	}
+}
+
+function hasProtocol(url) {
+    var regexp = /(ftp|http|https|sftp)/;
+    return regexp.test(url);
+}
diff --git a/apps/bookmarks/templates/addBm.php b/apps/bookmarks/templates/addBm.php
new file mode 100644
index 0000000000000000000000000000000000000000..cbc4910e1aeb8bbdf8f312b0b5b9ed074c782e2c
--- /dev/null
+++ b/apps/bookmarks/templates/addBm.php
@@ -0,0 +1,8 @@
+<div class="bookmarks_addBm">
+	<p><label class="bookmarks_label">Address</label><input type="text" id="bookmark_add_url" class="bookmarks_input" value="<? echo $_['URL']; ?>"/></p>
+	<p><label class="bookmarks_label">Title</label><input type="text" id="bookmark_add_title" class="bookmarks_input" value="<? echo $_['TITLE']; ?>" /></p>
+	<p><label class="bookmarks_label">Description</label><input type="text" id="bookmark_add_description" class="bookmarks_input" value="<? echo $_['DESCRIPTION']; ?>" /></p>
+	<p><label class="bookmarks_label">Tags</label><input type="text" id="bookmark_add_tags" class="bookmarks_input" /></p>
+	<p><label class="bookmarks_label"> </label><label class="bookmarks_hint">Hint: Use space to separate tags.</label></p>
+	<p><label class="bookmarks_label"></label><input type="submit" id="bookmark_add_submit" /></p>
+</div>
\ No newline at end of file
diff --git a/apps/bookmarks/templates/list.php b/apps/bookmarks/templates/list.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f101d44f90d773a53df3e4a11b013668857a647
--- /dev/null
+++ b/apps/bookmarks/templates/list.php
@@ -0,0 +1,27 @@
+<input type="hidden" id="bookmarkFilterTag" value="<?php if(isset($_GET['tag'])) echo htmlentities($_GET['tag']); ?>" />
+<h2 class="bookmarks_headline"><?php echo isset($_GET["tag"]) ? 'Bookmarks with tag: ' . urldecode($_GET["tag"]) : 'All bookmarks'; ?></h2>
+<div class="bookmarks_menu">
+	<input type="button" class="bookmarks_addBtn" value="Add Bookmark"/>&nbsp;
+	<a class="bookmarks_addBml" href="javascript:var url = encodeURIComponent(location.href);window.open('<?php echo (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . OC_Helper::linkTo('bookmarks', 'addBm.php'); ?>?url='+url, 'owncloud-bookmarks');" title="Drag this to your browser bookmarks and click it, when you want to bookmark a webpage.">Add page to ownCloud</a>
+</div>
+<div class="bookmarks_add">
+	<p><label class="bookmarks_label">Address</label><input type="text" id="bookmark_add_url" class="bookmarks_input" /></p>
+	<p><label class="bookmarks_label">Title</label><input type="text" id="bookmark_add_title" class="bookmarks_input" /></p>
+	<p><label class="bookmarks_label">Description</label><input type="text" id="bookmark_add_description" class="bookmarks_input" /></p>
+	<p><label class="bookmarks_label">Tags</label><input type="text" id="bookmark_add_tags" class="bookmarks_input" /></p>
+	<p><label class="bookmarks_label"> </label><label class="bookmarks_hint">Hint: Use space to separate tags.</label></p>
+	<p><label class="bookmarks_label"></label><input type="submit" id="bookmark_add_submit" /></p>
+</div>
+<div class="bookmarks_sorting pager">
+	<ul>
+		<li class="bookmarks_sorting_recent">Recent Bookmarks</li>
+		<li class="bookmarks_sorting_clicks">Most clicks</li>
+	</ul>
+</div>
+<div class="clear"></div>
+<div class="bookmarks_list">
+	<noscript>
+	JavaScript is needed to display your Bookmarks
+	</noscript>
+	You have no bookmarks
+</div>
diff --git a/apps/calendar/ajax/activation.php b/apps/calendar/ajax/activation.php
new file mode 100755
index 0000000000000000000000000000000000000000..51c5777f643f49fb0cf58e7170e4577645018644
--- /dev/null
+++ b/apps/calendar/ajax/activation.php
@@ -0,0 +1,27 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once ("../../../lib/base.php");
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+$calendarid = $_POST['calendarid'];
+OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
+$cal = OC_Calendar_Calendar::findCalendar($calendarid);
+echo $cal['active'];
diff --git a/apps/calendar/ajax/changeview.php b/apps/calendar/ajax/changeview.php
new file mode 100755
index 0000000000000000000000000000000000000000..d3a00bf1728aa6d41e2dca9c751757d56cb1ecc1
--- /dev/null
+++ b/apps/calendar/ajax/changeview.php
@@ -0,0 +1,26 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once ("../../../lib/base.php");
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+$currentview = $_GET["v"];
+OC_Preferences::setValue(OC_USER::getUser(), "calendar", "currentview", $currentview);
+?>
\ No newline at end of file
diff --git a/apps/calendar/ajax/choosecalendar.php b/apps/calendar/ajax/choosecalendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..03765dabe9deb12156613f88e02ac51958d4177c
--- /dev/null
+++ b/apps/calendar/ajax/choosecalendar.php
@@ -0,0 +1,27 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once('../../../lib/base.php');
+$l10n = new OC_L10N('calendar');
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+$output = new OC_TEMPLATE("calendar", "part.choosecalendar");
+$output -> printpage();
+?>
\ No newline at end of file
diff --git a/apps/calendar/ajax/editcalendar.php b/apps/calendar/ajax/editcalendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..e32bf4586523edf2dc162fdec1f83d8ba72ffede
--- /dev/null
+++ b/apps/calendar/ajax/editcalendar.php
@@ -0,0 +1,29 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once('../../../lib/base.php');
+$l10n = new OC_L10N('calendar');
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+$calendar = OC_Calendar_Calendar::findCalendar($_GET['calendarid']);
+$tmpl = new OC_Template("calendar", "part.editcalendar");
+$tmpl->assign('calendar',$calendar);
+$tmpl->printPage();
+?>
diff --git a/apps/calendar/ajax/editevent.php b/apps/calendar/ajax/editevent.php
new file mode 100755
index 0000000000000000000000000000000000000000..6182a60e611ad4f08771320c1970d24bd3cfebf5
--- /dev/null
+++ b/apps/calendar/ajax/editevent.php
@@ -0,0 +1,22 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * <http://www.gnu.org/licenses/>                 *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+
+?> 
diff --git a/apps/calendar/ajax/editeventform.php b/apps/calendar/ajax/editeventform.php
new file mode 100755
index 0000000000000000000000000000000000000000..8d1c8b69c3fce7bea45c73efd06983e3c419a92f
--- /dev/null
+++ b/apps/calendar/ajax/editeventform.php
@@ -0,0 +1 @@
+ 
diff --git a/apps/calendar/ajax/getcal.php b/apps/calendar/ajax/getcal.php
new file mode 100755
index 0000000000000000000000000000000000000000..b20f22957c312a9c78e4b3a937270887eaf17e29
--- /dev/null
+++ b/apps/calendar/ajax/getcal.php
@@ -0,0 +1,26 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once ("../../../lib/base.php");
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+$output = new OC_TEMPLATE("calendar", "part.getcal");
+$output -> printpage();
+?>
diff --git a/apps/calendar/ajax/geteventinfo.php b/apps/calendar/ajax/geteventinfo.php
new file mode 100755
index 0000000000000000000000000000000000000000..6182a60e611ad4f08771320c1970d24bd3cfebf5
--- /dev/null
+++ b/apps/calendar/ajax/geteventinfo.php
@@ -0,0 +1,22 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * <http://www.gnu.org/licenses/>                 *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+
+?> 
diff --git a/apps/calendar/ajax/newevent.php b/apps/calendar/ajax/newevent.php
new file mode 100755
index 0000000000000000000000000000000000000000..f3b95aae4bac81a9ae306d7ceb85217d6d33abda
--- /dev/null
+++ b/apps/calendar/ajax/newevent.php
@@ -0,0 +1,157 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * <http://www.gnu.org/licenses/>                 *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once('../../../lib/base.php');
+$l10n = new OC_L10N('calendar');
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+//short variables
+$title = $_POST["title"];
+$location = $_POST["location"];
+$cat = $_POST["cat"];
+$cal = str_replace("option_","", $_POST["cal"]);
+$allday = $_POST["allday"];
+$from = $_POST["from"];
+$fromtime = $_POST["fromtime"];
+$to  = $_POST["to"];
+$totime = $_POST["totime"];
+$description = $_POST["description"];
+$repeat = $_POST["repeat"];
+/*switch($_POST["repeatfreq"]){
+	case "DAILY":
+		$repeatfreq = "DAILY";
+	case "WEEKLY":
+		$repeatfreq = "WEEKLY";
+	case "WEEKDAY":
+		$repeatfreq = "DAILY;BYDAY=MO,TU,WE,TH,FR"; //load weeksdayss from userconfig when weekdays are choosable
+	case "":
+		$repeatfreq = "";
+	case "":
+		$repeatfreq = "";
+	case "":
+		$repeatfreq = "";
+	default:
+		$repeat = "false";
+}*/
+$repeat = "false";
+//validate variables
+$errnum = 0;
+$errarr = array("title"=>"false", "cal"=>"false", "from"=>"false", "fromtime"=>"false", "to"=>"false", "totime"=>"false", "endbeforestart"=>"false");
+if($title == ""){
+	$errarr["title"] = "true";
+	$errnum++;
+}
+$calendar = OC_Calendar_Calendar::findCalendar($cal);
+if($calendar["userid"] != OC_User::getUser()){
+	$errarr["cal"] = "true";
+	$errnum++;
+}
+$fromday = substr($_POST["from"], 0, 2);
+$frommonth = substr($_POST["from"], 3, 2);
+$fromyear = substr($_POST["from"], 6, 4);
+if(!checkdate($frommonth, $fromday, $fromyear)){
+	$errarr["from"] = "true";
+	$errnum++;
+}
+$fromhours = substr($_POST["fromtime"], 0, 2);
+$fromminutes = substr($_POST["fromtime"], 3, 2);
+if($fromhours > 24 || $fromminutes > 60 || $fromtime == ""){
+	$errarr["fromtime"] = "true";
+	$errnum++;
+}
+
+$today = substr($_POST["to"], 0, 2);
+$tomonth = substr($_POST["to"], 3, 2);
+$toyear = substr($_POST["to"], 6, 4);
+if(!checkdate($tomonth, $today, $toyear)){
+	$errarr["to"] = "true";
+	$errnum++;
+}
+$tohours = substr($_POST["totime"], 0, 2);
+$tominutes = substr($_POST["totime"], 3, 2);
+if($tohours > 24 || $tominutes > 60 || $totime == ""){
+	$errarr["totime"] = "true";
+	$errnum++;
+}
+if($today < $fromday && $frommonth == $tomonth && $fromyear == $toyear){
+	$errarr["endbeforestart"] = "true";
+	$errnum++;
+}
+if($today == $fromday && $frommonth > $tomonth && $fromyear == $toyear){
+	$errarr["endbeforestart"] = "true";
+	$errnum++;
+}
+if($today == $fromday && $frommonth == $tomonth && $fromyear > $toyear){
+	$errarr["endbeforestart"] = "true";
+	$errnum++;
+}
+if($fromday == $today && $frommonth == $tomonth && $fromyear == $toyear){
+	if($tohours < $fromhours){
+		$errarr["endbeforestart"] = "true";
+		$errnum++;
+	}
+	if($tohours == $fromhours && $tominutes < $fromminutes){
+		$errarr["endbeforestart"] = "true";
+		$errnum++;
+	}
+}
+if($errnum != 0){
+	//show validate errors
+	$errarr["error"] = "true";
+	echo json_encode($errarr);
+	exit;
+}else{
+	$data = "BEGIN:VCALENDAR\nPRODID:ownCloud Calendar\nVERSION:2.0\n";
+	$timezone = OC_Preferences::getValue(OC_USER::getUser(), "calendar", "timezone", "Europe/London");
+	$created = date("Ymd") . "T" . date("His");
+	$data .= "BEGIN:VEVENT\n";
+	$data .= "CREATED:" . $created . "\nLAST-MODIFIED:" . $created . "\nDTSTAMP:" . $created . "\n";
+	$data .= "SUMMARY:" . $title . "\n";
+	if($allday == "true"){
+		$start = $fromyear . $frommonth . $fromday;
+		$unixend = mktime(0,0,0,$tomonth, $today, $toyear) + (24 * 60 * 60);
+		$end = date("Ymd", $unixend);
+		$data .= "DTSTART;VALUE=DATE:" . $start . "\n";
+		$data .= "DTEND;VALUE=DATE:" . $end . "\n";
+	}else{
+		$start = $fromyear . $frommonth . $fromday . "T" . $fromhours . $fromminutes . "00";
+		$end = $toyear . $tomonth . $today . "T" . $tohours . $tominutes . "00";
+		$data .= "DTSTART;TZID=" . $timezone . ":" . $start . "\n";
+		$data .= "DTEND;TZID=" . $timezone . ":" . $end . "\n";
+	}
+	if($location != ""){
+		$data .= "LOCATION:" . $location . "\n";
+	}
+	if($description != ""){
+		$des = str_replace("\n","\\n", $description);
+		$data .= "DESCRIPTION:" . $des . "\n";
+	}
+	if($cat != "none"){
+		$data .= "CATEGORIES:" . $cat . "\n";
+	}
+	if($repeat == "true"){
+		$data .= "RRULE:" . $repeat . "\n";
+	}
+	$data .= "END:VEVENT\nEND:VCALENDAR";
+	$result = OC_Calendar_Calendar::addCalendarObject($cal, $data);
+	echo json_encode(array("success"=>"true"));
+}
+?>
\ No newline at end of file
diff --git a/apps/calendar/ajax/neweventform.php b/apps/calendar/ajax/neweventform.php
new file mode 100755
index 0000000000000000000000000000000000000000..b1cb6d5b8160357c23c78101a26f177059fba041
--- /dev/null
+++ b/apps/calendar/ajax/neweventform.php
@@ -0,0 +1,27 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once('../../../lib/base.php');
+$l10n = new OC_L10N('calendar');
+if(!OC_USER::isLoggedIn()) {
+	die("<script type=\"text/javascript\">document.location = oc_webroot;</script>");
+}
+$output = new OC_TEMPLATE("calendar", "part.newevent");
+$output -> printpage();
+?>
\ No newline at end of file
diff --git a/apps/calendar/ajax/settimezone.php b/apps/calendar/ajax/settimezone.php
new file mode 100755
index 0000000000000000000000000000000000000000..62e171c66b9bae983368cc9c6e1bf88fb239c99a
--- /dev/null
+++ b/apps/calendar/ajax/settimezone.php
@@ -0,0 +1,26 @@
+<?php
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$l=new OC_L10N('calendar');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Authentication error") )));
+	exit();
+}
+
+// Get data
+if( isset( $_POST['timezone'] ) ){
+	$timezone=$_POST['timezone'];
+	OC_Preferences::setValue( OC_User::getUser(), 'calendar', 'timezone', $timezone );
+	echo json_encode( array( "status" => "success", "data" => array( "message" => $l->t("Timezone changed") )));
+}else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Invalid request") )));
+}
+
+?>
diff --git a/apps/calendar/ajax/updatecalendar.php b/apps/calendar/ajax/updatecalendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..2836dda218ae7e318847857c42c51dc4a0afceea
--- /dev/null
+++ b/apps/calendar/ajax/updatecalendar.php
@@ -0,0 +1,39 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once('../../../lib/base.php');
+
+$l10n = new OC_L10N('calendar');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Authentication error") )));
+	exit();
+}
+
+$calendarid = $_POST['id'];
+OC_Calendar_Calendar::editCalendar($calendarid, $_POST['name'], $_POST['description'], null, null, null, $_POST['color']);
+OC_Calendar_Calendar::setCalendarActive($calendarid, $_POST['active']);
+$calendar = OC_Calendar_Calendar::findCalendar($calendarid);
+$tmpl = new OC_Template('calendar', 'part.calendar.row');
+$tmpl->assign('calendar', $calendar);
+echo json_encode( array( "status" => "success", "data" => $tmpl->fetchPage() ));
diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php
new file mode 100755
index 0000000000000000000000000000000000000000..5ec2177e20cff91f27849ab96857185019349cea
--- /dev/null
+++ b/apps/calendar/appinfo/app.php
@@ -0,0 +1,20 @@
+<?php
+$l=new OC_L10N('calendar');
+OC::$CLASSPATH['OC_Calendar_Calendar'] = 'apps/calendar/lib/calendar.php';
+OC::$CLASSPATH['OC_Calendar_Hooks'] = 'apps/calendar/lib/hooks.php';
+OC::$CLASSPATH['OC_Connector_Sabre_CalDAV'] = 'apps/calendar/lib/connector_sabre.php';
+OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'deleteUser');
+
+OC_App::register( array( 
+  'order' => 10,
+  'id' => 'calendar',
+  'name' => 'Calendar' ));
+
+OC_App::addNavigationEntry( array( 
+  'id' => 'calendar_index',
+  'order' => 10,
+  'href' => OC_Helper::linkTo( 'calendar', 'index.php' ),
+  'icon' => OC_Helper::imagePath( 'calendar', 'icon.png' ),
+  'name' => $l->t('Calendar')));
+
+OC_App::registerPersonal('calendar', 'settings');
diff --git a/apps/calendar/appinfo/database.xml b/apps/calendar/appinfo/database.xml
new file mode 100755
index 0000000000000000000000000000000000000000..bf92179c40b45e0ac7a5cb69ddd99c8704f97e67
--- /dev/null
+++ b/apps/calendar/appinfo/database.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>utf8</charset>
+
+ <table>
+
+  <name>*dbprefix*calendar_objects</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>calendarid</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>objecttype</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>40</length>
+   </field>
+
+   <field>
+    <name>startdate</name>
+    <type>timestamp</type>
+    <default>0000-00-00 00:00:00</default>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>enddate</name>
+    <type>timestamp</type>
+    <default>0000-00-00 00:00:00</default>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>repeating</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>summary</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>calendardata</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>uri</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>lastmodified</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>4</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*calendar_calendars</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>userid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>displayname</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>uri</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>active</name>
+    <type>integer</type>
+    <default>1</default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>ctag</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>description</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>calendarorder</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>calendarcolor</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>10</length>
+   </field>
+
+   <field>
+    <name>timezone</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>components</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+</database>
diff --git a/apps/calendar/appinfo/info.xml b/apps/calendar/appinfo/info.xml
new file mode 100755
index 0000000000000000000000000000000000000000..c846fc1eebcc708de14adeb9f30f63f3420e8d39
--- /dev/null
+++ b/apps/calendar/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?> 
+<info>
+	<id>calendar</id>
+	<name>Calendar</name>
+	<version>0.2</version>
+	<licence>AGPL</licence>
+	<author>Georg Ehrke (Userinterface), Jakob Sack</author>
+	<require>2</require>
+	<description>Calendar with CalDAV support</description>
+</info>
diff --git a/apps/calendar/caldav.php b/apps/calendar/caldav.php
new file mode 100755
index 0000000000000000000000000000000000000000..49fc963336518b0c555bd95be232967de35b1488
--- /dev/null
+++ b/apps/calendar/caldav.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * ownCloud - Calendar
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Do not load FS ...
+$RUNTIME_NOSETUPFS = true;
+
+require_once('../../lib/base.php');
+
+// Backends
+$authBackend = new OC_Connector_Sabre_Auth();
+$principalBackend = new OC_Connector_Sabre_Principal();
+$caldavBackend    = new OC_Connector_Sabre_CalDAV();
+
+// Root nodes
+$nodes = array(
+	new Sabre_DAVACL_PrincipalCollection($principalBackend),
+	new Sabre_CalDAV_CalendarRootNode($principalBackend, $caldavBackend),
+);
+
+// Fire up server
+$server = new Sabre_DAV_Server($nodes);
+$server->setBaseUri($WEBROOT.'/apps/calendar/caldav.php');
+// Add plugins
+$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
+$server->addPlugin(new Sabre_CalDAV_Plugin());
+$server->addPlugin(new Sabre_DAVACL_Plugin());
+
+// And off we go!
+$server->exec();
diff --git a/apps/calendar/css/style.css b/apps/calendar/css/style.css
new file mode 100755
index 0000000000000000000000000000000000000000..afb530bcff06f7b58ce8a460ff3d745932135d0a
--- /dev/null
+++ b/apps/calendar/css/style.css
@@ -0,0 +1,55 @@
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * <http://www.gnu.org/licenses/>                 *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+#calendar_holder{position: absolute; top: 80px; bottom: 0px; left: 160px; right: 0px;}
+
+#view {margin-left: 10px; float: left; font-size: 12px;}
+#datecontrol {text-align: center;}
+#datecontrol_left{font-size: 12px;}
+#datecontrol_right{font-size: 12px;}
+#datecontrol_date_label {margin: 0; padding: 0; font-size: 12px;}
+#choosecalendar {margin-right: 170px; float: right; font-size: 12px;}
+
+#choosecalendar_dialog {display: none;}
+#newentry_dialog {display: none;}
+#editentry_dialog {display: none;}
+#parsingfail_dialog{display: none;}
+
+#view {margin-left: 10px; float: left; font-size: 12px;}
+#onedayview, #oneweekview, #fourweeksview, #onemonthview, #listview {display: none;}
+#onedayview table {margin: 0; padding: 0; width: 100%; border-spacing:1px; background: #EEEEEE;}
+#oneweekview table {margin: 0; padding: 0; width: 100%; border-spacing:1px; background: #EEEEEE;}
+#fourweeksview table {margin: 0; padding: 0; width: 100%; border-spacing:1px; background: #EEEEEE;}
+#onemonthview table {margin: 0;	padding: 0; width: 100%; border-spacing:1px; background: #EEEEEE;}
+#fourweeksview_calw1, #fourweeksview_calw2, #fourweeksview_calw3, #fourweeksview_calw4{vertical-align: middle;text-align: center;width: 50px;}
+
+#sysbox{display: none;}
+
+.actions {height: 33px;	min-width: 800px;}
+.controls {min-width: 800px;}
+.center {text-align: center;}
+.dateinfo {height: 15px; width: 100%; overflow: hidden; margin: 0; padding: 0; font-size: 12px;background: #F7F7F7;}
+.events {height: 80px; width: 100%; margin: 0; padding: 0;}
+.calendar_row {height: 20px; text-align: center;background: #ffffff;}
+.calendar_time {height: 20px; width: 50px; text-align:right;background: #ffffff;}
+.fourweeksview_item {text-align: center; background: #ffffff;width: 14%;}
+.onemonthview_item {text-align: center; height: 80px; margin: 0; padding: 0; vertical-align: top; background: #ffffff; width: 14%; height: 16%;}
+.weekend{text-align: center;margin: 0;	padding: 0;vertical-align: top;background: #F3F3F3; height: 80px;width: 100%; }
+.weekend_thead, .weekend_row{height: 20px;text-align: center;text-align: center;background: #F3F3F3;}
+.thisday{background: #FFFABC;text-align: center;}
diff --git a/apps/calendar/img/icon.png b/apps/calendar/img/icon.png
new file mode 100755
index 0000000000000000000000000000000000000000..ee0249b2c9be86e86b9951b1a1876d5e1c9af579
Binary files /dev/null and b/apps/calendar/img/icon.png differ
diff --git a/apps/calendar/index.php b/apps/calendar/index.php
new file mode 100755
index 0000000000000000000000000000000000000000..053b36c41833d5da1378050b963d83456ea83590
--- /dev/null
+++ b/apps/calendar/index.php
@@ -0,0 +1,36 @@
+<?php
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+require_once ("../../lib/base.php");
+if(!OC_USER::isLoggedIn()) {
+	header("Location: " . OC_HELPER::linkTo("", "index.php"));
+	exit;
+}
+// Create default calendar ...
+$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
+if( count($calendars) == 0){
+	OC_Calendar_Calendar::addCalendar(OC_User::getUser(),'default','Default calendar');
+	$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
+}
+OC_UTIL::addScript("calendar", "calendar");
+OC_UTIL::addScript("calendar", "calendar_init");
+OC_UTIL::addStyle("calendar", "style");
+OC_APP::setActiveNavigationEntry("calendar_index");
+$output = new OC_TEMPLATE("calendar", "calendar", "user");
+$output -> printPage();
diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js
new file mode 100755
index 0000000000000000000000000000000000000000..804b781886c626f3f9e831783a880e0f4d6ba651
--- /dev/null
+++ b/apps/calendar/js/calendar.js
@@ -0,0 +1,886 @@
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * <http://www.gnu.org/licenses/>                 *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ **************************************************
+ *               list of all fx                   *
+ * calw - Calendarweek                            *
+ * doy - Day of the year                          *
+ * checkforleapyear - check for a leap year       *
+ * forward_day - switching one day forward        *
+ * forward_week - switching one week forward      *
+ * forward_month - switching one month forward    *
+ * backward_day - switching one day backward      *
+ * backward_week - switching one week backward    *
+ * backward_month - switching one month backward  *
+ * update_view - update the view of the calendar  *
+ * onedayview - one day view                      *
+ * oneweekview - one week view                    *
+ * onemonthview - four Weeks view                 *
+ * onemonthview - one Month view                  *
+ * listview - listview                            *
+ * generate_monthview - generating month view     *
+ * generate_dates - generate other days for view  *
+ * load_events - load the events                  *
+ * switch2today - switching to today              *
+ * remove_events - remove old events in view      *
+ *************************************************/
+Calendar={
+	Date:{
+		normal_year_cal: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+		leap_year_cal: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+		calw:function() {
+			var generate_dayofweek = oc_cal_dayofweek;
+			if(generate_dayofweek == 0) {
+				generate_dayofweek = 7;
+			}
+			var calw = Math.floor((this.doy() - generate_dayofweek) / 7) + 1;
+			return calw;
+		},
+
+		doy:function() {
+			var cal = this.getnumberofdays(oc_cal_year);
+			var doy = 0;
+			for(var i = 0; i < oc_cal_month; i++) {
+				doy = doy + parseInt(cal[i]);
+			}
+			doy = doy + parseInt(oc_cal_dayofmonth);
+			return doy;
+		},
+
+		getnumberofdays:function(year) {
+			if(this.checkforleapyear(year) == true) {
+				var cal = this.leap_year_cal;
+			} else {
+				var cal = this.normal_year_cal;
+			}
+			return cal;
+		},
+
+		checkforleapyear:function(year2check) {
+			if((year2check / 600) == Math.floor(year2check / 400)) {
+				return true;
+			}
+			if((year2check / 4) == Math.floor(year2check / 4)) {
+				if((year2check / 100) == Math.floor(year2check / 100)) {
+					return false;
+				}
+				return true;
+			}
+			return false;
+		},
+
+		forward_day:function(){
+			var cal = this.getnumberofdays(oc_cal_year);
+			if(oc_cal_dayofmonth == cal[oc_cal_month]) {
+				if(oc_cal_month == 11) {
+					oc_cal_year++;
+					oc_cal_month = 0;
+					oc_cal_dayofmonth = 1;
+					if(oc_cal_dayofweek == 6) {
+						oc_cal_dayofweek = 0;
+					} else {
+						oc_cal_dayofweek++;
+					}
+				} else {
+					oc_cal_month++;
+					oc_cal_dayofmonth = 1;
+					if(oc_cal_dayofweek == 6) {
+						oc_cal_dayofweek = 0;
+					} else {
+						oc_cal_dayofweek++;
+					}
+				}
+			} else {
+				oc_cal_dayofmonth++;
+				if(oc_cal_dayofweek == 6) {
+					oc_cal_dayofweek = 0;
+				} else {
+					oc_cal_dayofweek++;
+				}
+			}
+		},
+
+		forward_week:function(){
+			for(var i = 1; i <= 7; i++) {
+				this.forward_day();
+			}
+		},
+
+		forward_month:function(){
+			var cal = this.getnumberofdays(oc_cal_year);
+			for(var i = 1; i <= cal[oc_cal_month]; i++) {
+				this.forward_day();
+			}
+		},
+
+		backward_day:function(){
+			var cal = this.getnumberofdays(oc_cal_year);
+			if(oc_cal_dayofmonth == 1) {
+				if(oc_cal_month == 0) {
+					oc_cal_year--;
+					oc_cal_month = 11;
+					oc_cal_dayofmonth = 31
+					if(oc_cal_dayofweek == 0) {
+						oc_cal_dayofweek = 6;
+					} else {
+						oc_cal_dayofweek--;
+					}
+				} else {
+					oc_cal_month--;
+					oc_cal_dayofmonth = cal[oc_cal_month];
+					if(oc_cal_dayofweek == 0) {
+						oc_cal_dayofweek = 6;
+					} else {
+						oc_cal_dayofweek--;
+					}
+				}
+			} else {
+				oc_cal_dayofmonth--;
+				if(oc_cal_dayofweek == 0) {
+					oc_cal_dayofweek = 6;
+				} else {
+					oc_cal_dayofweek--;
+				}
+			}
+		},
+
+		backward_week:function(){
+			for(var i = 1; i <= 7; i++) {
+				this.backward_day();
+			}
+		},
+
+		backward_month:function(){
+			var cal = this.getnumberofdays(oc_cal_year);
+			for(var i = cal[oc_cal_month]; i >= 1; i--) {
+				this.backward_day();
+			}
+		},
+
+	}
+}
+
+function oc_cal_update_view(view, task) {
+	if(view == "") {
+		view = oc_cal_currentview;
+	}
+	$("#sysbox").load(oc_webroot + "/apps/calendar/ajax/changeview.php?v="+view+"");
+	//no necessary to check whether the response is true or not
+	switch(view) {
+		case "onedayview":
+			if(task == "forward") {
+				Calendar.Date.forward_day();
+			}
+			if(task == "backward") {
+				Calendar.Date.backward_day();
+			}
+			oc_cal_remove_events("oneday");
+			oc_cal_load_cal("oneday");
+			oc_cal_load_events("oneday");
+			break;
+		case "oneweekview":
+			if(task == "forward") {
+				Calendar.Date.forward_week();
+			}
+			if(task == "backward") {
+				Calendar.Date.backward_week();
+			}
+			oc_cal_remove_events("oneweek");
+			oc_cal_load_cal("oneweek");
+			oc_cal_load_events("oneweek");
+			break;
+		case "fourweeksview":
+			if(task == "forward") {
+				Calendar.Date.forward_week();
+			}
+			if(task == "backward") {
+				Calendar.Date.backward_week();
+			}
+			oc_cal_remove_events("fourweeks");
+			oc_cal_load_cal("fourweeks");
+			oc_cal_load_events("fourweeks");
+			break;
+		case "onemonthview":
+			if(task == "forward") {
+				Calendar.Date.forward_month();
+			}
+			if(task == "backward") {
+				Calendar.Date.backward_month();
+			}
+			oc_cal_remove_events("onemonth");
+			oc_cal_load_cal("onemonth");
+			oc_cal_load_events("onemonth");
+			break;
+		case "listview":
+			if(task == "forward") {
+				Calendar.Date.forward_day();
+			}
+			if(task == "backward") {
+				Calendar.Date.backward_day();
+			}
+			oc_cal_remove_events("list");
+			oc_cal_load_cal("list");
+			oc_cal_load_events("list");
+			break;
+		default:
+			break;
+	}
+	if(oc_cal_month == 0){
+		oc_cal_update_eventsvar(oc_cal_year - 1);
+	}
+	if(oc_cal_month == 11){
+		oc_cal_update_eventsvar(oc_cal_year + 1);
+	}
+}
+
+function oc_cal_listview(task) {
+	if(task == "forward") {
+		Calendar.Date.forward_day();
+	}
+	if(task == "backward") {
+		Calendar.Date.backward_day();
+	}
+	document.getElementById("datecontrol_date").value = dayshort[dayofweek] + space + dayofmonth + space + monthshort[month] + space + year;
+}
+
+function oc_cal_generate_dates(view) {
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	if(view == "oneweek") {
+		var generate_dayofweek = oc_cal_dayofweek;
+		var generate_dayofmonth = oc_cal_dayofmonth;
+		var generate_month = oc_cal_month;
+		var generate_year = oc_cal_year;
+		var dates = new Array();
+		if(generate_dayofweek == 0) {
+			generate_dayofweek = 7;
+		}
+		for(var i = generate_dayofweek; i > 1; i--) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == 1) {
+				if(generate_month == 0) {
+					generate_year--;
+					generate_month = 11;
+					generate_dayofmonth = cal[generate_month];
+				} else {
+					generate_month--;
+					generate_dayofmonth = cal[generate_month];
+				}
+			} else {
+				generate_dayofmonth--;
+			}
+			generate_dayofweek--;
+		}
+		dates[0] = new Array(generate_dayofmonth, generate_month, generate_year);
+		for(var i = 1; i <= 6; i++) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == cal[generate_month]) {
+				if(generate_month == 11) {
+					generate_year++;
+					generate_month = 0;
+					generate_dayofmonth = 1;
+				} else {
+					generate_month++;
+					generate_dayofmonth = 1;
+				}
+			} else {
+				generate_dayofmonth++;
+			}
+			dates[i] = new Array(generate_dayofmonth, generate_month, generate_year);
+		}
+		return dates;
+	}
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	if(view == "fourweeks") {
+		var generate_dayofweek = oc_cal_dayofweek;
+		var generate_dayofmonth = oc_cal_dayofmonth;
+		var generate_month = oc_cal_month;
+		var generate_year = oc_cal_year;
+		var dates = new Array();
+		if(generate_dayofweek == 0) {
+			generate_dayofweek = 7;
+		}
+		for(var i = generate_dayofweek; i > 1; i--) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == 1) {
+				if(generate_month == 0) {
+					generate_year--;
+					generate_month = 11;
+					generate_dayofmonth = cal[generate_month];
+				} else {
+					generate_month--;
+					generate_dayofmonth = cal[generate_month];
+				}
+			} else {
+				generate_dayofmonth--;
+			}
+			generate_dayofweek--;
+		}
+		dates[0] = new Array(generate_dayofmonth, generate_month, generate_year);
+		for(var i = 1; i <= 27; i++) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == cal[generate_month]) {
+				if(generate_month == 11) {
+					generate_year++;
+					generate_month = 0;
+					generate_dayofmonth = 1;
+				} else {
+					generate_month++;
+					generate_dayofmonth = 1;
+				}
+			} else {
+				generate_dayofmonth++;
+			}
+			dates[i] = new Array(generate_dayofmonth, generate_month, generate_year);
+		}
+		return dates;
+	}
+	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+	if(view == "onemonth") {
+		var generate_dayofweek = oc_cal_dayofweek;
+		var generate_dayofmonth = oc_cal_dayofmonth;
+		var generate_month = oc_cal_month;
+		var generate_year = oc_cal_year;
+		var dates = new Array();
+		for(var i = generate_dayofmonth; i > 1; i--) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == 1) {
+				if(generate_month == 0) {
+					generate_year--;
+					generate_month = 11;
+					generate_dayofmonth = cal[generate_month];
+				} else {
+					generate_month--;
+					generate_dayofmonth = cal[generate_month];
+				}
+			} else {
+				generate_dayofmonth--;
+			}
+			if(generate_dayofweek == 0) {
+				generate_dayofweek = 6;
+			} else {
+				generate_dayofweek--;
+			}
+		}
+		if(generate_dayofweek == 0) {
+			generate_dayofweek = 7;
+			oc_cal_rows++;
+		}
+		for(var i = generate_dayofweek; i > 1; i--) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == 1) {
+				if(generate_month == 0) {
+					generate_year--;
+					generate_month = 11;
+					generate_dayofmonth = cal[generate_month];
+				} else {
+					generate_month--;
+					generate_dayofmonth = cal[generate_month];
+				}
+			} else {
+				generate_dayofmonth--;
+			}
+			generate_dayofweek--;
+		}
+		dates[0] = new Array(generate_dayofmonth, generate_month, generate_year);
+		for(var i = 1; i <= 41; i++) {
+			var cal = Calendar.Date.getnumberofdays(generate_year);
+			if(generate_dayofmonth == cal[generate_month]) {
+				if(generate_month == 11) {
+					generate_year++;
+					generate_month = 0;
+					generate_dayofmonth = 1;
+				} else {
+					generate_month++;
+					generate_dayofmonth = 1;
+				}
+			} else {
+				generate_dayofmonth++;
+			}
+			dates[i] = new Array(generate_dayofmonth, generate_month, generate_year);
+		}
+		return dates;
+	} else {////////////////////////////////////////////////////////////////////////////////////////////////////
+		return false;
+	}
+}
+
+function oc_cal_switch2today() {
+	oc_cal_date = oc_cal_today;
+	oc_cal_dayofweek = oc_cal_todaydayofweek;
+	oc_cal_month = oc_cal_todaymonth;
+	oc_cal_dayofmonth = oc_cal_todaydayofmonth;
+	oc_cal_year = oc_cal_todayyear;
+	oc_cal_update_view('', '');
+}
+
+function oc_cal_update_eventsvar(loadyear) {
+	$.getJSON(oc_webroot + "/apps/calendar/ajax/getcal.php?year=" + loadyear, function(newevents, status) {
+	if(status == "nosession") {
+		alert("You are not logged in. That can happen if you don't use owncloud for a long time.");
+		document.location(oc_webroot);
+	}
+	if(status == "parsingfail" || typeof (newevents) == "undefined") {
+		$(function() {
+			$( "#parsingfail_dialog" ).dialog();
+		});
+	} else {
+		oc_cal_events[loadyear] = newevents[loadyear];
+		oc_cal_update_view('', '');
+	}
+	});
+}
+
+function oc_cal_load_cal(loadview) {
+	if(loadview == "oneday") {
+		document.getElementById("datecontrol_date").value = oc_cal_dayshort[oc_cal_dayofweek] + oc_cal_space + oc_cal_dayofmonth + oc_cal_space + oc_cal_monthshort[oc_cal_month] + oc_cal_space + oc_cal_year;
+		document.getElementById("onedayview_today").innerHTML = oc_cal_daylong[oc_cal_dayofweek] + oc_cal_space + oc_cal_dayofmonth + oc_cal_space + oc_cal_monthshort[oc_cal_month];
+		var generate_dayofmonth = oc_cal_dayofmonth;
+		var generate_month = oc_cal_month;
+		var generate_year = oc_cal_year;
+		if(parseInt(generate_dayofmonth) <= 9){
+			generate_dayofmonth = "0" + generate_dayofmonth;
+		}
+		generate_month++;
+		if(parseInt(generate_month) <= 9){
+			generate_month = "0" + generate_month;
+		}
+		var generate_title = String(generate_dayofmonth) + String(generate_month) + String(generate_year);
+		document.getElementById('onedayview_today').title = generate_title;
+	}
+	if(loadview == "oneweek") {
+		document.getElementById("datecontrol_date").value = cw_label + ": " + Calendar.Date.calw();
+		var dates = oc_cal_generate_dates("oneweek");
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var weekday = 1;
+		for(var i = 0; i <= 6; i++){
+			var generate_dayofmonth = String(dates[i][0]);
+			var generate_month = String(dates[i][1]);
+			document.getElementById("oneweekview_" + weekdays[i]).innerHTML = oc_cal_dayshort[weekday] + oc_cal_space + dates[i][0] + oc_cal_space + oc_cal_monthshort[dates[i][1]];
+			if(parseInt(generate_dayofmonth) <= 9){
+				generate_dayofmonth = "0" + generate_dayofmonth;
+			}
+			generate_month++;
+			if(parseInt(generate_month) <= 9){
+				generate_month = "0" + generate_month;
+			}
+			var generate_title = String(generate_dayofmonth) + String(generate_month) + String(dates[i][2]);
+			document.getElementById("oneweekview_" + weekdays[i]).title = generate_title;
+			if(weekday == 6){
+				weekday = 0;
+			}else{
+				weekday++;
+			}
+		}
+	}
+	if(loadview == "fourweeks") {
+		var calw1 = Calendar.Date.calw();
+		if(calw1 == 52) {
+			var calw2 = 1;
+		} else {
+			var calw2 = Calendar.Date.calw() + 1;
+		}
+		if(calw1 == 51) {
+			var calw3 = 1;
+		} else if(calw1 == 52) {
+			var calw3 = 2;
+		} else {
+			var calw3 = Calendar.Date.calw() + 2;
+		}
+		if(calw1 == 50) {
+			var calw4 = 1;
+		} else if(calw1 == 51) {
+			var calw4 = 2;
+		} else if(calw1 == 52) {
+			var calw4 = 3;
+		} else {
+			var calw4 = Calendar.Date.calw() + 3;
+		}
+		var calwplusfour = calw4;
+		var dates = oc_cal_generate_dates("fourweeks");
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var weeknum = 1;
+		var weekday = 0;
+		for(var i = 0; i <= 27; i++){
+			var generate_dayofmonth = String(dates[i][0]);
+			var generate_month = String(dates[i][1]);
+			var generate_year = dates[i][2];
+			document.getElementById("dateinfo_fourweeksview_" + weekdays[weekday] + "_" + weeknum).innerHTML = generate_dayofmonth + oc_cal_space + oc_cal_monthshort[generate_month]; 
+			if(parseInt(generate_dayofmonth) <= 9){
+				generate_dayofmonth = "0" + generate_dayofmonth;
+			}
+			if(generate_dayofmonth == oc_cal_todaydayofmonth && generate_month == oc_cal_todaymonth && generate_year == oc_cal_todayyear){
+				document.getElementById("fourweeksview_" + weekdays[weekday] + "_" + weeknum).className = "thisday";
+			}else{
+				document.getElementById("fourweeksview_" + weekdays[weekday] + "_" + weeknum).className = "fourweeksview_item";
+			}
+			generate_month++;
+			if(parseInt(generate_month) <= 9){
+				generate_month = "0" + generate_month;
+			}
+			var generate_title = String(generate_dayofmonth) + String(generate_month) + String(dates[i][2]);
+			document.getElementById("fourweeksview_" + weekdays[weekday] + "_" + weeknum).title = generate_title;
+			if(weekday == 6){
+				weekday = 0;
+				weeknum++;
+			}else{
+				weekday++;
+			}
+		}
+		document.getElementById("fourweeksview_calw1").innerHTML = calw1;
+		document.getElementById("fourweeksview_calw2").innerHTML = calw2;
+		document.getElementById("fourweeksview_calw3").innerHTML = calw3;
+		document.getElementById("fourweeksview_calw4").innerHTML = calw4;
+		document.getElementById("datecontrol_date").value = cws_label + ": " + Calendar.Date.calw() + " - " + calwplusfour;
+	}
+	if(loadview == "onemonth") {
+		document.getElementById("datecontrol_date").value = oc_cal_monthlong[oc_cal_month] + oc_cal_space + oc_cal_year;
+		var cal = Calendar.Date.getnumberofdays(oc_cal_year);
+		var monthview_dayofweek = oc_cal_dayofweek;
+		var monthview_dayofmonth = oc_cal_dayofmonth;
+		for(var i = monthview_dayofmonth; i > 1; i--) {
+			if(monthview_dayofweek == 0) {
+				monthview_dayofweek = 6;
+			} else {
+				monthview_dayofweek--;
+			}
+		}
+		document.getElementById("onemonthview_week_5").style.display = "none";
+		document.getElementById("onemonthview_week_6").style.display = "none";
+		oc_cal_rows = parseInt(monthview_dayofweek) + parseInt(cal[oc_cal_month]);
+		oc_cal_rows = oc_cal_rows / 7;
+		oc_cal_rows = Math.ceil(oc_cal_rows);
+		var dates = oc_cal_generate_dates("onemonth");
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var weeknum = 1;
+		var weekday = 0;
+		for(var i = 0; i <= 41; i++){
+			var generate_dayofmonth = dates[i][0];
+			var generate_month = dates[i][1];
+			var generate_year = dates[i][2];
+			document.getElementById("dateinfo_onemonthview_" + weekdays[weekday] + "_" + weeknum).innerHTML = generate_dayofmonth + oc_cal_space + oc_cal_monthshort[generate_month];
+			if(parseInt(generate_dayofmonth) <= 9){
+				generate_dayofmonth = "0" + generate_dayofmonth;
+			}
+			if(generate_dayofmonth == oc_cal_todaydayofmonth && generate_month == oc_cal_todaymonth && generate_year == oc_cal_todayyear){
+				document.getElementById("onemonthview_" + weekdays[weekday] + "_" + weeknum).className = "thisday";
+			}else{
+				document.getElementById("onemonthview_" + weekdays[weekday] + "_" + weeknum).className = "onemonthview_item";
+			}
+			generate_month++;
+			if(parseInt(generate_month) <= 9){
+				generate_month = "0" + generate_month;
+			}
+			var generate_title = String(generate_dayofmonth) + String(generate_month) + String(generate_year);
+			document.getElementById("onemonthview_" + weekdays[weekday] + "_" + weeknum).title = generate_title;
+			if(weekday == 6){
+				weekday = 0;
+				weeknum++;
+			}else{
+				weekday++;
+			}
+		}
+		if(oc_cal_rows == 5) {
+			document.getElementById("onemonthview_week_5").style.display = "table-row";
+		}
+		if(oc_cal_rows == 6) {
+			document.getElementById("onemonthview_week_5").style.display = "table-row";
+			document.getElementById("onemonthview_week_6").style.display = "table-row";
+		}
+	}
+	if(loadview == "list") {
+		document.getElementById("datecontrol_date").value = oc_cal_dayshort[oc_cal_dayofweek] + oc_cal_space + oc_cal_dayofmonth + oc_cal_space + oc_cal_monthshort[oc_cal_month] + oc_cal_space + oc_cal_year;
+	}
+}
+
+function oc_cal_load_events(loadview) {
+	if(loadview == "oneday") {
+		if( typeof (oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth]) != "undefined") {
+			if( typeof (oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth]["allday"]) != "undefined") {
+				var eventnumber = 1;
+				var eventcontainer = document.getElementById("onedayview_wholeday");
+				while( typeof (oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth]["allday"][eventnumber]) != "undefined") {
+					var newp = document.createElement("p");
+					newp.id = "onedayview_allday_" + eventnumber;
+					newp.className = "onedayview_event";
+					eventcontainer.appendChild(newp);
+					newp.innerHTML = oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth]["allday"][eventnumber]["description"];
+					eventnumber++;
+				}
+			}
+			for( i = 0; i <= 23; i++) {
+				if( typeof (oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth][i]) != "undefined") {
+					var eventnumber = 1;
+					var eventcontainer = document.getElementById("onedayview_" + i);
+					while( typeof (oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth][i][eventnumber]) != "undefined") {
+						var newp = document.createElement("p");
+						newp.id = "onedayview_" + i + "_" + eventnumber;
+						newp.className = "onedayview_event";
+						eventcontainer.appendChild(newp);
+						newp.innerHTML = oc_cal_events[oc_cal_year][oc_cal_month][oc_cal_dayofmonth][i][eventnumber]["description"];
+						eventnumber++;
+					}
+				}
+			}
+		}
+	}
+	if(loadview == "oneweek") {//(generate_dayofmonth, generate_month, generate_year);
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var dates = oc_cal_generate_dates("oneweek");
+		for(var i = 0; i <= 6; i++) {
+			var loadevents_month = dates[i][1];
+			var loadevents_days = dates[i][0];
+			if( typeof (oc_cal_events[oc_cal_year][loadevents_month]) != "undefined") {
+				if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]) != "undefined") {
+					if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"]) != "undefined") {
+						var eventnumber = 1;
+						var eventcontainer = document.getElementById("oneweekview_" + weekdays[i] + "_allday");
+						while( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"][eventnumber]) != "undefined") {
+							var newp = document.createElement("p");
+							newp.id = "oneweekview_" + weekdays[i] + "_allday_" + eventnumber;
+							newp.className = "oneweekview_event";
+							eventcontainer.appendChild(newp);
+							newp.innerHTML = oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"][eventnumber]["description"];
+							eventnumber++;
+						}
+					}
+					for(var time = 0; time <= 23; time++) {
+						if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time]) != "undefined") {
+							var eventnumber = 1;
+							var eventcontainer = document.getElementById("oneweekview_" + weekdays[i] + "_" + time);
+							while( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time][eventnumber]) != "undefined") {
+								var newp = document.createElement("p");
+								newp.id = "oneweekview_" + weekdays[i] + "_" + time + "_" + eventnumber;
+								newp.className = "oneweekview_event";
+								eventcontainer.appendChild(newp);
+								newp.innerHTML = oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time][eventnumber]["description"];
+								eventnumber++;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	if(loadview == "fourweeks") {
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var dates = oc_cal_generate_dates("fourweeks");
+		var weekdaynum = 0;
+		var weeknum = 1;
+		for(var i = 0; i <= 27; i++) {
+			var loadevents_month = dates[i][1];
+			var loadevents_days = dates[i][0];
+			if( typeof (oc_cal_events[oc_cal_year][loadevents_month]) != "undefined") {
+				if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]) != "undefined") {
+					var pnum = 0;
+					if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"]) != "undefined") {
+						var eventnumber = 1;
+						var eventcontainer = document.getElementById("events_fourweeksview_" + weekdays[weekdaynum] + "_" + weeknum);
+						while( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"][eventnumber]) != "undefined") {
+							var newp = document.createElement("p");
+							newp.id = "fourweeksview_" + weekdays[weekdaynum] + "_" + weeknum + "_" + pnum;
+							newp.className = "fourweeksview_event";
+							eventcontainer.appendChild(newp);
+							newp.innerHTML = oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"][eventnumber]["description"];
+							eventnumber++;
+							pnum++;
+						}
+					}
+					for(var time = 0; time <= 23; time++) {
+						if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time]) != "undefined") {
+							var eventnumber = 1;
+							var eventcontainer = document.getElementById("events_fourweeksview_" + weekdays[weekdaynum] + "_" + weeknum);
+							while( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time][eventnumber]) != "undefined") {
+								var newp = document.createElement("p");
+								newp.id = "fourweeksview_" + weekdays[i] + "_" + i + "_" + eventnumber;
+								newp.className = "fourweeksview_event";
+								eventcontainer.appendChild(newp);
+								newp.innerHTML = oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time][eventnumber]["description"];
+								eventnumber++;
+								pnum++;
+							}
+						}
+					}
+				}
+			}
+			if(weekdaynum == 6){
+				weekdaynum = 0;
+				weeknum++;
+			}else{
+				weekdaynum++;
+			}
+		}
+	}
+	if(loadview == "onemonth") {
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var dates = oc_cal_generate_dates("onemonth");
+		var weekdaynum = 0;
+		var weeknum = 1;
+		for(var i = 0; i <= 41; i++) {
+			var loadevents_month = dates[i][1];
+			var loadevents_days = dates[i][0];
+			if( typeof (oc_cal_events[oc_cal_year][loadevents_month]) != "undefined") {
+				if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]) != "undefined") {
+					var pnum = 0;
+					if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"]) != "undefined") {
+						var eventnumber = 1;
+						var eventcontainer = document.getElementById("events_onemonthview_" + weekdays[weekdaynum] + "_" + weeknum);
+						while( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"][eventnumber]) != "undefined") {
+							var newp = document.createElement("p");
+							newp.id = "onemonthview_" + weekdays[weekdaynum] + "_" + weeknum + "_" + pnum;
+							newp.className = "onemonthview_event";
+							eventcontainer.appendChild(newp);
+							newp.innerHTML = oc_cal_events[oc_cal_year][loadevents_month][loadevents_days]["allday"][eventnumber]["description"];
+							eventnumber++;
+							pnum++;
+						}
+					}
+					for(var time = 0; time <= 23; time++) {
+						if( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time]) != "undefined") {
+							var eventnumber = 1;
+							var eventcontainer = document.getElementById("events_onemonthview_" + weekdays[weekdaynum] + "_" + weeknum);
+							while( typeof (oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time][eventnumber]) != "undefined") {
+								var newp = document.createElement("p");
+								newp.id = "onemonthview_" + weekdays[i] + "_" + time + "_" + eventnumber;
+								newp.className = "onemonthview_event";
+								eventcontainer.appendChild(newp);
+								newp.innerHTML = oc_cal_events[oc_cal_year][loadevents_month][loadevents_days][time][eventnumber]["description"];
+								eventnumber++;
+								pnum++;
+							}
+						}
+					}
+				}
+			}
+			if(weekdaynum == 6){
+				weekdaynum = 0;
+				weeknum++;
+			}else{
+				weekdaynum++;
+			}
+		}
+	}
+	if(loadview == "list") {
+		//
+	}
+}
+
+function oc_cal_remove_events(removeview) {
+	if(removeview == "oneday") {
+		document.getElementById("onedayview_wholeday").innerHTML = "";
+		for(var i = 0; i <= 23; i++) {
+			document.getElementById("onedayview_" + i).innerHTML = "";
+		}
+	}
+	if(removeview == "oneweek") {
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		for( i = 0; i <= 6; i++) {
+			document.getElementById("oneweekview_" + weekdays[i] + "_allday").innerHTML = "";
+			for(var time = 0; time <= 23; time++) {
+				document.getElementById("oneweekview_" + weekdays[i] + "_" + time).innerHTML = "";
+			}
+		}
+	}
+	if(removeview == "fourweeks") {
+		document.getElementById("events_fourweeksview_monday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_tuesday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_wednesday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_thursday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_friday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_saturday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_sunday_1").innerHTML = "";
+		document.getElementById("events_fourweeksview_monday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_tuesday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_wednesday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_thursday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_friday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_saturday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_sunday_2").innerHTML = "";
+		document.getElementById("events_fourweeksview_monday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_tuesday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_wednesday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_thursday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_friday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_saturday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_sunday_3").innerHTML = "";
+		document.getElementById("events_fourweeksview_monday_4").innerHTML = "";
+		document.getElementById("events_fourweeksview_tuesday_4").innerHTML = "";
+		document.getElementById("events_fourweeksview_wednesday_4").innerHTML = "";
+		document.getElementById("events_fourweeksview_thursday_4").innerHTML = "";
+		document.getElementById("events_fourweeksview_friday_4").innerHTML = "";
+		document.getElementById("events_fourweeksview_saturday_4").innerHTML = "";
+		document.getElementById("events_fourweeksview_sunday_4").innerHTML = "";
+	}
+	if(removeview == "onemonth") {
+		var weekdays = new Array("monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday");
+		var weeknum = 1;
+		var weekday = 0;
+		for(var i = 0; i <= 41; i++){//events_onemonthview_saturday_6
+			document.getElementById("events_onemonthview_" + weekdays[weekday] + "_" + weeknum).innerHTML = "";
+			document.getElementById("onemonthview_" + weekdays[weekday] + "_" + weeknum).className = "onemonthview_item";
+			if(weekday == 6){
+				weekday = 0;
+				weeknum++;
+			}else{
+				weekday++;
+			}
+		}
+	}
+	if(removeview == "list") {
+		document.getElementById("listview").innerHTML = "";
+	}
+}
+function oc_cal_newevent(date, time){
+	if(oc_cal_opendialog == 0){
+		$("#dialog_holder").load(oc_webroot + "/apps/calendar/ajax/neweventform.php?d=" + date + "&t=" + time);
+		oc_cal_opendialog = 1;
+	}else{
+		alert(t("calendar", "You can't open more than one dialog per site!"));
+	}
+}
+function oc_cal_choosecalendar(){
+	if(oc_cal_opendialog == 0){
+		$("#dialog_holder").load(oc_webroot + "/apps/calendar/ajax/choosecalendar.php");
+		oc_cal_opendialog = 1;
+	}else{
+		alert(t("calendar", "You can't open more than one dialog per site!"));
+	}
+}
+function oc_cal_calender_activation(checkbox, calendarid)
+{
+	$.post(oc_webroot + "/apps/calendar/ajax/activation.php", { calendarid: calendarid, active: checkbox.checked?1:0 },
+	  function(data) {
+		checkbox.checked = data == 1;
+	  });
+}
+function oc_cal_editcalendar(object, calendarid){
+	$(object).closest('tr').load(oc_webroot + "/apps/calendar/ajax/editcalendar.php?calendarid="+calendarid);
+}
+function oc_cal_editcalendar_submit(button, calendarid){
+	var displayname = $("#displayname_"+calendarid).val();
+	var active = $("#active_"+calendarid+":checked").length;
+	var description = $("#description_"+calendarid).val();
+	var calendarcolor = $("#calendarcolor_"+calendarid).val();
+
+	$.post("ajax/updatecalendar.php", { id: calendarid, name: displayname, active: active, description: description, color: calendarcolor },
+		function(data){
+			if(data.error == "true"){
+			}else{
+				$(button).closest('tr').html(data.data)
+			}
+		}, 'json');
+}
diff --git a/apps/calendar/js/calendar_init.js b/apps/calendar/js/calendar_init.js
new file mode 100755
index 0000000000000000000000000000000000000000..9f6478638474970b7266773f83438d78a33207b3
--- /dev/null
+++ b/apps/calendar/js/calendar_init.js
@@ -0,0 +1,55 @@
+/*************************************************
+ * ownCloud - Calendar Plugin                     *
+ *                                                *
+ * (c) Copyright 2011 Georg Ehrke                 *
+ * author: Georg Ehrke                            *
+ * email: ownclouddev at georgswebsite dot de     *
+ * homepage: ownclouddev.georgswebsite.de         *
+ * manual: ownclouddev.georgswebsite.de/manual    *
+ * License: GNU AFFERO GENERAL PUBLIC LICENSE     *
+ *                                                *
+ * <http://www.gnu.org/licenses/>                 *
+ * If you are not able to view the License,       *
+ * <http://www.gnu.org/licenses/>                 *
+ * <http://ownclouddev.georgswebsite.de/license/> *
+ * please write to the Free Software Foundation.  *
+ * Address:                                       *
+ * 59 Temple Place, Suite 330, Boston,            *
+ * MA 02111-1307  USA                             *
+ *************************************************/
+//loading multiselect
+$(document).ready(function(){
+	$("#calendar_select").multiSelect({
+		selectedText: "Calendars",
+		noneSelectedText: "Calendars",
+		selectedList: 0,
+		close: function(){
+			alert("abc");
+   		}
+	});
+	oc_cal_update_eventsvar(oc_cal_year);
+});
+//init date vars
+var oc_cal_date = new Date();
+var oc_cal_dayofweek = oc_cal_date.getDay();
+var oc_cal_month = oc_cal_date.getMonth();
+var oc_cal_dayofmonth = oc_cal_date.getDate();
+var oc_cal_year = oc_cal_date.getFullYear();
+var oc_cal_space = " ";
+//init today date vars
+var oc_cal_today = new Date();
+var oc_cal_todaydayofweek = oc_cal_today.getDay();
+var oc_cal_todaymonth = oc_cal_today.getMonth();
+var oc_cal_todaydayofmonth = oc_cal_today.getDate();
+var oc_cal_todayyear = oc_cal_today.getFullYear();
+//other vars
+var oc_cal_rows;
+var oc_cal_dates;
+var oc_cal_listview_numofevents = 0;
+var oc_cal_listview_count = 0;
+var oc_cal_opendialog = 0;
+var oc_cal_datemonthyear =  String(oc_cal_dayofmonth) + String(oc_cal_month) + String(oc_cal_year);
+var oc_cal_calendars = new Array();
+//event vars
+var oc_cal_events = new Array();
+oc_cal_events[oc_cal_year] = new Array();
diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js
new file mode 100755
index 0000000000000000000000000000000000000000..b2da81b0d0f9a291d42f70499d97f023c521bb0c
--- /dev/null
+++ b/apps/calendar/js/settings.js
@@ -0,0 +1,15 @@
+$(document).ready(function(){
+	$("#timezone").change( function(){
+		// Serialize the data
+		var post = $( "#timezone" ).serialize();
+		// Ajax foo
+		$.post( oc_webroot + '/apps/calendar/ajax/settimezone.php', post, function(data){
+			if( data.status == "success" ){
+			}
+			else{
+				$('#timezoneerror').html( data.data.message );
+			}
+		});
+		return false;
+	});
+});
diff --git a/apps/calendar/known bugs b/apps/calendar/known bugs
new file mode 100644
index 0000000000000000000000000000000000000000..fb3cd2aa28abdd1585618944850ed8c3f2bcb388
--- /dev/null
+++ b/apps/calendar/known bugs	
@@ -0,0 +1 @@
+There are actually no known bugs
diff --git a/apps/calendar/l10n/de.php b/apps/calendar/l10n/de.php
new file mode 100755
index 0000000000000000000000000000000000000000..325a323d57ab2852198844a860048733e52e915e
--- /dev/null
+++ b/apps/calendar/l10n/de.php
@@ -0,0 +1,78 @@
+<?php $TRANSLATIONS = array(
+"Calendar" => "Kalender",
+"Location" => "Ort",
+"Category" => "Kategorie",
+"Create a new event" => "Neuen Termin erstellen",
+"Title of the Event" => "Titel des Termins",
+"Location of the Event" => "Ort des Termins",
+"All Day Event" => "Ganztägig",
+"From" => "Von",
+"To" => "Bis",
+"Repeat" => "Wiederhol.",
+"Attendees" => "Teilnehmer",
+"Description" => "Beschreibung",
+"Submit" => "Speichern",
+"Reset" => "Zurücksetzen",
+"Title" => "Titel",
+"Sunday" => "Sonntag",
+"Monday" => "Montag",
+"Tuesday" => "Dienstag",
+"Wednesday" =>  "Mittwoch",
+"Thursday" =>  "Donnerstag",
+"Friday" =>  "Freitag",
+"Saturday" =>  "Samstag",
+"CW" => "KW",
+"CWs" => "KW",
+"Sun." => "So.",
+"Mon." => "Mo.",
+"Tue." => "Di.",
+"Wed." => "Mi.",
+"Thu." => "Do.",
+"Fri." => "Fr.",
+"Sat." => "Sa.",
+"January" => "Januar",
+"February" => "Februar",
+"March" => "März",
+"April" => "April",
+"May" => "Mai",
+"June" => "Juni",
+"July" => "Juli",
+"August" => "August",
+"September" => "September",
+"October" => "Oktober",
+"November" => "November",
+"December" => "Dezember",
+"Jan." => "Jan.",
+"Feb." => "Feb.",
+"Mar." => "März",
+"Apr." => "Apr.",
+"May" => "Mai",
+"Jun." => "Juni",
+"Jul." => "Juli",
+"Aug." => "Aug.",
+"Sep." => "Sep.",
+"Oct." => "Okt.",
+"Nov." => "Nov.",
+"Dec." => "Dez.",
+"Day" => "Tag",
+"Week" => "Woche",
+"Weeks" => "Wochen",
+"Month" => "Monat",
+"Listview" => "Liste",
+"Today" => "Heute",
+"Calendars" => "Kalender",
+"Time" => "Uhrzeit",
+"All day" => "Ganztägig",
+"Does not repeat" => "Keine Wiederholung",
+"Daily" => "Täglich",
+"Weekly" => "Wöchentlich",
+"Every Weekday" => "jeden Wochentag",
+"Bi-Weekly" => "jede 2. Woche",
+"Monthly" => "Monatlich",
+"Yearly" => "Jährlich",
+"Description of the Event" => "Beschreibung des Termins",
+"" => "",
+"" => "",
+"" => ""
+);
+?>
\ No newline at end of file
diff --git a/apps/calendar/l10n/es.php b/apps/calendar/l10n/es.php
new file mode 100755
index 0000000000000000000000000000000000000000..f20230fe17f9f8bd9724fac649c04fd0acc9f1e0
--- /dev/null
+++ b/apps/calendar/l10n/es.php
@@ -0,0 +1,68 @@
+<?php $TRANSLATIONS = array(
+"Calendar" => "Calendario",
+"Location" => "Lugar",
+"Category" => "Categoría",
+"Create a new event" => "Crea un plazo",
+"Title of the Event" => "Título del plazo",
+"Location of the Event" => "Lugar del plazo",
+"All Day Event" => "todo el día",
+"From" => "Desde",
+"To" => "Hasta",
+"Repeat" => "Repetición",
+"Attendees" => "Participante",
+"Description" => "Descripción",
+"Submit" => "Guarda",
+"Reset" => "Repone",
+"Title" => "Título",
+"Sunday" => "Domingo",
+"Monday" => "Lunes",
+"Tuesday" => "Martes",
+"Wednesday" =>  "Miércoles",
+"Thursday" =>  "Jueves",
+"Friday" =>  "Viernes",
+"Saturday" =>  "Sábado",
+"CW" => "Semana",
+"CWs" => "Semanas",
+"Sun." => "Do.",
+"Mon." => "Lu.",
+"Tue." => "Ma.",
+"Wed." => "Mi.",
+"Thu." => "Ju.",
+"Fri." => "Vi.",
+"Sat." => "Sá.",
+"January" => "Enero",
+"February" => "Febrero",
+"March" => "Marzo",
+"April" => "Abril",
+"May" => "Mayo",
+"June" => "Junio",
+"July" => "Julio",
+"August" => "Agosto",
+"September" => "Septiembre",
+"October" => "Octubre",
+"November" => "Noviembre",
+"December" => "Deciembre",
+"Jan." => "Ene.",
+"Feb." => "Feb.",
+"Mar." => "Mär.",
+"Apr." => "Abr.",
+"May" => "May.",
+"Jun." => "Jun.",
+"Jul." => "Jul.",
+"Aug." => "Ago.",
+"Sep." => "Sep.",
+"Oct." => "Oct.",
+"Nov." => "Nov.",
+"Dec." => "Dec.",
+"Day" => "Día",
+"Week" => "Semana",
+"Weeks" => "Semanas",
+"Month" => "Mes",
+"Listview" => "Lista",
+"Today" => "Hoy",
+"Calendars" => "Calendarios",
+"Time" => "Hora",
+"All day" => "todo el día",
+"" => ""
+);
+?>
\ No newline at end of file
diff --git a/apps/calendar/l10n/xgettextfiles b/apps/calendar/l10n/xgettextfiles
new file mode 100755
index 0000000000000000000000000000000000000000..c94fb9d8b2efae83ba0a5457d0ce7b197472e83b
--- /dev/null
+++ b/apps/calendar/l10n/xgettextfiles
@@ -0,0 +1,8 @@
+../appinfo/app.php
+../templates/calendar.php
+../templates/part.editevent.php
+../templates/part.eventinfo.php
+../templates/part.newevent.php
+../templates/part.choosecalendar.php
+../js/calendar.js
+../js/calendar_init.js
\ No newline at end of file
diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..c1223b5b3ab9e3424085f607bf2ccceb3c84eb7a
--- /dev/null
+++ b/apps/calendar/lib/calendar.php
@@ -0,0 +1,354 @@
+<?php
+/**
+ * ownCloud - Calendar
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE calendar_objects (
+ *     id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ *     calendarid INTEGER UNSIGNED NOT NULL,
+ *     objecttype VARCHAR(40) NOT NULL,
+ *     startdate DATETIME,
+ *     enddate DATETIME,
+ *     repeating INT(1),
+ *     summary VARCHAR(255),
+ *     calendardata TEXT,
+ *     uri VARCHAR(100),
+ *     lastmodified INT(11)
+ * );
+ * 
+ * CREATE TABLE calendar_calendars (
+ *     id INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ *     userid VARCHAR(255),
+ *     displayname VARCHAR(100),
+ *     uri VARCHAR(100),
+ *     active INTEGER UNSIGNED NOT NULL DEFAULT '0',
+ *     ctag INTEGER UNSIGNED NOT NULL DEFAULT '0',
+ *     description TEXT,
+ *     calendarorder INTEGER UNSIGNED NOT NULL DEFAULT '0',
+ *     calendarcolor VARCHAR(10),
+ *     timezone TEXT,
+ *     components VARCHAR(20)
+ * );
+ */
+
+/**
+ * This class manages our calendars
+ */
+class OC_Calendar_Calendar{
+	public static function allCalendars($uid, $active=null){
+		$values = array($uid);
+		$active_where = '';
+		if (!is_null($active)){
+			$active_where = ' AND active = ?';
+			$values[] = $active;
+		}
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_calendars WHERE userid = ?' . $active_where );
+		$result = $stmt->execute($values);
+		
+		$calendars = array();
+		while( $row = $result->fetchRow()){
+			$calendars[] = $row;
+		}
+
+		return $calendars;
+	}
+	
+	public static function allCalendarsWherePrincipalURIIs($principaluri){
+		$uid = self::extractUserID($principaluri);
+		return self::allCalendars($uid);
+	}
+
+	public static function findCalendar($id){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_calendars WHERE id = ?' );
+		$result = $stmt->execute(array($id));
+
+		return $result->fetchRow();
+	}
+
+	public static function addCalendar($userid,$name,$description,$components='VEVENT,VTODO,VJOURNAL',$timezone=null,$order=0,$color=null){
+		$all = self::allCalendars($userid);
+		$uris = array();
+		foreach($all as $i){
+			$uris[] = $i['uri'];
+		}
+
+		$uri = self::createURI($name, $uris );
+
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*calendar_calendars (userid,displayname,uri,ctag,description,calendarorder,calendarcolor,timezone,components) VALUES(?,?,?,?,?,?,?,?,?)' );
+		$result = $stmt->execute(array($userid,$name,$uri,1,$description,$order,$color,$timezone,$components));
+
+		return OC_DB::insertid();
+	}
+
+	public static function addCalendarFromDAVData($principaluri,$uri,$name,$description,$components,$timezone,$order,$color){
+		$userid = self::extractUserID($principaluri);
+		
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*calendar_calendars (userid,displayname,uri,ctag,description,calendarorder,calendarcolor,timezone,components) VALUES(?,?,?,?,?,?,?,?,?)' );
+		$result = $stmt->execute(array($userid,$name,$uri,1,$description,$order,$color,$timezone,$components));
+
+		return OC_DB::insertid();
+	}
+
+	public static function editCalendar($id,$name=null,$description=null,$components=null,$timezone=null,$order=null,$color=null){
+		// Need these ones for checking uri
+		$calendar = self::findCalendar($id);
+
+		// Keep old stuff
+		if(is_null($name)) $name = $calendar['name'];
+		if(is_null($description)) $description = $calendar['description'];
+		if(is_null($components)) $components = $calendar['components'];
+		if(is_null($timezone)) $timezone = $calendar['timezone'];
+		if(is_null($order)) $order = $calendar['calendarorder'];
+		if(is_null($color)) $color = $calendar['color'];
+		
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*calendar_calendars SET displayname=?,description=?,calendarorder=?,calendarcolor=?,timezone=?,components=?,ctag=ctag+1 WHERE id=?' );
+		$result = $stmt->execute(array($name,$description,$order,$color,$timezone,$components,$id));
+
+		return true;
+	}
+
+	public static function setCalendarActive($id,$active){
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*calendar_calendars SET active = ? WHERE id = ?' );
+		$stmt->execute(array($active, $id));
+
+		return true;
+	}
+
+	public static function touchCalendar($id){
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*calendar_calendars SET ctag = ctag + 1 WHERE id = ?' );
+		$stmt->execute(array($id));
+
+		return true;
+	}
+
+	public static function deleteCalendar($id){
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*calendar_calendars WHERE id = ?' );
+		$stmt->execute(array($id));
+		
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*calendar_objects WHERE calendarid = ?' );
+		$stmt->execute(array($id));
+
+		return true;
+	}
+
+	public static function allCalendarObjects($id){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_objects WHERE calendarid = ?' );
+		$result = $stmt->execute(array($id));
+
+		$calendarobjects = array();
+		while( $row = $result->fetchRow()){
+			$calendarobjects[] = $row;
+		}
+
+		return $calendarobjects;
+	}
+	
+	public static function findCalendarObject($id){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_objects WHERE id = ?' );
+		$result = $stmt->execute(array($id));
+
+		return $result->fetchRow();
+	}
+
+	public static function findCalendarObjectWhereDAVDataIs($cid,$uri){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*calendar_objects WHERE calendarid = ? AND uri = ?' );
+		$result = $stmt->execute(array($cid,$uri));
+
+		return $result->fetchRow();
+	}
+
+	public static function addCalendarObject($id,$data){
+		$object = Sabre_VObject_Reader::read($data);
+		list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
+
+		if(is_null($uid)){
+			$uid = self::createUID();
+			$object->add('UID',$uid);
+			$data = $object->serialize();
+		}
+
+		$uri = 'owncloud-'.md5($data.rand().time()).'.ics';
+
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*calendar_objects (calendarid,objecttype,startdate,enddate,repeating,summary,calendardata,uri,lastmodified) VALUES(?,?,?,?,?,?,?,?,?)' );
+		$result = $stmt->execute(array($id,$type,$startdate,$enddate,$repeating,$summary,$data,$uri,time()));
+
+		self::touchCalendar($id);
+
+		return OC_DB::insertid();
+	}
+
+	public static function addCalendarObjectFromDAVData($id,$uri,$data){
+		$object = Sabre_VObject_Reader::read($data);
+		list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
+
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*calendar_objects (calendarid,objecttype,startdate,enddate,repeating,summary,calendardata,uri,lastmodified) VALUES(?,?,?,?,?,?,?,?,?)' );
+		$result = $stmt->execute(array($id,$type,$startdate,$enddate,$repeating,$summary,$data,$uri,time()));
+
+		self::touchCalendar($id);
+
+		return OC_DB::insertid();
+	}
+
+	public static function editCalendarObject($id, $data){
+		$oldobject = self::findCard($id);
+		
+		$object = Sabre_VObject_Reader::read($data);
+		list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
+
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*calendar_objects SET objecttype=?,startdate=?,enddate=?,repeating=?,summary=?,calendardata=?, lastmodified = ? WHERE id = ?' );
+		$result = $stmt->execute(array($type,$startdate,$enddate,$repeating,$summary,$data,time(),$id));
+
+		self::touchCalendar($id);
+
+		return true;
+	}
+
+	public static function editCalendarObjectFromDAVData($cid,$uri,$data){
+		$oldobject = self::findCardWhereDAVDataIs($cid,$uri);
+		
+		$object = Sabre_VObject_Reader::read($data);
+		list($type,$startdate,$enddate,$summary,$repeating,$uid) = self::extractData($object);
+
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*calendar_objects SET objecttype=?,startdate=?,enddate=?,repeating=?,summary=?,calendardata=?, lastmodified = ? WHERE id = ?' );
+		$result = $stmt->execute(array($type,$startdate,$enddate,$repeating,$summary,$data,time(),$oldobject['id']));
+
+		self::touchCalendar($oldobject['calendarid']);
+
+		return true;
+	}
+	
+	public static function deleteCalendarObject($id){
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*calendar_objects WHERE id = ?' );
+		$stmt->execute(array($id));
+
+		return true;
+	}
+
+	public static function deleteCalendarObjectFromDAVData($cid,$uri){
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*calendar_objects WHERE calendarid = ? AND uri=?' );
+		$stmt->execute(array($cid,$uri));
+
+		return true;
+	}
+	
+	public static function createURI($name,$existing){
+		$name = strtolower($name);
+		$newname = $name;
+		$i = 1;
+		while(in_array($newname,$existing)){
+			$newname = $name.$i;
+			$i = $i + 1;
+		}
+		return $newname;
+	}
+
+	public static function createUID(){
+		return substr(md5(rand().time()),0,10);
+	}
+	
+	public static function extractUserID($principaluri){
+		list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri);
+		return $userid;
+	}
+
+	protected static function extractData($object){
+		$return = array('',null,null,'',0,null);
+		
+		// Child to use
+		$children = 0;
+		$use = null;
+		foreach($object->children as &$property){
+			if($property->name == 'VEVENT'){
+				$children++;
+				$thisone = true;
+
+				foreach($property->children as &$element){
+					if($element->name == 'RECURRENCE-ID'){
+						$thisone = false;
+					}
+				} unset($element);
+
+				if($thisone){
+					$use = $property;
+				}
+			}
+			elseif($property->name == 'VTODO' || $property->name == 'VJOURNAL'){
+				$return[0] = $use->name;
+				foreach($property->children as &$element){
+					if($property->name == 'SUMMARY'){
+						$return[3] = $property->value;
+					}
+					elseif($property->name == 'UID'){
+						$return[5] = $property->value;
+					}
+				};
+
+				// Only one VTODO or VJOURNAL per object
+				// (only one UID per object but a UID is required by a VTODO =>
+				//    one VTODO per object)
+				break;
+			}
+		} unset($property);
+		
+		// find the data
+		if(!is_null($use)){
+			$return[0] = $use->name;
+			foreach($use->children as &$property){
+				if($property->name == 'DTSTART'){
+					$return[1] = self::getUTCforMDB($property->getDateTime());
+				}
+				elseif($property->name == 'DTEND'){
+					$return[2] = self::getUTCforMDB($property->getDateTime());
+				}
+				elseif($property->name == 'SUMMARY'){
+					$return[3] = $property->value;
+				}
+				elseif($property->name == 'RRULE'){
+					$return[4] = 1;
+				}
+				elseif($property->name == 'UID'){
+					$return[5] = $property->value;
+				}
+			} unset($property);
+		}
+		
+		// More than one child means reoccuring!
+		if($children > 1){
+			$return[4] = 1;
+		}
+		return $return;
+	}
+	
+	/**
+	 * @brief DateTime to UTC string
+	 * @param DateTime $datetime The date to convert
+	 * @returns date as YYYY-MM-DD hh:mm
+	 *
+	 * This function creates a date string that can be used by MDB2.
+	 * Furthermore it converts the time to UTC.
+	 */
+	protected static function getUTCforMDB($datetime){
+		return date('Y-m-d H:i', $datetime->format('U') - $datetime->getOffset());
+	}
+}
diff --git a/apps/calendar/lib/connector_sabre.php b/apps/calendar/lib/connector_sabre.php
new file mode 100755
index 0000000000000000000000000000000000000000..aff1941688f5b52d86bd5fcf3b28a3b9d5312622
--- /dev/null
+++ b/apps/calendar/lib/connector_sabre.php
@@ -0,0 +1,311 @@
+<?php
+
+class OC_Connector_Sabre_CalDAV extends Sabre_CalDAV_Backend_Abstract {
+	/**
+	 * List of CalDAV properties, and how they map to database fieldnames
+	 *
+	 * Add your own properties by simply adding on to this array
+	 *
+	 * @var array
+	 */
+	public $propertyMap = array(
+		'{DAV:}displayname'						  => 'displayname',
+		'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
+		'{urn:ietf:params:xml:ns:caldav}calendar-timezone'	=> 'timezone',
+		'{http://apple.com/ns/ical/}calendar-order'  => 'calendarorder',
+		'{http://apple.com/ns/ical/}calendar-color'  => 'calendarcolor',
+	);
+
+	/**
+	 * Returns a list of calendars for a principal.
+	 *
+	 * Every project is an array with the following keys:
+	 *  * id, a unique id that will be used by other functions to modify the
+	 *	calendar. This can be the same as the uri or a database key.
+	 *  * uri, which the basename of the uri with which the calendar is
+	 *	accessed.
+	 *  * principalUri. The owner of the calendar. Almost always the same as
+	 *	principalUri passed to this method.
+	 *
+	 * Furthermore it can contain webdav properties in clark notation. A very
+	 * common one is '{DAV:}displayname'.
+	 *
+	 * @param string $principalUri
+	 * @return array
+	 */
+	public function getCalendarsForUser($principalUri) {
+		$raw = OC_Calendar_Calendar::allCalendarsWherePrincipalURIIs($principalUri);
+		
+		$calendars = array();
+		foreach( $raw as $row ){
+			$components = explode(',',$row['components']);
+
+			$calendar = array(
+				'id' => $row['id'],
+				'uri' => $row['uri'],
+				'principaluri' => 'principals/'.$row['userid'],
+				'{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $row['ctag']?$row['ctag']:'0',
+				'{' . Sabre_CalDAV_Plugin::NS_CALDAV . '}supported-calendar-component-set' => new Sabre_CalDAV_Property_SupportedCalendarComponentSet($components),
+			);
+	
+			foreach($this->propertyMap as $xmlName=>$dbName) {
+				$calendar[$xmlName] = $row[$dbName];
+			}
+
+			$calendars[] = $calendar;
+		}
+		return $calendars;
+	}
+
+	/**
+	 * Creates a new calendar for a principal.
+	 *
+	 * If the creation was a success, an id must be returned that can be used to reference
+	 * this calendar in other methods, such as updateCalendar
+	 *
+	 * @param string $principalUri
+	 * @param string $calendarUri
+	 * @param array $properties
+	 * @return mixed
+	 */
+	public function createCalendar($principalUri,$calendarUri, array $properties) {
+		$fieldNames = array(
+			'principaluri',
+			'uri',
+			'ctag',
+		);
+		$values = array(
+			':principaluri' => $principalUri,
+			':uri'		  => $calendarUri,
+			':ctag'		 => 1,
+		);
+
+		// Default value
+		$sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
+		$fieldNames[] = 'components';
+		if (!isset($properties[$sccs])) {
+			$values[':components'] = 'VEVENT,VTODO';
+		} else {
+			if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
+				throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
+			}
+			$values[':components'] = implode(',',$properties[$sccs]->getValue());
+		}
+
+		foreach($this->propertyMap as $xmlName=>$dbName) {
+			if (isset($properties[$xmlName])) {
+
+				$myValue = $properties[$xmlName];
+				$values[':' . $dbName] = $properties[$xmlName];
+				$fieldNames[] = $dbName;
+			}
+		}
+
+		if(!isset($newValues['displayname'])) $newValues['displayname'] = 'unnamed';
+		if(!isset($newValues['description'])) $newValues['description'] = '';
+		if(!isset($newValues['components'])) $newValues['components'] = 'VEVENT,VTODO';
+		if(!isset($newValues['timezone'])) $newValues['timezone'] = null;
+		if(!isset($newValues['calendarorder'])) $newValues['calendarorder'] = 0;
+		if(!isset($newValues['calendarcolor'])) $newValues['calendarcolor'] = null;
+		
+		return OC_Calendar_Calendar::addCalendarFromDAVData($principalUri,$calendarUri,$newValues['displayname'],$newValues['description'],$newValues['components'],$newValues['timezone'],$newValues['calendarorder'],$newValues['calendarcolor']);
+	}
+
+	/**
+	 * Updates a calendars properties
+	 *
+	 * The properties array uses the propertyName in clark-notation as key,
+	 * and the array value for the property value. In the case a property
+	 * should be deleted, the property value will be null.
+	 *
+	 * This method must be atomic. If one property cannot be changed, the
+	 * entire operation must fail.
+	 *
+	 * If the operation was successful, true can be returned.
+	 * If the operation failed, false can be returned.
+	 *
+	 * Deletion of a non-existant property is always succesful.
+	 *
+	 * Lastly, it is optional to return detailed information about any
+	 * failures. In this case an array should be returned with the following
+	 * structure:
+	 *
+	 * array(
+	 *   403 => array(
+	 *	  '{DAV:}displayname' => null,
+	 *   ),
+	 *   424 => array(
+	 *	  '{DAV:}owner' => null,
+	 *   )
+	 * )
+	 *
+	 * In this example it was forbidden to update {DAV:}displayname.
+	 * (403 Forbidden), which in turn also caused {DAV:}owner to fail
+	 * (424 Failed Dependency) because the request needs to be atomic.
+	 *
+	 * @param string $calendarId
+	 * @param array $properties
+	 * @return bool|array
+	 */
+	public function updateCalendar($calendarId, array $properties) {
+
+		$newValues = array();
+		$result = array(
+			200 => array(), // Ok
+			403 => array(), // Forbidden
+			424 => array(), // Failed Dependency
+		);
+
+		$hasError = false;
+
+		foreach($properties as $propertyName=>$propertyValue) {
+
+			// We don't know about this property.
+			if (!isset($this->propertyMap[$propertyName])) {
+				$hasError = true;
+				$result[403][$propertyName] = null;
+				unset($properties[$propertyName]);
+				continue;
+			}
+
+			$fieldName = $this->propertyMap[$propertyName];
+			$newValues[$fieldName] = $propertyValue;
+	
+		}
+
+		// If there were any errors we need to fail the request
+		if ($hasError) {
+			// Properties has the remaining properties
+			foreach($properties as $propertyName=>$propertyValue) {
+				$result[424][$propertyName] = null;
+			}
+
+			// Removing unused statuscodes for cleanliness
+			foreach($result as $status=>$properties) {
+				if (is_array($properties) && count($properties)===0) unset($result[$status]);
+			}
+
+			return $result;
+
+		}
+
+		// Success
+		if(!isset($newValues['displayname'])) $newValues['displayname'] = null;
+		if(!isset($newValues['description'])) $newValues['description'] = null;
+		if(!isset($newValues['timezone'])) $newValues['timezone'] = null;
+		if(!isset($newValues['calendarorder'])) $newValues['calendarorder'] = null;
+		if(!isset($newValues['calendarcolor'])) $newValues['calendarcolor'] = null;
+
+		OC_Calendar_Calendar::editCalendar($calendarId,$newValues['displayname'],$newValues['description'],null,$newValues['timezone'],$newValues['calendarorder'],$newValues['calendarcolor']);
+
+		return true;
+
+	}
+
+	/**
+	 * Delete a calendar and all it's objects
+	 *
+	 * @param string $calendarId
+	 * @return void
+	 */
+	public function deleteCalendar($calendarId) {
+		OC_Calendar_Calendar::deleteCalendar($calendarId);
+	}
+
+	/**
+	 * Returns all calendar objects within a calendar object.
+	 *
+	 * Every item contains an array with the following keys:
+	 *   * id - unique identifier which will be used for subsequent updates
+	 *   * calendardata - The iCalendar-compatible calnedar data
+	 *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
+	 *   * lastmodified - a timestamp of the last modification time
+	 *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
+	 *   '  "abcdef"')
+	 *   * calendarid - The calendarid as it was passed to this function.
+	 *
+	 * Note that the etag is optional, but it's highly encouraged to return for
+	 * speed reasons.
+	 *
+	 * The calendardata is also optional. If it's not returned
+	 * 'getCalendarObject' will be called later, which *is* expected to return
+	 * calendardata.
+	 *
+	 * @param string $calendarId
+	 * @return array
+	 */
+	public function getCalendarObjects($calendarId) {
+		$data = array();
+		foreach(OC_Calendar_Calendar::allCalendarObjects($calendarId) as $row){
+			$data[] = $this->OCAddETag($row);
+		}
+		return $data;
+	}
+
+	/**
+	 * Returns information from a single calendar object, based on it's object
+	 * uri.
+	 *
+	 * The returned array must have the same keys as getCalendarObjects. The
+	 * 'calendardata' object is required here though, while it's not required
+	 * for getCalendarObjects.
+	 *
+	 * @param string $calendarId
+	 * @param string $objectUri
+	 * @return array
+	 */
+	public function getCalendarObject($calendarId,$objectUri) {
+		$data = OC_Calendar_Calendar::findCalendarObjectWhereDAVDataIs($calendarId,$objectUri);
+		if(is_array($data)){
+			$data = $this->OCAddETag($data);
+		}
+		return $data;
+	}
+
+	/**
+	 * Creates a new calendar object.
+	 *
+	 * @param string $calendarId
+	 * @param string $objectUri
+	 * @param string $calendarData
+	 * @return void
+	 */
+	public function createCalendarObject($calendarId,$objectUri,$calendarData) {
+		OC_Calendar_Calendar::addCalendarObjectFromDAVData($calendarId,$objectUri,$calendarData);
+	}
+
+	/**
+	 * Updates an existing calendarobject, based on it's uri.
+	 *
+	 * @param string $calendarId
+	 * @param string $objectUri
+	 * @param string $calendarData
+	 * @return void
+	 */
+	public function updateCalendarObject($calendarId,$objectUri,$calendarData){
+		OC_Calendar_Calendar::editCalendarObjectFromDAVData($calendarId,$objectUri,$calendarData);
+	}
+
+	/**
+	 * Deletes an existing calendar object.
+	 *
+	 * @param string $calendarId
+	 * @param string $objectUri
+	 * @return void
+	 */
+	public function deleteCalendarObject($calendarId,$objectUri){
+		OC_Calendar_Calendar::deleteCalendarObjectFromDAVData($calendarID,$objectUri);
+	}
+	
+	/**
+	 * @brief Creates a etag
+	 * @param array $row Database result
+	 * @returns associative array
+	 *
+	 * Adds a key "etag" to the row
+	 */
+	private function OCAddETag($row){
+		$row['etag'] = '"'.md5($row['calendarid'].$row['uri'].$row['calendardata'].$row['lastmodified']).'"';
+		return $row;
+	}
+}
diff --git a/apps/calendar/lib/hooks.php b/apps/calendar/lib/hooks.php
new file mode 100755
index 0000000000000000000000000000000000000000..5c446102b2288a60a935c39284927e5b98952c82
--- /dev/null
+++ b/apps/calendar/lib/hooks.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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 contains all hooks.
+ */
+class OC_Calendar_Hooks{
+	/**
+	 * @brief Deletes all Addressbooks of a certain user
+	 * @param paramters parameters from postDeleteUser-Hook
+	 * @return array
+	 */
+	public function deleteUser($parameters) {
+		$calendars = OC_Calendar_Calendar::allCalendars($parameters['uid']);
+		
+		foreach($calendars as $calendar) {
+			OC_Calendar_Calendar::deleteCalendar($calendar['id']);
+		}
+
+		return true;
+	}
+}
diff --git a/apps/calendar/settings.php b/apps/calendar/settings.php
new file mode 100755
index 0000000000000000000000000000000000000000..bdf997eef70e9cbddc17f665c33b7fccffd5c7b9
--- /dev/null
+++ b/apps/calendar/settings.php
@@ -0,0 +1,10 @@
+<?php
+
+$tmpl = new OC_Template( 'calendar', 'settings');
+$timezone=OC_Preferences::getValue(OC_User::getUser(),'calendar','timezone','');
+$tmpl->assign('timezone',$timezone);
+$tmpl->assign('timezones',DateTimeZone::listIdentifiers());
+
+OC_Util::addScript('calendar','settings');
+
+return $tmpl->fetchPage();
diff --git a/apps/calendar/templates/calendar.php b/apps/calendar/templates/calendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..8690d7a45d2e65c3ff2adf38e9415a8ab65b8146
--- /dev/null
+++ b/apps/calendar/templates/calendar.php
@@ -0,0 +1,953 @@
+				<script type="text/javascript">
+				var oc_cal_daylong = new Array("<?php echo $l -> t("Sunday");?>", "<?php echo $l -> t("Monday");?>", "<?php echo $l -> t("Tuesday");?>", "<?php echo $l -> t("Wednesday");?>", "<?php echo $l -> t("Thursday");?>", "<?php echo $l -> t("Friday");?>", "<?php echo $l -> t("Saturday");?>");
+				var oc_cal_dayshort = new Array("<?php echo $l -> t("Sun.");?>", "<?php echo $l -> t("Mon.");?>", "<?php echo $l -> t("Tue.");?>", "<?php echo $l -> t("Wed.");?>", "<?php echo $l -> t("Thu.");?>", "<?php echo $l -> t("Fri.");?>", "<?php echo $l -> t("Sat.");?>");
+				var oc_cal_monthlong = new Array("<?php echo $l -> t("January");?>", "<?php echo $l -> t("February");?>", "<?php echo $l -> t("March");?>", "<?php echo $l -> t("April");?>", "<?php echo $l -> t("May");?>", "<?php echo $l -> t("June");?>", "<?php echo $l -> t("July");?>", "<?php echo $l -> t("August");?>", "<?php echo $l -> t("September");?>", "<?php echo $l -> t("October");?>", "<?php echo $l -> t("November");?>", "<?php echo $l -> t("December");?>");
+				var oc_cal_monthshort = new Array("<?php echo $l -> t("Jan.");?>", "<?php echo $l -> t("Feb.");?>", "<?php echo $l -> t("Mar.");?>", "<?php echo $l -> t("Apr.");?>", "<?php echo $l -> t("May");?>", "<?php echo $l -> t("Jun.");?>", "<?php echo $l -> t("Jul.");?>", "<?php echo $l -> t("Aug.");?>", "<?php echo $l -> t("Sep.");?>", "<?php echo $l -> t("Oct.");?>", "<?php echo $l -> t("Nov.");?>", "<?php echo $l -> t("Dec.");?>");
+				var onedayview_radio = "1 <?php echo $l->t("Day");?>";
+				var oneweekview_radio = "1 <?php echo $l->t("Week");?>";
+				var fourweeksview_radio = "4 <?php echo $l->t("Weeks");?>";
+				var onemonthview_radio = "1 <?php echo $l->t("Month");?>";
+				var listview_radio = "<?php echo $l->t("Listview");?>";
+				var today_button_value = "<?php echo $l->t("Today");?>";
+				var choosecalendar_value = "<?php echo $l->t("Calendars");?>";
+				var cw_label = "<?php echo $l->t("CW");?>";
+				var cws_label = "<?php echo $l->t("CWs");?>";
+				</script>
+				<div id="sysbox"></div>
+				<div id="controls">
+					<div>
+						<form>
+							<div id="view">
+								<input type="button" value="1 Day" id="onedayview_radio" onclick="oc_cal_change_view('onedayview');"/>
+								<input type="button" value="1 Week" id="oneweekview_radio" onclick="oc_cal_change_view('oneweekview');"/>
+								<input type="button" value="4 Weeks" id="fourweeksview_radio" onclick="oc_cal_change_view('fourweeksview');"/>
+								<input type="button" value="1 Month" id="onemonthview_radio" onclick="oc_cal_change_view('onemonthview');"/>
+								<input type="button" value="Listview" id="listview_radio" onclick="oc_cal_change_view('listview');"/>
+							</div>
+						</form>
+						<form>
+							<div id="choosecalendar">
+								<input type="button" id="today_input" value="Today" onclick="oc_cal_switch2today();"/>
+								<input type="button" id="choosecalendar_input" value="Calendars" onclick="oc_cal_choosecalendar();" />
+							</div>
+						</form>
+						<form>
+							<div id="datecontrol">
+								<input type="button" value="&nbsp;&lt;&nbsp;" id="datecontrol_left" onclick="oc_cal_update_view('', 'backward');"/>
+								<input id="datecontrol_date" type="button" value=""/>
+								<input type="button" value="&nbsp;&gt;&nbsp;" id="datecontrol_left" onclick="oc_cal_update_view('', 'forward');"/>
+							</div>
+						</form>
+					</div>
+				</div>
+				<div id="calendar_holder">
+					<div id="onedayview">
+						<table>
+							<thead>
+								<tr>
+									<th class="calendar_time"><?php echo $l->t("Time");?></th>
+									<th id="onedayview_today" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title);"></th>
+								</tr>
+							</thead>
+							<tbody>
+								<tr>
+									<td class="calendar_time"><?php echo $l->t("All day");?></td>
+									<td id="onedayview_wholeday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, 'allday');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">00:00</td>
+									<td id="onedayview_0" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '0');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">01:00</td>
+									<td id="onedayview_1" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '1');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">02:00</td>
+									<td id="onedayview_2" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '2');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">03:00</td>
+									<td id="onedayview_3" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '3');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">04:00</td>
+									<td id="onedayview_4" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '4');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">05:00</td>
+									<td id="onedayview_5" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '5');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">06:00</td>
+									<td id="onedayview_6" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '6');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">07:00</td>
+									<td id="onedayview_7" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '7');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">08:00</td>
+									<td id="onedayview_8" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '8');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">09:00</td>
+									<td id="onedayview_9" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '9');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">10:00</td>
+									<td id="onedayview_10" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '10');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">11:00</td>
+									<td id="onedayview_11" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '11');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">12:00</td>
+									<td id="onedayview_12" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '12');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">13:00</td>
+									<td id="onedayview_13" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '13');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">14:00</td>
+									<td id="onedayview_14" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '14');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">15:00</td>
+									<td id="onedayview_15" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '15');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">16:00</td>
+									<td id="onedayview_16" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '16');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">17:00</td>
+									<td id="onedayview_17" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '17');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">18:00</td>
+									<td id="onedayview_18" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '18');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">19:00</td>
+									<td id="onedayview_19" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '19');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">20:00</td>
+									<td id="onedayview_20" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '20');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">21:00</td>
+									<td id="onedayview_21" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '21');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">22:00</td>
+									<td id="onedayview_22" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '22');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">23:00</td>
+									<td id="onedayview_23" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('onedayview_today').title, '23');"></td>
+								</tr>
+							</tbody>
+						</table>
+					</div>
+					<div id="oneweekview">
+						<table>
+							<thead>
+								<tr>
+									<th class="calendar_time"><?php echo $l->t("Time");?></th>
+									<th id="oneweekview_monday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title);"></th>
+									<th id="oneweekview_tuesday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title);"></th>
+									<th id="oneweekview_wednesday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title);"></th>
+									<th id="oneweekview_thursday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title);"></th>
+									<th id="oneweekview_friday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title);"></th>
+									<th id="oneweekview_saturday" class="weekend_thead" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title);"></th>
+									<th id="oneweekview_sunday" class="weekend_thead" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title);"></th>
+								</tr>
+							</thead>
+							<tbody>
+								<tr>
+									<td class="calendar_time"><?php echo $l->t("All day");?></td>
+									<td id="oneweekview_monday_allday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, 'allday');"></td>
+									<td id="oneweekview_tuesday_allday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, 'allday');"></td>
+									<td id="oneweekview_wednesday_allday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, 'allday');"></td>
+									<td id="oneweekview_thursday_allday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, 'allday');"></td>
+									<td id="oneweekview_friday_allday" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, 'allday');"></td>
+									<td id="oneweekview_saturday_allday" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, 'allday');"></td>
+									<td id="oneweekview_sunday_allday" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, 'allday');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">00:00</td>
+									<td id="oneweekview_monday_0" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '0');"></td>
+									<td id="oneweekview_tuesday_0" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '0');"></td>
+									<td id="oneweekview_wednesday_0" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '0');"></td>
+									<td id="oneweekview_thursday_0" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '0');"></td>
+									<td id="oneweekview_friday_0" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '0');"></td>
+									<td id="oneweekview_saturday_0" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '0');"></td>
+									<td id="oneweekview_sunday_0" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '0');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">01:00</td>
+									<td id="oneweekview_monday_1" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '1');"></td>
+									<td id="oneweekview_tuesday_1" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '1');"></td>
+									<td id="oneweekview_wednesday_1" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '1');"></td>
+									<td id="oneweekview_thursday_1" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '1');"></td>
+									<td id="oneweekview_friday_1" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '1');"></td>
+									<td id="oneweekview_saturday_1" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '1');"></td>
+									<td id="oneweekview_sunday_1" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '1');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">02:00</td>
+									<td id="oneweekview_monday_2" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '2');"></td>
+									<td id="oneweekview_tuesday_2" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '2');"></td>
+									<td id="oneweekview_wednesday_2" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '2');"></td>
+									<td id="oneweekview_thursday_2" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '2');"></td>
+									<td id="oneweekview_friday_2" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '2');"></td>
+									<td id="oneweekview_saturday_2" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '2');"></td>
+									<td id="oneweekview_sunday_2" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '2');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">03:00</td>
+									<td id="oneweekview_monday_3" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '3');"></td>
+									<td id="oneweekview_tuesday_3" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '3');"></td>
+									<td id="oneweekview_wednesday_3" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '3');"></td>
+									<td id="oneweekview_thursday_3" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '3');"></td>
+									<td id="oneweekview_friday_3" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '3');"></td>
+									<td id="oneweekview_saturday_3" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '3');"></td>
+									<td id="oneweekview_sunday_3" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '3');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">04:00</td>
+									<td id="oneweekview_monday_4" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '4');"></td>
+									<td id="oneweekview_tuesday_4" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '4');"></td>
+									<td id="oneweekview_wednesday_4" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '4');"></td>
+									<td id="oneweekview_thursday_4" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '4');"></td>
+									<td id="oneweekview_friday_4" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '4');"></td>
+									<td id="oneweekview_saturday_4" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '4');"></td>
+									<td id="oneweekview_sunday_4" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '4');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">05:00</td>
+									<td id="oneweekview_monday_5" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '5');"></td>
+									<td id="oneweekview_tuesday_5" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '5');"></td>
+									<td id="oneweekview_wednesday_5" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '5');"></td>
+									<td id="oneweekview_thursday_5" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '5');"></td>
+									<td id="oneweekview_friday_5" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '5');"></td>
+									<td id="oneweekview_saturday_5" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '5');"></td>
+									<td id="oneweekview_sunday_5" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '5');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">06:00</td>
+									<td id="oneweekview_monday_6" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '6');"></td>
+									<td id="oneweekview_tuesday_6" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '6');"></td>
+									<td id="oneweekview_wednesday_6" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '6');"></td>
+									<td id="oneweekview_thursday_6" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '6');"></td>
+									<td id="oneweekview_friday_6" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '6');"></td>
+									<td id="oneweekview_saturday_6" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '6');"></td>
+									<td id="oneweekview_sunday_6" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '6');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">07:00</td>
+									<td id="oneweekview_monday_7" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '7');"></td>
+									<td id="oneweekview_tuesday_7" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '7');"></td>
+									<td id="oneweekview_wednesday_7" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '7');"></td>
+									<td id="oneweekview_thursday_7" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '7');"></td>
+									<td id="oneweekview_friday_7" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '7');"></td>
+									<td id="oneweekview_saturday_7" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '7');"></td>
+									<td id="oneweekview_sunday_7" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '7');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">08:00</td>
+									<td id="oneweekview_monday_8" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '8');"></td>
+									<td id="oneweekview_tuesday_8" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '8');"></td>
+									<td id="oneweekview_wednesday_8" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '8');"></td>
+									<td id="oneweekview_thursday_8" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '8');"></td>
+									<td id="oneweekview_friday_8" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '8');"></td>
+									<td id="oneweekview_saturday_8" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '8');"></td>
+									<td id="oneweekview_sunday_8" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '8');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">09:00</td>
+									<td id="oneweekview_monday_9" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '9');"></td>
+									<td id="oneweekview_tuesday_9" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '9');"></td>
+									<td id="oneweekview_wednesday_9" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '9');"></td>
+									<td id="oneweekview_thursday_9" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '9');"></td>
+									<td id="oneweekview_friday_9" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '9');"></td>
+									<td id="oneweekview_saturday_9" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '9');"></td>
+									<td id="oneweekview_sunday_9" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '9');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">10:00</td>
+									<td id="oneweekview_monday_10" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '10');"></td>
+									<td id="oneweekview_tuesday_10" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '10');"></td>
+									<td id="oneweekview_wednesday_10" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '10');"></td>
+									<td id="oneweekview_thursday_10" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '10');"></td>
+									<td id="oneweekview_friday_10" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '10');"></td>
+									<td id="oneweekview_saturday_10" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '10');"></td>
+									<td id="oneweekview_sunday_10" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '10');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">11:00</td>
+									<td id="oneweekview_monday_11" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '11');"></td>
+									<td id="oneweekview_tuesday_11" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '11');"></td>
+									<td id="oneweekview_wednesday_11" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '11');"></td>
+									<td id="oneweekview_thursday_11" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '11');"></td>
+									<td id="oneweekview_friday_11" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '11');"></td>
+									<td id="oneweekview_saturday_11" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '11');"></td>
+									<td id="oneweekview_sunday_11" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '11');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">12:00</td>
+									<td id="oneweekview_monday_12" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '12');"></td>
+									<td id="oneweekview_tuesday_12" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '12');"></td>
+									<td id="oneweekview_wednesday_12" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '12');"></td>
+									<td id="oneweekview_thursday_12" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '12');"></td>
+									<td id="oneweekview_friday_12" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '12');"></td>
+									<td id="oneweekview_saturday_12" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '12');"></td>
+									<td id="oneweekview_sunday_12" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '12');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">13:00</td>
+									<td id="oneweekview_monday_13" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '13');"></td>
+									<td id="oneweekview_tuesday_13" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '13');"></td>
+									<td id="oneweekview_wednesday_13" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '13');"></td>
+									<td id="oneweekview_thursday_13" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '13');"></td>
+									<td id="oneweekview_friday_13" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '13');"></td>
+									<td id="oneweekview_saturday_13" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '13');"></td>
+									<td id="oneweekview_sunday_13" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '13');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">14:00</td>
+									<td id="oneweekview_monday_14" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '14');"></td>
+									<td id="oneweekview_tuesday_14" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '14');"></td>
+									<td id="oneweekview_wednesday_14" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '14');"></td>
+									<td id="oneweekview_thursday_14" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '14');"></td>
+									<td id="oneweekview_friday_14" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '14');"></td>
+									<td id="oneweekview_saturday_14" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '14');"></td>
+									<td id="oneweekview_sunday_14" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '14');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">15:00</td>
+									<td id="oneweekview_monday_15" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '15');"></td>
+									<td id="oneweekview_tuesday_15" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '15');"></td>
+									<td id="oneweekview_wednesday_15" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '15');"></td>
+									<td id="oneweekview_thursday_15" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '15');"></td>
+									<td id="oneweekview_friday_15" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '15');"></td>
+									<td id="oneweekview_saturday_15" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '15');"></td>
+									<td id="oneweekview_sunday_15" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '15');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">16:00</td>
+									<td id="oneweekview_monday_16" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '16');"></td>
+									<td id="oneweekview_tuesday_16" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '16');"></td>
+									<td id="oneweekview_wednesday_16" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '16');"></td>
+									<td id="oneweekview_thursday_16" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '16');"></td>
+									<td id="oneweekview_friday_16" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '16');"></td>
+									<td id="oneweekview_saturday_16" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '16');"></td>
+									<td id="oneweekview_sunday_16" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '16);"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">17:00</td>
+									<td id="oneweekview_monday_17" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '17');"></td>
+									<td id="oneweekview_tuesday_17" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '17');"></td>
+									<td id="oneweekview_wednesday_17" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '17');"></td>
+									<td id="oneweekview_thursday_17" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '17');"></td>
+									<td id="oneweekview_friday_17" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '17');"></td>
+									<td id="oneweekview_saturday_17" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '17');"></td>
+									<td id="oneweekview_sunday_17" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '17');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">18:00</td>
+									<td id="oneweekview_monday_18" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '18');"></td>
+									<td id="oneweekview_tuesday_18" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '18');"></td>
+									<td id="oneweekview_wednesday_18" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '18');"></td>
+									<td id="oneweekview_thursday_18" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '18');"></td>
+									<td id="oneweekview_friday_18" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '18');"></td>
+									<td id="oneweekview_saturday_18" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '18');"></td>
+									<td id="oneweekview_sunday_18" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '18');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">19:00</td>
+									<td id="oneweekview_monday_19" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '19');"></td>
+									<td id="oneweekview_tuesday_19" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '19');"></td>
+									<td id="oneweekview_wednesday_19" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '19');"></td>
+									<td id="oneweekview_thursday_19" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '19');"></td>
+									<td id="oneweekview_friday_19" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '19');"</td>
+									<td id="oneweekview_saturday_19" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '19');"></td>
+									<td id="oneweekview_sunday_19" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '19');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">20:00</td>
+									<td id="oneweekview_monday_20" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '20');"></td>
+									<td id="oneweekview_tuesday_20" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '20');"></td>
+									<td id="oneweekview_wednesday_20" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '20');"></td>
+									<td id="oneweekview_thursday_20" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '20');"></td>
+									<td id="oneweekview_friday_20" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '20');"></td>
+									<td id="oneweekview_saturday_20" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '20');"></td>
+									<td id="oneweekview_sunday_20" class="weekend_row"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">21:00</td>
+									<td id="oneweekview_monday_21" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '21');"></td>
+									<td id="oneweekview_tuesday_21" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '21');"></td>
+									<td id="oneweekview_wednesday_21" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '21');"></td>
+									<td id="oneweekview_thursday_21" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '21');"></td>
+									<td id="oneweekview_friday_21" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '21');"></td>
+									<td id="oneweekview_saturday_21" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '21');"></td>
+									<td id="oneweekview_sunday_21" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '21');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">22:00</td>
+									<td id="oneweekview_monday_22" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '22');"></td>
+									<td id="oneweekview_tuesday_22" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '22');"></td>
+									<td id="oneweekview_wednesday_22" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '22');"></td>
+									<td id="oneweekview_thursday_22" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '22');"></td>
+									<td id="oneweekview_friday_22" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '22');"></td>
+									<td id="oneweekview_saturday_22" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '22');"></td>
+									<td id="oneweekview_sunday_22" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '22');"></td>
+								</tr>
+								<tr>
+									<td class="calendar_time">23:00</td>
+									<td id="oneweekview_monday_23" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_monday').title, '23');"></td>
+									<td id="oneweekview_tuesday_23" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_tuesday').title, '23');"></td>
+									<td id="oneweekview_wednesday_23" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_wednesday').title, '23');"></td>
+									<td id="oneweekview_thursday_23" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_thursday').title, '23');"></td>
+									<td id="oneweekview_friday_23" class="calendar_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_friday').title, '23');"></td>
+									<td id="oneweekview_saturday_23" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_saturday').title, '23');"></td>
+									<td id="oneweekview_sunday_23" class="weekend_row" onclick="oc_cal_newevent(document.getElementById('oneweekview_sunday').title, '23');"></td>
+								</tr>
+							</tbody>
+						</table>
+					</div>
+					<div id="fourweeksview">
+						<table>
+							<thead>
+								<tr>
+									<th id="fourweeksview_calw_label" class="calendar_row"><?php echo $l -> t("CW");?></th>
+									<th id="fourweeksview_monday" class="calendar_row"><?php echo $l -> t("Monday");?></th>
+									<th id="fourweeksview_tuesday" class="calendar_row"><?php echo $l -> t("Tuesday");?></th>
+									<th id="fourweeksview_wednesday" class="calendar_row"><?php echo $l -> t("Wednesday");?></th>
+									<th id="fourweeksview_thursday" class="calendar_row"><?php echo $l -> t("Thursday");?></th>
+									<th id="fourweeksview_friday" class="calendar_row"><?php echo $l -> t("Friday");?></th>
+									<th id="fourweeksview_saturday" class="weekend_thead"><?php echo $l -> t("Saturday");?></th>
+									<th id="fourweeksview_sunday" class="weekend_thead"><?php echo $l -> t("Sunday");?></th>
+								</tr>
+							</thead>
+							<tbody>
+								<tr id="fourweeksview_week_1">
+									<td id="fourweeksview_calw1"></td>
+									<td id="fourweeksview_monday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_monday_1">
+									</div>
+									<div class="events" id="events_fourweeksview_monday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_monday_1').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_tuesday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_tuesday_1">
+									</div>
+									<div class="events" id="events_fourweeksview_tuesday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_tuesday_1').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_wednesday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_wednesday_1">
+									</div>
+									<div class="events" id="events_fourweeksview_wednesday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_wednesday_1').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_thursday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_thursday_1">
+									</div>
+									<div class="events" id="events_fourweeksview_thursday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_thursday_1').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_friday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_friday_1">
+									</div>
+									<div class="events" id="events_fourweeksview_friday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_friday_1').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_saturday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_saturday_1">
+									</div>
+									<div class="weekend" id="events_fourweeksview_saturday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_saturday_1').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_sunday_1" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_sunday_1">
+									</div>
+									<div class="weekend" id="events_fourweeksview_sunday_1" onclick="oc_cal_newevent(document.getElementById('fourweeksview_sunday_1').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="fourweeksview_week_2">
+									<td id="fourweeksview_calw2"></td>
+									<td id="fourweeksview_monday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_monday_2">
+									</div>
+									<div class="events" id="events_fourweeksview_monday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_monday_2').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_tuesday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_tuesday_2">
+									</div>
+									<div class="events" id="events_fourweeksview_tuesday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_tuesday_2').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_wednesday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_wednesday_2">
+									</div>
+									<div class="events" id="events_fourweeksview_wednesday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_wednesday_2').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_thursday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_thursday_2">
+									</div>
+									<div class="events" id="events_fourweeksview_thursday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_thursday_2').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_friday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_friday_2">
+									</div>
+									<div class="events" id="events_fourweeksview_friday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_friday_2').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_saturday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_saturday_2">
+									</div>
+									<div class="weekend" id="events_fourweeksview_saturday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_saturday_2').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_sunday_2" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_sunday_2">
+									</div>
+									<div class="weekend" id="events_fourweeksview_sunday_2" onclick="oc_cal_newevent(document.getElementById('fourweeksview_sunday_2').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="fourweeksview_week_3">
+									<td id="fourweeksview_calw3"></td>
+									<td id="fourweeksview_monday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_monday_3">
+									</div>
+									<div class="events" id="events_fourweeksview_monday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_monday_3').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_tuesday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_tuesday_3">
+									</div>
+									<div class="events" id="events_fourweeksview_tuesday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_tuesday_3').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_wednesday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_wednesday_3">
+									</div>
+									<div class="events" id="events_fourweeksview_wednesday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_wednesday_3').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_thursday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_thursday_3">
+									</div>
+									<div class="events" id="events_fourweeksview_thursday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_thursday_3').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_friday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_friday_3">
+									</div>
+									<div class="events" id="events_fourweeksview_friday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_friday_3').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_saturday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_saturday_3">
+									</div>
+									<div class="weekend" id="events_fourweeksview_saturday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_saturday_3').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_sunday_3" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_sunday_3">
+									</div>
+									<div class="weekend" id="events_fourweeksview_sunday_3" onclick="oc_cal_newevent(document.getElementById('fourweeksview_sunday_3').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="fourweeksview_week_4">
+									<td id="fourweeksview_calw4"></td>
+									<td id="fourweeksview_monday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_monday_4">
+									</div>
+									<div class="events" id="events_fourweeksview_monday_4" onclick="oc_cal_newevent(document.getElementById('fourweeksview_monday_4').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_tuesday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_tuesday_4">
+									</div>
+									<div class="events" id="events_fourweeksview_tuesday_4" onclick="oc_cal_newevent(document.getElementById('fourweeksview_tuesday_4').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_wednesday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_wednesday_4">
+									</div>
+									<div class="events" id="events_fourweeksview_wednesday_4" onclick="oc_cal_newevent(document.getElementById('fourweeksview_wednesday_4').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_thursday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_thursday_4">
+									</div>
+									<div class="events" id="events_fourweeksview_thursday_4" onclick="oc_cal_newevent(document.getElementById('fourweeksview_thursday_4').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_friday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_friday_4">
+									</div>
+									<div class="events" id="events_fourweeksview_friday_4" onclick="oc_cal_newevent(document.getElementById('fourweeksview_friday_4').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_saturday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_saturday_4">
+									</div>
+									<div class="weekend" id="events_fourweeksview_saturday_4" onclick="oc_cal_newevent(document.getElementById('fourweeksview_saturday_4').title)">
+									</div>
+									</td>
+									<td id="fourweeksview_sunday_4" class="fourweeksview_item">
+									<div class="dateinfo" id="dateinfo_fourweeksview_sunday_4">
+									</div>
+									<div class="weekend" id="events_fourweeksview_sunday_4"  onclick="oc_cal_newevent(document.getElementById('fourweeksview_sunday_4').title)">
+									</div>
+									</td>
+								</tr>
+							</tbody>
+						</table>
+					</div>
+					<div id="onemonthview">
+						<table>
+							<thead>
+								<tr>
+									<th id="onemonthview_monday" class="calendar_row"><?php echo $l -> t("Monday");?></th>
+									<th id="onemonthview_tuesday" class="calendar_row"><?php echo $l -> t("Tuesday");?></th>
+									<th id="onemonthview_wednesday" class="calendar_row"><?php echo $l -> t("Wednesday");?></th>
+									<th id="onemonthview_thursday" class="calendar_row"><?php echo $l -> t("Thursday");?></th>
+									<th id="onemonthview_friday" class="calendar_row"><?php echo $l -> t("Friday");?></th>
+									<th id="onemonthview_saturday" class="weekend_thead"><?php echo $l -> t("Saturday");?></th>
+									<th id="onemonthview_sunday" class="weekend_thead"><?php echo $l -> t("Sunday");?></th>
+								</tr>
+							</thead>
+							<tbody>
+								<tr id="onemonthview_week_1">
+									<td id="onemonthview_monday_1" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_monday_1">
+									</div>
+									<div class="events" id="events_onemonthview_monday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_monday_1').title)">
+									</div>
+									</td>
+									<td id="onemonthview_tuesday_1" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_tuesday_1">
+									</div>
+									<div class="events" id="events_onemonthview_tuesday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_tuesday_1').title)">
+									</div>
+									</td>
+									<td id="onemonthview_wednesday_1" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_wednesday_1">
+									</div>
+									<div class="events" id="events_onemonthview_wednesday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_wednesday_1').title)">
+									</div>
+									</td>
+									<td id="onemonthview_thursday_1" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_thursday_1">
+									</div>
+									<div class="events" id="events_onemonthview_thursday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_thursday_1').title)">
+									</div>
+									</td>
+									<td id="onemonthview_friday_1" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_friday_1">
+									</div>
+									<div class="events" id="events_onemonthview_friday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_friday_1').title)">
+									</div>
+									</td>
+									<td id="onemonthview_saturday_1" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_saturday_1">
+									</div>
+									<div class="weekend" id="events_onemonthview_saturday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_saturday_1').title)">
+									</div>
+									</td>
+									<td id="onemonthview_sunday_1" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_sunday_1">
+									</div>
+									<div class="weekend" id="events_onemonthview_sunday_1" onclick="oc_cal_newevent(document.getElementById('onemonthview_sunday_1').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="onemonthview_week_2">
+									<td id="onemonthview_monday_2" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_monday_2">
+									</div>
+									<div class="events" id="events_onemonthview_monday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_monday_2').title)">
+									</div>
+									</td>
+									<td id="onemonthview_tuesday_2" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_tuesday_2">
+									</div>
+									<div class="events" id="events_onemonthview_tuesday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_tuesday_2').title)">
+									</div>
+									</td>
+									<td id="onemonthview_wednesday_2" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_wednesday_2">
+									</div>
+									<div class="events" id="events_onemonthview_wednesday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_wednesday_2').title)">
+									</div>
+									</td>
+									<td id="onemonthview_thursday_2" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_thursday_2">
+									</div>
+									<div class="events" id="events_onemonthview_thursday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_thursday_2').title)">
+									</div>
+									</td>
+									<td id="onemonthview_friday_2" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_friday_2">
+									</div>
+									<div class="events" id="events_onemonthview_friday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_friday_2').title)">
+									</div>
+									</td>
+									<td id="onemonthview_saturday_2" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_saturday_2">
+									</div>
+									<div class="weekend" id="events_onemonthview_saturday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_saturday_2').title)">
+									</div>
+									</td>
+									<td id="onemonthview_sunday_2" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_sunday_2">
+									</div>
+									<div class="weekend" id="events_onemonthview_sunday_2" onclick="oc_cal_newevent(document.getElementById('onemonthview_sunday_2').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="onemonthview_week_3">
+									<td id="onemonthview_monday_3" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_monday_3">
+									</div>
+									<div class="events" id="events_onemonthview_monday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_monday_3').title)">
+									</div>
+									</td>
+									<td id="onemonthview_tuesday_3" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_tuesday_3">
+									</div>
+									<div class="events" id="events_onemonthview_tuesday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_tuesday_3').title)">
+									</div>
+									</td>
+									<td id="onemonthview_wednesday_3" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_wednesday_3">
+									</div>
+									<div class="events" id="events_onemonthview_wednesday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_wednesday_3').title)">
+									</div>
+									</td>
+									<td id="onemonthview_thursday_3" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_thursday_3">
+									</div>
+									<div class="events" id="events_onemonthview_thursday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_thursday_3').title)">
+									</div>
+									</td>
+									<td id="onemonthview_friday_3" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_friday_3">
+									</div>
+									<div class="events" id="events_onemonthview_friday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_friday_3').title)">
+									</div>
+									</td>
+									<td id="onemonthview_saturday_3" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_saturday_3">
+									</div>
+									<div class="weekend" id="events_onemonthview_saturday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_saturday_3').title)">
+									</div>
+									</td>
+									<td id="onemonthview_sunday_3" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_sunday_3">
+									</div>
+									<div class="weekend" id="events_onemonthview_sunday_3" onclick="oc_cal_newevent(document.getElementById('onemonthview_sunday_3').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="onemonthview_week_4">
+									<td id="onemonthview_monday_4" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_monday_4">
+									</div>
+									<div class="events" id="events_onemonthview_monday_4" onclick="oc_cal_newevent(document.getElementById('onemonthview_monday_4').title)">
+									</div>
+									</td>
+									<td id="onemonthview_tuesday_4" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_tuesday_4">
+									</div>
+									<div class="events" id="events_onemonthview_tuesday_4" onclick="oc_cal_newevent(document.getElementById('onemonthview_tuesday_4').title)">
+									</div>
+									</td>
+									<td id="onemonthview_wednesday_4" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_wednesday_4">
+									</div>
+									<div class="events" id="events_onemonthview_wednesday_4" onclick="oc_cal_newevent(document.getElementById('onemonthview_wednesday_4').title)">
+									</div>
+									</td>
+									<td id="onemonthview_thursday_4" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_thursday_4">
+									</div>
+									<div class="events" id="events_onemonthview_thursday_4" onclick="oc_cal_newevent(document.getElementById('onemonthview_thursday_4').title)">
+									</div>
+									</td>
+									<td id="onemonthview_friday_4" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_friday_4">
+									</div>
+									<div class="events" id="events_onemonthview_friday_4" onclick="oc_cal_newevent(document.getElementById('onemonthview_friday_4').title)">
+									</div>
+									</td>
+									<td id="onemonthview_saturday_4" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_saturday_4">
+									</div>
+									<div class="weekend" id="events_onemonthview_saturday_4" onclick="oc_cal_newevent(document.getElementById('onemonthview_saturday_4').title)">
+									</div>
+									</td>
+									<td id="onemonthview_sunday_4" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_sunday_4">
+									</div>
+									<div class="weekend" id="events_onemonthview_sunday_4"  onclick="oc_cal_newevent(document.getElementById('onemonthview_sunday_4').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="onemonthview_week_5">
+									<td id="onemonthview_monday_5" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_monday_5">
+									</div>
+									<div class="events" id="events_onemonthview_monday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_monday_5').title)">
+									</div>
+									</td>
+									<td id="onemonthview_tuesday_5" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_tuesday_5">
+									</div>
+									<div class="events" id="events_onemonthview_tuesday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_tuesday_5').title)">
+									</div>
+									</td>
+									<td id="onemonthview_wednesday_5" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_wednesday_5">
+									</div>
+									<div class="events" id="events_onemonthview_wednesday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_wednesday_5').title)">
+									</div>
+									</td>
+									<td id="onemonthview_thursday_5" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_thursday_5">
+									</div>
+									<div class="events" id="events_onemonthview_thursday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_thursday_5').title)">
+									</div>
+									</td>
+									<td id="onemonthview_friday_5" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_friday_5">
+									</div>
+									<div class="events" id="events_onemonthview_friday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_friday_5').title)">
+									</div>
+									</td>
+									<td id="onemonthview_saturday_5" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_saturday_5">
+									</div>
+									<div class="weekend" id="events_onemonthview_saturday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_saturday_5').title)">
+									</div>
+									</td>
+									<td id="onemonthview_sunday_5" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_sunday_5">
+									</div>
+									<div class="weekend" id="events_onemonthview_sunday_5" onclick="oc_cal_newevent(document.getElementById('onemonthview_sunday_5').title)">
+									</div>
+									</td>
+								</tr>
+								<tr id="onemonthview_week_6">
+									<td id="onemonthview_monday_6" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_monday_6">
+									</div>
+									<div class="events" id="events_onemonthview_monday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_monday_6').title)">
+									</div>
+									</td>
+									<td id="onemonthview_tuesday_6" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_tuesday_6">
+									</div>
+									<div class="events" id="events_onemonthview_tuesday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_tuesday_6').title)">
+									</div>
+									</td>
+									<td id="onemonthview_wednesday_6" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_wednesday_6">
+									</div>
+									<div class="events" id="events_onemonthview_wednesday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_wednesday_6').title)">
+									</div>
+									</td>
+									<td id="onemonthview_thursday_6" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_thursday_6">
+									</div>
+									<div class="events" id="events_onemonthview_thursday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_thursday_6').title)">
+									</div>
+									</td>
+									<td id="onemonthview_friday_6" class="onemonthview_item">
+									<div class="dateinfo" id="dateinfo_onemonthview_friday_6">
+									</div>
+									<div class="events" id="events_onemonthview_friday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_friday_6').title)">
+									</div>
+									</td>
+									<td id="onemonthview_saturday_6" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_saturday_6">
+									</div>
+									<div class="weekend" id="events_onemonthview_saturday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_saturday_6').title)">
+									</div>
+									</td>
+									<td id="onemonthview_sunday_6" class="weekend">
+									<div class="dateinfo" id="dateinfo_onemonthview_sunday_6">
+									</div>
+									<div class="weekend" id="events_onemonthview_sunday_6" onclick="oc_cal_newevent(document.getElementById('onemonthview_sunday_6').title)">
+									</div>
+									</td>
+								</tr>
+							</tbody>
+						</table>
+					</div>
+					<div id="listview">
+						
+					</div>
+				</div>
+				<!-- Dialogs -->
+				<div id="dialog_holder"></div>
+				<div id="parsingfail_dialog" title="Parsing Fail">
+					<?php echo $l->t("There was a fail, while parsing the file."); ?>
+				</div>
+				<!-- End of Dialogs -->
+				<script type="text/javascript">
+				//sending ajax request on every change view and use last view as default on the next
+				<?php
+				if(OC_Preferences::getValue(OC_USER::getUser(), "calendar", "currentview") == ""){
+					echo "var oc_cal_currentview = \"onemonthview\";";
+				}else{
+					echo "var oc_cal_currentview = \"" . OC_Preferences::getValue(OC_USER::getUser(), "calendar", "currentview") . "\";";
+				}
+				 
+				?>
+				document.getElementById(oc_cal_currentview).style.display = "block";
+				document.getElementById(oc_cal_currentview + "_radio").style.color = "#0098E4";
+				oc_cal_update_view(oc_cal_currentview);
+				function oc_cal_change_view(view, task){
+					document.getElementById(oc_cal_currentview).style.display = "none";
+					document.getElementById(oc_cal_currentview + "_radio").style.color = "#000000";
+					document.getElementById(view).style.display = "block";
+					oc_cal_currentview = view;
+					document.getElementById(oc_cal_currentview + "_radio").style.color = "#0098E4";
+					oc_cal_update_view(view, task);
+				}
+				document.getElementById("onedayview_radio").value = onedayview_radio;
+				document.getElementById("oneweekview_radio").value = oneweekview_radio;
+				document.getElementById("fourweeksview_radio").value = fourweeksview_radio;
+				document.getElementById("onemonthview_radio").value = onemonthview_radio;
+				document.getElementById("listview_radio").value = listview_radio;
+				document.getElementById("today_input").value = today_button_value;
+				document.getElementById("choosecalendar_input").value = choosecalendar_value;
+				//document.getElementById("download_input").src = oc_webroot + "/core/img/actions/download.svg";
+				</script>
+				<script type="text/javascript" id="js_events">
+				<?php
+				//
+				
+				
+				
+				
+				
+				
+				?>
+				</script>
\ No newline at end of file
diff --git a/apps/calendar/templates/part.choosecalendar.php b/apps/calendar/templates/part.choosecalendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..30866270ac5ab8202ba671389f9986f01ba44559
--- /dev/null
+++ b/apps/calendar/templates/part.choosecalendar.php
@@ -0,0 +1,26 @@
+<div id="choosecalendar_dialog" title="<?php echo $l->t("Choose active calendars"); ?>">
+<table width="100%" style="border: 0;">
+<?php
+$option_calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
+for($i = 0; $i < count($option_calendars); $i++){
+	echo "<tr>";
+	$tmpl = new OC_Template('calendar', 'part.choosecalendar.rowfields');
+	$tmpl->assign('calendar', $option_calendars[$i]);
+	$tmpl->printpage();
+	echo "</tr>";
+}
+?>
+</table>
+<script type="text/javascript">
+	$( "#choosecalendar_dialog" ).dialog({
+		width : 500,
+		close : function() {
+					oc_cal_opendialog = 0;
+					var lastchild = document.getElementById("body-user").lastChild
+					while(lastchild.id != "lightbox"){
+						document.getElementById("body-user").removeChild(lastchild);
+						lastchild = document.getElementById("body-user").lastChild;
+					}
+			}
+	});
+</script> 
diff --git a/apps/calendar/templates/part.choosecalendar.rowfields.php b/apps/calendar/templates/part.choosecalendar.rowfields.php
new file mode 100755
index 0000000000000000000000000000000000000000..ebe1ca0b13711aa37cec1ecba7807504fc7faf0b
--- /dev/null
+++ b/apps/calendar/templates/part.choosecalendar.rowfields.php
@@ -0,0 +1,4 @@
+<?php
+	echo "<td width=\"20px\"><input id=\"active_" . $_['calendar']["id"] . "\" type=\"checkbox\" onClick=\"oc_cal_calender_activation(this, " . $_['calendar']["id"] . ")\"" . ($_['calendar']["active"] ? ' checked="checked"' : '') . "></td>";
+	echo "<td><label for=\"active_" . $_['calendar']["id"] . "\">" . $_['calendar']["displayname"] . "</label></td>";
+	echo "<td width=\"20px\"><a style=\"display: block; opacity: 0.214133;\" href=\"#\" title=\"" . $l->t("Download") . "\" class=\"action\"><img src=\"/owncloud/core/img/actions/download.svg\"></a></td><td width=\"20px\"><a style=\"display: block; opacity: 0.214133;\" href=\"#\" title=\"" . $l->t("Edit") . "\" class=\"action\" onclick=\"oc_cal_editcalendar(this, " . $_['calendar']["id"] . ");\"><img src=\"/owncloud/core/img/actions/rename.svg\"></a></td>";
diff --git a/apps/calendar/templates/part.editcalendar.php b/apps/calendar/templates/part.editcalendar.php
new file mode 100755
index 0000000000000000000000000000000000000000..d6f4e9f16fff312611f8a9ea45f07bb31519d4e9
--- /dev/null
+++ b/apps/calendar/templates/part.editcalendar.php
@@ -0,0 +1,32 @@
+<td id="editcalendar_dialog" title="<?php echo $l->t("Edit calendar"); ?>" colspan="4">
+<table width="100%" style="border: 0;">
+<tr>
+	<th><?php echo $l->t('Displayname') ?></th>
+	<td>
+		<input id="displayname_<?php echo $_['calendar']['id'] ?>" type="text" value="<?php echo $_['calendar']['displayname'] ?>">
+	</td>
+</tr>
+<tr>
+	<td></td>
+	<td>
+		<input id="active_<?php echo $_['calendar']['id'] ?>" type="checkbox"<?php echo ($_['calendar']['active'] ? ' checked="checked"' : '' ) ?>>
+		<label for="active_<?php echo $_['calendar']['id'] ?>">
+			<?php echo $l->t('Active') ?>
+		</label>
+	</td>
+</tr>
+<tr>
+	<th><?php echo $l->t('Description') ?></th>
+	<td>
+		<textarea id="description_<?php echo $_['calendar']['id'] ?>"><?php echo $_['calendar']['description'] ?></textarea>
+	</td>
+</tr>
+<tr>
+	<th><?php echo $l->t('Calendar color') ?></th>
+	<td>
+		<input id="calendarcolor_<?php echo $_['calendar']['id'] ?>" type="text" value="<?php echo $_['calendar']['calendarcolor'] ?>">
+	</td>
+</tr>
+</table>
+<input style="float: left;" type="button" onclick="oc_cal_editcalendar_submit(this, <?php echo $_['calendar']['id'] ?>);" value="<?php echo $l->t("Submit"); ?>">
+</td>
diff --git a/apps/calendar/templates/part.editevent.php b/apps/calendar/templates/part.editevent.php
new file mode 100755
index 0000000000000000000000000000000000000000..8a85652442c87331f65b92874decb97a5cd33e0c
--- /dev/null
+++ b/apps/calendar/templates/part.editevent.php
@@ -0,0 +1,133 @@
+<div id="editevent" title="<?php echo $l -> t("Edit an event");?>">
+	<table id="editevent_table" width="100%">
+		<tr>
+			<td width="75px"><?php echo $l -> t("Title");?>:</td>
+			<td>
+			<input type="text" style="width:350px;" size="100" placeholder="Title of the Event"  maxlength="100" />
+			</td>
+		</tr>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Location");?>:</td>
+			<td>
+			<input type="text" style="width:350px;" size="100" placeholder="Location of the Event"  maxlength="100" />
+			</td>
+		</tr>
+	</table>
+	<table>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Category");?>:</td>
+			<td>
+			<select class="formselect" id="formcategorie_select" style="width:140px;">
+				<option>Coming soon</option><!--
+				<option>Work</option>
+				<option>Call</option>-->
+			</select></td>
+			<td width="75px">&nbsp;&nbsp;&nbsp;<?php echo $l -> t("Calendar");?>:</td>
+			<td>
+			<select class="formselect" id="formcalendar_select" style="width:140px;">
+				<option>Coming soon</option><!--
+				<option>Calendar 1</option>
+				<option>Calendar 2</option>
+				<option>Calendar 3</option>-->
+			</select></td>
+		</tr>
+	</table>
+	<hr>
+	<table>
+		<tr>
+			<td width="75px"></td>
+			<td>
+			<input onclick="lock_time();" type="checkbox" id="newcalendar_allday_checkbox">
+			<label for="newcalendar_allday_checkbox"><?php echo $l -> t("All Day Event");?></label></td>
+		</tr>
+		<tr>
+			<?php $day = substr($_GET["d"], 0, 2);
+			$month = substr($_GET["d"], 2, 2);
+			$year = substr($_GET["d"], 4, 4);
+			?>
+			<td width="75px"><?php echo $l -> t("From");?>:</td>
+			<td>
+			<input type="text" value="<?php echo $day . "-" . $month . "-" . $year;?>" id="from">
+			&nbsp;&nbsp;
+			<input type="time" value="<?php echo date("H:i");?>" id="fromtime">
+			</td><!--use jquery-->
+		</tr>
+		<tr>
+			<?php
+			if(date("H") == 23) {$day++;
+				$time = 0;
+			} else {$time = date("H") + 1;
+			}
+			?>
+			<td width="75px"><?php echo $l -> t("To");?>:</td>
+			<td>
+			<input type="text" value="<?php echo $day . "-" . $month . "-" . $year;?>" id="to">
+			&nbsp;&nbsp;
+			<input type="time" value="<?php echo $time . date(":i");?>" id="totime">
+			</td><!--use jquery-->
+		</tr>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Repeat");?>:</td>
+			<td>
+			<select class="formselect" id="formrepeat_select" style="width:350px;">
+				<option id="doesnotrepeat" selected="selected">Does not repeat</option>
+				<option>Daily</option>
+				<option>Weekly</option>
+				<option>Every Weekday</option>
+				<option>Bi-Weekly</option>
+				<option>Monthly</option>
+				<option>Yearly</option>
+			</select></td>
+		</tr>
+	</table>
+	<hr>
+	<table>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Attendees");?>:</td>
+			<td style="height: 50px;"></td>
+		</tr>
+	</table>
+	<hr>
+	<table>
+		<tr>
+			<td width="75px" style="vertical-align: top;"><?php echo $l -> t("Description");?>:</td>
+			<td>			<textarea style="width:350px;height: 150px;"placeholder="Description of the Event"></textarea></td>
+		</tr>
+	</table>
+	<span id="editevent_actions">
+		<input type="button" style="float: left;" value="<?php echo $l -> t("Submit");?>">
+	</span>
+</div>
+<script type="text/javascript">
+	$( "#editevent" ).dialog({
+		width : 500,
+		close : function() {
+					oc_cal_opendialog = 0;
+					var lastchild = document.getElementById("body-user").lastChild
+					while(lastchild.id != "lightbox"){
+						document.getElementById("body-user").removeChild(lastchild);
+						lastchild = document.getElementById("body-user").lastChild;
+					}
+			},
+		open : function(){alert("Doesn't work yet.");}
+	});
+	$( "#from" ).datepicker({
+		dateFormat : 'dd-mm-yy'
+	});
+	$( "#to" ).datepicker({
+		dateFormat : 'dd-mm-yy'
+	});
+	function lock_time() {
+		if(document.getElementById("totime").disabled == true) {
+			document.getElementById("fromtime").disabled = false;
+			document.getElementById("totime").disabled = false;
+			document.getElementById("fromtime").style.color = "#333";
+			document.getElementById("totime").style.color = "#333";
+		} else {
+			document.getElementById("fromtime").disabled = true;
+			document.getElementById("totime").disabled = true;
+			document.getElementById("fromtime").style.color = "#A9A9A9";
+			document.getElementById("totime").style.color = "#A9A9A9";
+		}
+	}
+</script>
diff --git a/apps/calendar/templates/part.eventinfo.php b/apps/calendar/templates/part.eventinfo.php
new file mode 100755
index 0000000000000000000000000000000000000000..edccaaa22275fd7afa161781f709490dc3c6d4e7
--- /dev/null
+++ b/apps/calendar/templates/part.eventinfo.php
@@ -0,0 +1,87 @@
+ <div id="eventinfo" title="<?php echo $l -> t("Edit an event");?>">
+	<table id="eventinfo_table" width="100%">
+		<tr>
+			<td width="75px"><?php echo $l -> t("Title");?>:</td>
+			<td>
+			</td>
+		</tr>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Location");?>:</td>
+			<td>
+			</td>
+		</tr>
+	</table>
+	<table>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Category");?>:</td>
+			<td></td>
+			<td width="75px">&nbsp;&nbsp;&nbsp;<?php echo $l -> t("Calendar");?>:</td>
+			<td></td>
+		</tr>
+	</table>
+	<hr>
+	<table>
+		<tr>
+			<td width="75px"></td>
+			<td>
+			<input type="checkbox" id="newcalendar_allday_checkbox"  disabled="true">
+			<label for="newcalendar_allday_checkbox"><?php echo $l -> t("All Day Event");?></label></td>
+		</tr>
+		<tr>
+			<td width="75px"><?php echo $l -> t("From");?>:</td>
+			<td>
+			&nbsp;&nbsp;
+			</td><!--use jquery-->
+		</tr>
+		<tr>
+
+			<td width="75px"><?php echo $l -> t("To");?>:</td>
+			<td>
+			&nbsp;&nbsp;
+			</td><!--use jquery-->
+		</tr>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Repeat");?>:</td>
+			<td></td>
+		</tr>
+	</table>
+	<hr>
+	<table>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Attendees");?>:</td>
+			<td style="height: 50px;"></td>
+		</tr>
+	</table>
+	<hr>
+	<table>
+		<tr>
+			<td width="75px" style="vertical-align: top;"><?php echo $l -> t("Description");?>:</td>
+			<td></td>
+		</tr>
+	</table>
+	<span id="editevent_actions">
+		<input type="button" style="float: left;" value="<?php echo $l -> t("Close");?>">
+		<input type="button" style="float: right;" value="<?php echo $l -> t("Edit");?>">
+	</span>
+</div>
+<script type="text/javascript">
+	$( "#eventinfo" ).dialog({
+		width : 500,
+		close : function() {
+					oc_cal_opendialog = 0;
+					var lastchild = document.getElementById("body-user").lastChild
+					while(lastchild.id != "lightbox"){
+						document.getElementById("body-user").removeChild(lastchild);
+						lastchild = document.getElementById("body-user").lastChild;
+					}
+			},
+		open : function(){alert("Doesn't work yet.");}
+	});
+	$( "#from" ).datepicker({
+		dateFormat : 'dd-mm-yy'
+	});
+	$( "#to" ).datepicker({
+		dateFormat : 'dd-mm-yy'
+	});
+	}
+</script>
\ No newline at end of file
diff --git a/apps/calendar/templates/part.getcal.php b/apps/calendar/templates/part.getcal.php
new file mode 100755
index 0000000000000000000000000000000000000000..0d85c9be4c4e71a95e31d0ccb78b05715530587c
--- /dev/null
+++ b/apps/calendar/templates/part.getcal.php
@@ -0,0 +1,42 @@
+<?php
+$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1);
+$events = OC_Calendar_Calendar::allCalendarObjects($calendars[0]['id']);
+$select_year = $_GET["year"];
+$return_events = array();
+$user_timezone = OC_Preferences::getValue(OC_USER::getUser(), "calendar", "timezone", "Europe/London");
+foreach($events as $event)
+{
+	if ($select_year != substr($event['startdate'], 0, 4))
+		continue;
+	$start_dt = new DateTime($event['startdate'], new DateTimeZone('UTC'));
+	$start_dt->setTimezone(new DateTimeZone($user_timezone));
+	$end_dt = new DateTime($event['enddate'], new DateTimeZone('UTC'));
+	$end_dt->setTimezone(new DateTimeZone($user_timezone));
+	$year  = $start_dt->format('Y');
+	$month = $start_dt->format('n') - 1; // return is 0 based
+	$day   = $start_dt->format('j');
+	$hour  = $start_dt->format('G');
+
+	// hack
+	if (strstr($event['calendardata'], 'DTSTART;VALUE=DATE:')) {
+		$hour = 'allday';
+	}
+	$return_event = array();
+	foreach(array('id', 'calendarid', 'objecttype', 'repeating') as $prop)
+	{
+		$return_event[$prop] = $event[$prop];
+	}
+	$return_event['startdate'] = $start_dt->format('Y-m-d H:i');
+	$return_event['enddate'] = $end_dt->format('Y-m-d H:i');
+	$return_event['description'] = $event['summary'];
+	if (isset($return_events[$year][$month][$day][$hour]))
+	{
+		$return_events[$year][$month][$day][$hour][] = $return_event;
+	}
+	else
+	{
+		$return_events[$year][$month][$day][$hour] = array(1 => $return_event);
+	}
+}
+echo json_encode($return_events);
+?>
diff --git a/apps/calendar/templates/part.newevent.php b/apps/calendar/templates/part.newevent.php
new file mode 100755
index 0000000000000000000000000000000000000000..692be344dec02b7cff3bf2e880df3564493bc32c
--- /dev/null
+++ b/apps/calendar/templates/part.newevent.php
@@ -0,0 +1,217 @@
+
+<div id="newevent" title="<?php echo $l -> t("Create a new event");?>">
+	<form id="newevent_form">
+	<table id="newevent_table" width="100%">
+		<tr>
+			<td width="75px"><?php echo $l -> t("Title");?>:</td>
+			<td>
+			<input type="text" style="width:350px;" size="100" placeholder="<?php echo $l -> t("Title of the Event");?>"  maxlength="100" id="newevent_title"/>
+			</td>
+		</tr>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Location");?>:</td>
+			<td>
+			<input type="text" style="width:350px;" size="100" placeholder="<?php echo $l -> t("Location of the Event");?>" maxlength="100"  id="newevent_location" />
+			</td>
+		</tr>
+	</table>
+	<table>
+		<tr>
+			<td width="75px"><?php echo $l -> t("Category");?>:</td>
+			<td>
+			<select class="formselect" id="formcategorie_select" style="width:140px;" id="newevent_cat">
+				<option>none</option>
+				<option>Birthday</option>
+				<option>Business</option>
+				<option>Call</option>
+				<option>Clients</option>
+				<option>Customer</option>
+				<option>Deliverer</option>
+				<option>Holidays</option>
+				<option>Ideas</option>
+				<option>Journey</option>
+				<option>Jubilee</option>
+				<option>Meeting</option>
+				<option>Other</option>
+				<option>Personal</option>
+				<option>Projects</option>
+				<option>Questions</option>
+				<option>Work</option>
+			</select></td>
+			<td width="75px">&nbsp;&nbsp;&nbsp;<?php echo $l -> t("Calendar");?>:</td>
+			<td>
+			<select class="formselect" id="formcalendar_select" style="width:140px;" id="newevent_cal">
+				<?php
+				$option_calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser());
+				for($i = 0; $i < count($option_calendars); $i++){
+					echo "<option id=\"option_" . $option_calendars[$i]["id"] . "\">" . $option_calendars[$i]["displayname"] . "</option>";
+				}
+				?>
+			</select></td>
+		</tr>
+	</table>
+	<hr>
+				<?php $day = substr($_GET["d"], 0, 2);
+			$month = substr($_GET["d"], 2, 2);
+			$year = substr($_GET["d"], 4, 4);
+			$time = $_GET["t"];
+			if($time != "undefined" && !is_nan($_GET["t"]) && $_GET["t"] != "allday"){
+				$time = $_GET["t"];
+				$minutes = "00";
+			}elseif($_GET["t"] == "allday"){
+				$time = "0";
+				$minutes = "00";
+				$allday = true;
+			}else{
+				$time = date("H");
+				$minutes = date("i");
+			}
+			?>
+	<table>
+		<tr>
+			<td width="75px"></td>
+			<td>
+			<input onclick="lock_time();" type="checkbox"<?php if($allday == true){echo "checked=\"checked\"";}  ?> id="newcalendar_allday_checkbox">
+			<?php if($allday == true){echo "<script type=\"text/javascript\">document.getElementById(\"fromtime\").disabled = true;document.getElementById(\"totime\").disabled = true;document.getElementById(\"fromtime\").style.color = \"#A9A9A9\";document.getElementById(\"totime\").style.color = \"#A9A9A9\";</script>";}?>
+			<label for="newcalendar_allday_checkbox"><?php echo $l -> t("All Day Event");?></label></td>
+		</tr>
+		<tr>
+
+			<td width="75px"><?php echo $l -> t("From");?>:</td>
+			<td>
+			<input type="text" value="<?php echo $day . "-" . $month . "-" . $year;?>" id="from">
+			&nbsp;&nbsp;
+			<input type="time" value="<?php echo $time . ":" . $minutes;?>" id="fromtime">
+			</td><!--use jquery-->
+		</tr>
+		<tr>
+			<?php
+			if(date("H") == 23) {
+				$day++;
+				$time = 0;
+			} else {
+				$time++;
+			}
+			?>
+			<td width="75px"><?php echo $l -> t("To");?>:</td>
+			<td>
+			<input type="text" value="<?php echo $day . "-" . $month . "-" . $year;?>" id="to">
+			&nbsp;&nbsp;
+			<input type="time" value="<?php echo $time . ":" . $minutes;?>" id="totime">
+			</td><!--use jquery-->
+		</tr><!--
+		<tr>
+			<td width="75px"><?php echo $l -> t("Repeat");?>:</td>
+			<td>
+			<select class="formselect" id="formrepeat_select" style="width:350px;">
+				<option id="repeat_doesnotrepeat" selected="selected"><?php echo $l->t("Does not repeat");?></option>
+				<option id="repeat_daily"><?php echo $l->t("Daily");?></option>
+				<option id="repeat_weekly"><?php echo $l->t("Weekly");?></option>
+				<option id="repeat_weekday"><?php echo $l->t("Every Weekday");?></option>
+				<option id="repeat_biweekly"><?php echo $l->t("Bi-Weekly");?></option>
+				<option id="repeat_monthly"><?php echo $l->t("Monthly");?></option>
+				<option id="repeat_yearly"><?php echo $l->t("Yearly");?></option>
+			</select></td>
+		</tr>-->
+	</table>
+	<hr>
+	<table><!--
+		<tr>
+			<td width="75px"><?php echo $l -> t("Attendees");?>:</td>
+			<td style="height: 50px;"></td>
+		</tr>
+	</table>
+	<hr>-->
+	<table>
+		<tr>
+			<td width="75px" style="vertical-align: top;"><?php echo $l -> t("Description");?>:</td>
+			<td><textarea style="width:350px;height: 150px;"placeholder="<?php echo $l->t("Description of the Event");?>" id="description"></textarea></td>
+		</tr>
+	</table>
+	<div style="width: 100%;text-align: center;color: #FF1D1D;" id="errorbox"></div>
+	<span id="newcalendar_actions">
+		<input type="button" class="submit" style="float: left;" value="<?php echo $l -> t("Submit");?>" onclick="validate_newevent_form();">
+	</span>
+	</form>
+</div>
+<script type="text/javascript">
+	$("#newevent").dialog({
+		width : 500,
+		close : function() {
+					oc_cal_opendialog = 0;
+					var lastchild = document.getElementById("body-user").lastChild
+					while(lastchild.id != "lightbox"){
+						document.getElementById("body-user").removeChild(lastchild);
+						lastchild = document.getElementById("body-user").lastChild;
+					}
+			}
+	});
+	$( "#from" ).datepicker({
+		dateFormat : 'dd-mm-yy'
+	});
+	$( "#to" ).datepicker({
+		dateFormat : 'dd-mm-yy'
+	});
+	function lock_time() {
+		if(document.getElementById("totime").disabled == true) {
+			document.getElementById("fromtime").disabled = false;
+			document.getElementById("totime").disabled = false;
+			document.getElementById("fromtime").style.color = "#333";
+			document.getElementById("totime").style.color = "#333";
+		} else {
+			document.getElementById("fromtime").disabled = true;
+			document.getElementById("totime").disabled = true;
+			document.getElementById("fromtime").style.color = "#A9A9A9";
+			document.getElementById("totime").style.color = "#A9A9A9";
+		}
+	}
+	function validate_newevent_form(){
+		var newevent_title = document.getElementById("newevent_title").value;
+		var newevent_location = document.getElementById("newevent_location").value;
+		var newevent_cat = document.getElementById("formcategorie_select").options[document.getElementById("formcategorie_select").selectedIndex].value;
+		var newevent_cal = document.getElementById("formcalendar_select").options[document.getElementById("formcalendar_select").selectedIndex].id;
+		var newevent_allday = document.getElementById("newcalendar_allday_checkbox").checked;
+		var newevent_from = document.getElementById("from").value;
+		var newevent_fromtime = document.getElementById("fromtime").value;
+		var newevent_to = document.getElementById("to").value;
+		var newevent_totime = document.getElementById("totime").value;
+		var newevent_description = document.getElementById("description").value;
+		$.post("ajax/newevent.php", { title: newevent_title, location: newevent_location, cat: newevent_cat, cal: newevent_cal, allday: newevent_allday, from: newevent_from, fromtime: newevent_fromtime, to: newevent_to, totime: newevent_totime, description: newevent_description},
+			function(data){
+				if(data.error == "true"){
+					document.getElementById("errorbox").innerHTML = "";
+					var output = "Missing fields: <br />";
+					if(data.title == "true"){
+						output = output + "Title<br />";
+					}
+					if(data.cal == "true"){
+						output = output + "Calendar<br />";
+					}
+					if(data.from == "true"){
+						output = output + "From Date<br />";
+					}
+					if(data.fromtime == "true"){
+						output = output + "From Time<br />";
+					}
+					if(data.to == "true"){
+						output = output + "To Date<br />";
+					}
+					if(data.totime == "true"){
+						output = output + "To Time<br />";
+					}
+					if(data.endbeforestart == "true"){
+						output = "The event ends before it starts!";
+					}
+					if(data.dberror == "true"){
+						output = "There was a database fail!";
+					}
+					document.getElementById("errorbox").innerHTML = output;
+				}else{
+					window.location.reload();
+				}
+				if(data.success == true){
+					location.reload();
+				}
+			},"json");
+	}
+</script>
\ No newline at end of file
diff --git a/apps/calendar/templates/settings.php b/apps/calendar/templates/settings.php
new file mode 100755
index 0000000000000000000000000000000000000000..0b0a4f1c265584e3db1daa60e45a230553c935b9
--- /dev/null
+++ b/apps/calendar/templates/settings.php
@@ -0,0 +1,19 @@
+<form id="calendar">
+        <fieldset class="personalblock">
+                <label for="timezone"><strong><?php echo $l->t('Timezone');?></strong></label>
+		<select id="timezone" name="timezone">
+                <?php foreach($_['timezones'] as $timezone):
+			if ( preg_match( '/^(America|Antartica|Arctic|Asia|Atlantic|Europe|Indian|Pacific)\//', $timezone ) ):
+				$ex=explode('/', $timezone, 2);//obtain continent,city
+				if ($continent!=$ex[0]):
+					if ($continent!="") echo '</optgroup>';
+					echo '<optgroup label="'.$ex[0].'">';
+				endif;
+				$city=$ex[1];
+				$continent=$ex[0];
+				echo '<option value="'.$timezone.'"'.($_['timezone'] == $timezone?' selected="selected"':'').'>'.$city.'</option>';
+			endif;
+                endforeach;?>
+                </select><span id="timezoneerror"></span>
+        </fieldset>
+</form>
diff --git a/apps/contacts/ajax/addcard.php b/apps/contacts/ajax/addcard.php
new file mode 100644
index 0000000000000000000000000000000000000000..24766931d71814da524d3bf469bb342a690d8520
--- /dev/null
+++ b/apps/contacts/ajax/addcard.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$aid = $_POST['id'];
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $aid );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your addressbook!'))));
+	exit();
+}
+
+$fn = $_POST['fn'];
+
+$vcard = new Sabre_VObject_Component('VCARD');
+$vcard->add(new Sabre_VObject_Property('FN',$fn));
+$vcard->add(new Sabre_VObject_Property('UID',OC_Contacts_Addressbook::createUID()));
+$id = OC_Contacts_Addressbook::addCard($aid,$vcard->serialize());
+
+$details = OC_Contacts_Addressbook::structureContact($vcard);
+$tmpl = new OC_Template('contacts','part.details');
+$tmpl->assign('details',$details);
+$tmpl->assign('id',$id);
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'id' => $id, 'page' => $page )));
diff --git a/apps/contacts/ajax/addproperty.php b/apps/contacts/ajax/addproperty.php
new file mode 100644
index 0000000000000000000000000000000000000000..d92566d6a18d488120f596cfe64a5701c4d18efb
--- /dev/null
+++ b/apps/contacts/ajax/addproperty.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_POST['id'];
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+$vcard = Sabre_VObject_Reader::read($card['carddata']);
+
+$name = $_POST['name'];
+$value = $_POST['value'];
+$parameters = isset($_POST['parameteres'])?$_POST['parameters']:array();
+
+if(is_array($value)){
+	$value = OC_Contacts_Addressbook::escapeSemicolons($value);
+}
+$property = new Sabre_VObject_Property( $name, $value );
+$parameternames = array_keys($parameters);
+foreach($parameternames as $i){
+	$property->parameters[] = new Sabre_VObject_Parameter($i,$parameters[$i]);
+}
+
+$vcard->add($property);
+
+$line = count($vcard->children) - 1;
+$checksum = md5($property->serialize());
+
+OC_Contacts_Addressbook::editCard($id,$vcard->serialize());
+
+$tmpl = new OC_Template('contacts','part.property');
+$tmpl->assign('property',OC_Contacts_Addressbook::structureProperty($property,$line));
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/deletebook.php b/apps/contacts/ajax/deletebook.php
new file mode 100644
index 0000000000000000000000000000000000000000..8506284cc0d1a7d1205ed7d50c07fa55bc699019
--- /dev/null
+++ b/apps/contacts/ajax/deletebook.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_GET['id'];
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $id );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+OC_Contacts_Addressbook::deleteAddressbook($id);
+echo json_encode( array( 'status' => 'success', 'data' => array( 'id' => $id )));
diff --git a/apps/contacts/ajax/deletecard.php b/apps/contacts/ajax/deletecard.php
new file mode 100644
index 0000000000000000000000000000000000000000..839936d3fadfadd56221de5402541fc732b248d0
--- /dev/null
+++ b/apps/contacts/ajax/deletecard.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_GET['id'];
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+OC_Contacts_Addressbook::deleteCard($id);
+echo json_encode( array( 'status' => 'success', 'data' => array( 'id' => $id )));
diff --git a/apps/contacts/ajax/deleteproperty.php b/apps/contacts/ajax/deleteproperty.php
new file mode 100644
index 0000000000000000000000000000000000000000..d141cc00b8d557e3d405ba829b98709060cb2756
--- /dev/null
+++ b/apps/contacts/ajax/deleteproperty.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_GET['id'];
+$checksum = $_GET['checksum'];
+
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+$vcard = Sabre_VObject_Reader::read($card['carddata']);
+$line = null;
+for($i=0;$i<count($vcard->children);$i++){
+	if(md5($vcard->children[$i]->serialize()) == $checksum ){
+		$line = $i;
+	}
+}
+if(is_null($line)){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Information about vCard is incorrect. Please reload page!'))));
+	exit();
+}
+
+unset($vcard->children[$line]);
+
+OC_Contacts_Addressbook::editCard($id,$vcard->serialize());
+echo json_encode( array( 'status' => 'success', 'data' => array( 'id' => $id )));
diff --git a/apps/contacts/ajax/getdetails.php b/apps/contacts/ajax/getdetails.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ee3625afc2b0dc29dbdd7b0d17a66e98fec90f4
--- /dev/null
+++ b/apps/contacts/ajax/getdetails.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_GET['id'];
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+$vcard = Sabre_VObject_Reader::read($card['carddata']);
+$details = OC_Contacts_Addressbook::structureContact($vcard);
+$tmpl = new OC_Template('contacts','part.details');
+$tmpl->assign('details',$details);
+$tmpl->assign('id',$id);
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'id' => $id, 'page' => $page )));
diff --git a/apps/contacts/ajax/setproperty.php b/apps/contacts/ajax/setproperty.php
new file mode 100644
index 0000000000000000000000000000000000000000..08d8892254ef4fa6ca4d3b20c98178bad612dc77
--- /dev/null
+++ b/apps/contacts/ajax/setproperty.php
@@ -0,0 +1,97 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_POST['id'];
+$checksum = $_POST['checksum'];
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+$vcard = Sabre_VObject_Reader::read($card['carddata']);
+$line = null;
+for($i=0;$i<count($vcard->children);$i++){
+	if(md5($vcard->children[$i]->serialize()) == $checksum ){
+		$line = $i;
+	}
+}
+if(is_null($line)){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Information about vCard is incorrect. Please reload page!'))));
+	exit();
+}
+
+// Set the value
+$value = $_POST['value'];
+if(is_array($value)){
+	$value = OC_Contacts_Addressbook::escapeSemicolons($value);
+}
+$vcard->children[$line]->setValue($value);
+
+// Add parameters
+$postparameters = isset($_POST['parameters'])?$_POST['parameters']:array();
+for($i=0;$i<count($vcard->children[$line]->parameters);$i++){
+	$name = $vcard->children[$line]->parameters[$i]->name;
+	if(array_key_exists($name,$postparameters)){
+		if($postparameters[$name] == '' || is_null($postparameters[$name])){
+			unset($vcard->children[$line]->parameters[$i]);
+		}
+		else{
+			$vcard->children[$line]->parameters[$i]->value = $postparameters[$name];
+		}
+		unset($postparameters[$name]);
+	}
+}
+$missingparameters = array_keys($postparameters);
+foreach($missingparameters as $i){
+	if(!$postparameters[$i] == '' && !is_null($postparameters[$i])){
+		$vcard->children[$line]->parameters[] = new Sabre_VObject_Parameter($i,$postparameters[$i]);
+	}
+}
+
+// Do checksum and be happy
+$checksum = md5($vcard->children[$line]->serialize());
+
+OC_Contacts_Addressbook::editCard($id,$vcard->serialize());
+
+$tmpl = new OC_Template('contacts','part.property');
+$tmpl->assign('property',OC_Contacts_Addressbook::structureProperty($vcard->children[$line],$line));
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'page' => $page, 'line' => $line, 'oldchecksum' => $_POST['checksum'] )));
diff --git a/apps/contacts/ajax/showaddcard.php b/apps/contacts/ajax/showaddcard.php
new file mode 100644
index 0000000000000000000000000000000000000000..41ebb41d3e9d31bffb2c2e38f8de3d2977bf2090
--- /dev/null
+++ b/apps/contacts/ajax/showaddcard.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$addressbooks = OC_Contacts_Addressbook::allAddressbooks(OC_USER::getUser());
+$tmpl = new OC_Template('contacts','part.addcardform');
+$tmpl->assign('addressbooks',$addressbooks);
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/showaddproperty.php b/apps/contacts/ajax/showaddproperty.php
new file mode 100644
index 0000000000000000000000000000000000000000..becc39b120a01307ae65cfd1d3306defd472519d
--- /dev/null
+++ b/apps/contacts/ajax/showaddproperty.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_GET['id'];
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+$tmpl = new OC_Template('contacts','part.addpropertyform');
+$tmpl->assign('id',$id);
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'page' => $page )));
diff --git a/apps/contacts/ajax/showsetproperty.php b/apps/contacts/ajax/showsetproperty.php
new file mode 100644
index 0000000000000000000000000000000000000000..a00043384f33aa43ea498da32d49755a82e9db55
--- /dev/null
+++ b/apps/contacts/ajax/showsetproperty.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../../lib/base.php');
+
+$id = $_GET['id'];
+$checksum = $_GET['checksum'];
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('You need to log in!'))));
+	exit();
+}
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+	exit();
+}
+
+$vcard = Sabre_VObject_Reader::read($card['carddata']);
+$line = null;
+for($i=0;$i<count($vcard->children);$i++){
+	if(md5($vcard->children[$i]->serialize()) == $checksum ){
+		$line = $i;
+	}
+}
+if(is_null($line)){
+	echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Information about vCard is incorrect. Please reload page!'))));
+	exit();
+}
+
+
+$tmpl = new OC_Template('contacts','part.setpropertyform');
+$tmpl->assign('id',$id);
+$tmpl->assign('checksum',$checksum);
+$tmpl->assign('property',OC_Contacts_Addressbook::structureProperty($vcard->children[$line]));
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'page' => $page )));
diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ae6be5d6e3d3e1590431be9d37ce66b4fe89984
--- /dev/null
+++ b/apps/contacts/appinfo/app.php
@@ -0,0 +1,18 @@
+<?php
+
+OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php';
+OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php';
+OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php';
+OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Contacts_Hooks', 'deleteUser');
+
+OC_App::register( array( 
+  'order' => 10,
+  'id' => 'contacts',
+  'name' => 'Contacts' ));
+
+OC_App::addNavigationEntry( array( 
+  'id' => 'contacts_index',
+  'order' => 10,
+  'href' => OC_Helper::linkTo( 'contacts', 'index.php' ),
+  'icon' => OC_Helper::imagePath( 'settings', 'users.svg' ),
+  'name' => 'Contacts' ));
diff --git a/apps/contacts/appinfo/database.xml b/apps/contacts/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7c8268d71f53430c61b45ab6a27d2e34df035a44
--- /dev/null
+++ b/apps/contacts/appinfo/database.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>utf8</charset>
+
+ <table>
+
+  <name>*dbprefix*contacts_addressbooks</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>userid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>displayname</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>uri</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>description</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>ctag</name>
+    <type>integer</type>
+    <default>1</default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*contacts_cards</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>addressbookid</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>fullname</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>carddata</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
+   <field>
+    <name>uri</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>lastmodified</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+</database>
diff --git a/apps/contacts/appinfo/info.xml b/apps/contacts/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..77c9dc91bfc7420bef0b45de8fbf708b155d3a46
--- /dev/null
+++ b/apps/contacts/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?> 
+<info>
+	<id>contacts</id>
+	<name>Contacts</name>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Jakob Sack</author>
+	<require>2</require>
+	<description>Address book with CardDAV support.</description>
+</info>
diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php
new file mode 100644
index 0000000000000000000000000000000000000000..77b3c105deb34e82f1aee6e0338c1f3a106a5200
--- /dev/null
+++ b/apps/contacts/carddav.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Do not load FS ...
+$RUNTIME_NOSETUPFS = true;
+
+require_once('../../lib/base.php');
+
+// Backends
+$authBackend = new OC_Connector_Sabre_Auth();
+$principalBackend = new OC_Connector_Sabre_Principal();
+$carddavBackend   = new OC_Connector_Sabre_CardDAV();
+
+// Root nodes
+$nodes = array(
+	new Sabre_DAVACL_PrincipalCollection($principalBackend),
+	new Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend),
+);
+
+// Fire up server
+$server = new Sabre_DAV_Server($nodes);
+$server->setBaseUri($WEBROOT.'/apps/contacts/carddav.php');
+// Add plugins
+$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
+$server->addPlugin(new Sabre_CardDAV_Plugin());
+$server->addPlugin(new Sabre_DAVACL_Plugin());
+
+// And off we go!
+$server->exec();
diff --git a/apps/contacts/css/styles.css b/apps/contacts/css/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..1f95fdb2ad476f496e8b9c9885ac23117c1806ff
--- /dev/null
+++ b/apps/contacts/css/styles.css
@@ -0,0 +1,2 @@
+.contacts_details_left {text-align:right;vertical-align:top;padding:2px;}
+.contacts_details_right {text-align:left;vertical-align:top;padding:2px;}
diff --git a/apps/contacts/index.php b/apps/contacts/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d4ff83ef81fe4ba8e22acdc163ed053e9a088f5
--- /dev/null
+++ b/apps/contacts/index.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+function contacts_namesort($a,$b){
+	return strcmp($a['name'],$b['name']);
+}
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( 'Location: '.OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+// Check if the user has an addressbook
+$addressbooks = OC_Contacts_Addressbook::allAddressbooks(OC_User::getUser());
+if( count($addressbooks) == 0){
+	OC_Contacts_Addressbook::addAddressbook(OC_User::getUser(),'default','Default Address Book');
+	$addressbooks = OC_Contacts_Addressbook::allAddressbooks(OC_User::getUser());
+}
+$prefbooks = OC_Preferences::getValue(OC_User::getUser(),'contacts','openaddressbooks',null);
+if(is_null($prefbooks)){
+	$prefbooks = $addressbooks[0]['id'];
+	OC_Preferences::setValue(OC_User::getUser(),'contacts','openaddressbooks',$prefbooks);
+}
+
+// Load the files we need
+OC_App::setActiveNavigationEntry( 'contacts_index' );
+
+// Load a specific user?
+$id = isset( $_GET['id'] ) ? $_GET['id'] : null;
+
+// sort addressbooks  (use contactsort)
+usort($addressbooks,'contacts_namesort');
+// Addressbooks to load
+$openaddressbooks = explode(';',$prefbooks);
+
+$contacts = array();
+foreach( $openaddressbooks as $addressbook ){
+	$addressbookcontacts = OC_Contacts_Addressbook::allCards($addressbook);
+	foreach( $addressbookcontacts as $contact ){
+		$contacts[] = array( 'name' => $contact['fullname'], 'id' => $contact['id'] );
+	}
+}
+
+
+usort($contacts,'contacts_namesort');
+$details = array();
+
+if( !is_null($id) || count($contacts)){
+	if(is_null($id)) $id = $contacts[0]['id'];
+	$contact = OC_Contacts_Addressbook::findCard($id);
+	$vcard = Sabre_VObject_Reader::read($contact['carddata']);
+	$details = OC_Contacts_Addressbook::structureContact($vcard);
+}
+
+// Process the template
+$tmpl = new OC_Template( 'contacts', 'index', 'user' );
+$tmpl->assign('addressbooks', $addressbooks);
+$tmpl->assign('contacts', $contacts);
+$tmpl->assign('details', $details );
+$tmpl->assign('id',$id);
+$tmpl->printPage();
diff --git a/apps/contacts/js/interface.js b/apps/contacts/js/interface.js
new file mode 100644
index 0000000000000000000000000000000000000000..b8a66d51aab7753c56ae3bc05aef3034f7d60bb7
--- /dev/null
+++ b/apps/contacts/js/interface.js
@@ -0,0 +1,153 @@
+$(document).ready(function(){
+	/* $('.contacts_addressbooksexpander').click(function(){
+		$('.contacts_addressbooksdetails').toggle();
+		return false;
+	});*/
+
+	$('#leftcontent li').live('click',function(){
+		var id = $(this).data('id');
+		$.getJSON('ajax/getdetails.php',{'id':id},function(jsondata){
+			if(jsondata.status == 'success'){
+				$('#rightcontent').data('id',jsondata.data.id);
+				$('#rightcontent').html(jsondata.data.page);
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		});
+		return false;
+	});
+
+	$('#contacts_deletecard').live('click',function(){
+		var id = $('#rightcontent').data('id');
+		$.getJSON('ajax/deletecard.php',{'id':id},function(jsondata){
+			if(jsondata.status == 'success'){
+				$('#leftcontent [data-id="'+jsondata.data.id+'"]').remove();
+				$('#rightcontent').data('id','');
+				$('#rightcontent').html('');
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		});
+		return false;
+	});
+
+	$('#contacts_addproperty').live('click',function(){
+		var id = $('#rightcontent').data('id');
+		$.getJSON('ajax/showaddproperty.php',{'id':id},function(jsondata){
+			if(jsondata.status == 'success'){
+				$('#rightcontent').append(jsondata.data.page);
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		});
+		return false;
+	});
+
+	$('#contacts_addpropertyform [name="name"]').live('change',function(){
+		$('#contacts_addpropertyform #contacts_addresspart').remove();
+		$('#contacts_addpropertyform #contacts_phonepart').remove();
+		$('#contacts_addpropertyform #contacts_fieldpart').remove();
+		$('#contacts_addpropertyform #contacts_generic').remove();
+		if($(this).val() == 'ADR'){
+			$('#contacts_addresspart').clone().insertBefore($('#contacts_addpropertyform input[type="submit"]'));
+		}
+		else if($(this).val() == 'TEL'){
+			$('#contacts_phonepart').clone().insertBefore($('#contacts_addpropertyform input[type="submit"]'));
+		}
+		else{
+			$('#contacts_generic').clone().insertBefore($('#contacts_addpropertyform input[type="submit"]'));
+		}
+	});
+
+	$('#contacts_addpropertyform input[type="submit"]').live('click',function(){
+		$.post('ajax/addproperty.php',$('#contacts_addpropertyform').serialize(),function(jsondata){
+			if(jsondata.status == 'success'){
+				$('#contacts_cardoptions').before(jsondata.data.page);
+				$('#contacts_addpropertyform').remove();
+				$('#contacts_addcontactsparts').remove();
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		}, 'json');
+		return false;
+	});
+	
+	$('#contacts_newcontact').click(function(){
+		$.getJSON('ajax/showaddcard.php',{},function(jsondata){
+			if(jsondata.status == 'success'){
+				$('#rightcontent').data('id','');
+				$('#rightcontent').html(jsondata.data.page);
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		});
+		return false;
+	});
+
+	$('#contacts_addcardform input[type="submit"]').live('click',function(){
+		$.post('ajax/addcard.php',$('#contacts_addcardform').serialize(),function(jsondata){
+			if(jsondata.status == 'success'){
+				$('#rightcontent').data('id',jsondata.data.id);
+				$('#rightcontent').html(jsondata.data.page);
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		}, 'json');
+		return false;
+	});
+
+	$('.contacts_details_property [data-use="edit"]').live('click',function(){
+		var id = $('#rightcontent').data('id');
+		var checksum = $(this).parent().parent().data('checksum');
+		$.getJSON('ajax/showsetproperty.php',{'id': id, 'checksum': checksum },function(jsondata){
+			if(jsondata.status == 'success'){
+				$('.contacts_details_property[data-checksum="'+checksum+'"] .contacts_details_right').html(jsondata.data.page);
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		});
+		return false;
+	});
+
+	$('#contacts_setpropertyform input[type="submit"]').live('click',function(){
+		$.post('ajax/setproperty.php',$('#contacts_setpropertyform').serialize(),function(jsondata){
+			if(jsondata.status == 'success'){
+				$('.contacts_details_property[data-checksum="'+jsondata.data.oldchecksum+'"]').replaceWith(jsondata.data.page);
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		},'json');
+		return false;
+	});
+
+	$('.contacts_details_property [data-use="delete"]').live('click',function(){
+		var id = $('#rightcontent').data('id');
+		var checksum = $(this).parent().parent().data('checksum');
+		$.getJSON('ajax/deleteproperty.php',{'id': id, 'checksum': checksum },function(jsondata){
+			if(jsondata.status == 'success'){
+				$('.contacts_details_property[data-checksum="'+checksum+'"]').remove();
+			}
+			else{
+				alert(jsondata.data.message);
+			}
+		});
+		return false;
+	});
+
+
+	$('.contacts_details_property').live('mouseenter',function(){
+		$(this).find('span').show();
+	});
+	
+	$('.contacts_details_property').live('mouseleave',function(){
+		$(this).find('span').hide();
+	});
+});
diff --git a/apps/contacts/lib/addressbook.php b/apps/contacts/lib/addressbook.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a5dea6f45fa2505dffdc1f1d5df8b90dc64d5c5
--- /dev/null
+++ b/apps/contacts/lib/addressbook.php
@@ -0,0 +1,332 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE contacts_addressbooks (
+ * id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ * userid VARCHAR(255) NOT NULL,
+ * displayname VARCHAR(255),
+ * uri VARCHAR(100),
+ * description TEXT,
+ * ctag INT(11) UNSIGNED NOT NULL DEFAULT '1'
+ * );
+ * 
+ * CREATE TABLE contacts_cards (
+ * id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ * addressbookid INT(11) UNSIGNED NOT NULL,
+ * fullname VARCHAR(255),
+ * carddata TEXT,
+ * uri VARCHAR(100),
+ * lastmodified INT(11) UNSIGNED
+ * );
+ */
+
+/**
+ * This class manages our addressbooks.
+ */
+class OC_Contacts_Addressbook{
+	public static function allAddressbooks($uid){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE userid = ?' );
+		$result = $stmt->execute(array($uid));
+		
+		$addressbooks = array();
+		while( $row = $result->fetchRow()){
+			$addressbooks[] = $row;
+		}
+
+		return $addressbooks;
+	}
+	
+	public static function allAddressbooksWherePrincipalURIIs($principaluri){
+		$uid = self::extractUserID($principaluri);
+		return self::allAddressbooks($uid);
+	}
+
+	public static function findAddressbook($id){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
+		$result = $stmt->execute(array($id));
+
+		return $result->fetchRow();
+	}
+
+	public static function addAddressbook($userid,$name,$description){
+		$all = self::allAddressbooks($userid);
+		$uris = array();
+		foreach($all as $i){
+			$uris[] = $i['uri'];
+		}
+
+		$uri = self::createURI($name, $uris );
+
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_addressbooks (userid,displayname,uri,description,ctag) VALUES(?,?,?,?,?)' );
+		$result = $stmt->execute(array($userid,$name,$uri,$description,1));
+
+		return OC_DB::insertid();
+	}
+
+	public static function addAddressbookFromDAVData($principaluri,$uri,$name,$description){
+		$userid = self::extractUserID($principaluri);
+		
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_addressbooks (userid,displayname,uri,description,ctag) VALUES(?,?,?,?,?)' );
+		$result = $stmt->execute(array($userid,$name,$uri,$description,1));
+
+		return OC_DB::insertid();
+	}
+
+	public static function editAddressbook($id,$name,$description){
+		// Need these ones for checking uri
+		$addressbook = self::find($id);
+
+		if(is_null($name)){
+			$name = $addressbook['name'];
+		}
+		if(is_null($description)){
+			$description = $addressbook['description'];
+		}
+		
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET displayname=?,description=?, ctag=ctag+1 WHERE id=?' );
+		$result = $stmt->execute(array($name,$description,$id));
+
+		return true;
+	}
+
+	public static function touchAddressbook($id){
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET ctag = ctag + 1 WHERE id = ?' );
+		$stmt->execute(array($id));
+
+		return true;
+	}
+
+	public static function deleteAddressbook($id){
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
+		$stmt->execute(array($id));
+		
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_cards WHERE addressbookid = ?' );
+		$stmt->execute(array($id));
+
+		return true;
+	}
+
+	public static function allCards($id){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ?' );
+		$result = $stmt->execute(array($id));
+
+		$addressbooks = array();
+		while( $row = $result->fetchRow()){
+			$addressbooks[] = $row;
+		}
+
+		return $addressbooks;
+	}
+	
+	public static function findCard($id){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE id = ?' );
+		$result = $stmt->execute(array($id));
+
+		return $result->fetchRow();
+	}
+
+	public static function findCardWhereDAVDataIs($aid,$uri){
+		$stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri = ?' );
+		$result = $stmt->execute(array($aid,$uri));
+
+		return $result->fetchRow();
+	}
+
+	public static function addCard($id,$data){
+		$fn = null;
+		$uri = null;
+		$card = Sabre_VObject_Reader::read($data);
+		foreach($card->children as $property){
+			if($property->name == 'FN'){
+				$fn = $property->value;
+			}
+			elseif(is_null($uri) && $property->name == 'UID' ){
+				$uri = $property->value.'.vcf';
+			}
+		}
+		if(is_null($uri)){
+			$uid = self::createUID();
+			$uri = $uid.'.vcf';
+			$card->add(new Sabre_VObject_Property('UID',$uid));
+			$data = $card->serialize();
+		};
+
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
+		$result = $stmt->execute(array($id,$fn,$data,$uri,time()));
+
+		self::touchAddressbook($id);
+
+		return OC_DB::insertid();
+	}
+
+	public static function addCardFromDAVData($id,$uri,$data){
+		$fn = null;
+		$card = Sabre_VObject_Reader::read($data);
+		foreach($card->children as $property){
+			if($property->name == 'FN'){
+				$fn = $property->value;
+			}
+		}
+
+		$stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
+		$result = $stmt->execute(array($id,$fn,$data,$uri,time()));
+
+		self::touchAddressbook($id);
+
+		return OC_DB::insertid();
+	}
+
+	public static function editCard($id, $data){
+		$oldcard = self::findCard($id);
+		$fn = null;
+		$card = Sabre_VObject_Reader::read($data);
+		foreach($card->children as $property){
+			if($property->name == 'FN'){
+				$fn = $property->value;
+			}
+		}
+
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
+		$result = $stmt->execute(array($fn,$data,time(),$id));
+
+		self::touchAddressbook($oldcard['addressbookid']);
+
+		return true;
+	}
+
+	public static function editCardFromDAVData($aid,$uri,$data){
+		$oldcard = self::findCardWhereDAVDataIs($aid,$uri);
+
+		$fn = null;
+		$card = Sabre_VObject_Reader::read($data);
+		foreach($card->children as $property){
+			if($property->name == 'FN'){
+				$fn = $property->value;
+			}
+		}
+
+		$stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
+		$result = $stmt->execute(array($fn,$data,time(),$oldcard['id']));
+
+		self::touchAddressbook($oldcard['addressbookid']);
+
+		return true;
+	}
+	
+	public static function deleteCard($id){
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_cards WHERE id = ?' );
+		$stmt->execute(array($id));
+
+		return true;
+	}
+
+	public static function deleteCardFromDAVData($aid,$uri){
+		$stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri=?' );
+		$stmt->execute(array($aid,$uri));
+
+		return true;
+	}
+	
+	public static function createURI($name,$existing){
+		$name = strtolower($name);
+		$newname = $name;
+		$i = 1;
+		while(in_array($newname,$existing)){
+			$newname = $name.$i;
+			$i = $i + 1;
+		}
+		return $newname;
+	}
+
+	public static function createUID(){
+		return substr(md5(rand().time()),0,10);
+	}
+	
+	public static function extractUserID($principaluri){
+		list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri);
+		return $userid;
+	}
+
+	public static function escapeSemicolons($value){
+		foreach($value as &$i ){
+			$i = implode("\\\\;", explode(';', $i));
+		} unset($i);
+		return implode(';',$value);
+	}
+
+	public static function unescapeSemicolons($value){
+		$array = explode(';',$value);
+		for($i=0;$i<count($array);$i++){
+			if(substr($array[$i],-2,2)=="\\\\"){
+				if(isset($array[$i+1])){
+					$array[$i] = substr($array[$i],0,count($array[$i])-2).';'.$array[$i+1];
+					unset($array[$i+1]);
+				}
+				else{
+					$array[$i] = substr($array[$i],0,count($array[$i])-2).';';
+				}
+				$i = $i - 1;
+			}
+		}
+		return $array;
+	}
+
+	public static function structureContact($object){
+		$details = array();
+		foreach($object->children as $property){
+			$temp = self::structureProperty($property);
+			if(array_key_exists($property->name,$details)){
+				$details[$property->name][] = $temp;
+			}
+			else{
+				$details[$property->name] = array($temp);
+			}
+		}
+		return $details;
+	}
+	
+	public static function structureProperty($property){
+		$value = $property->value;
+		$value = htmlspecialchars($value);
+		if($property->name == 'ADR' || $property->name == 'N'){
+			$value = self::unescapeSemicolons($value);
+		}
+		$temp = array(
+			'name' => $property->name,
+			'value' => $value,
+			'parameters' => array(),
+			'checksum' => md5($property->serialize()));
+		foreach($property->parameters as $parameter){
+			// Faulty entries by kaddressbook
+			if($parameter->name == 'TYPE' && $parameter->value == 'PREF'){
+				$parameter->name = 'PREF';
+				$parameter->value = '1';
+			}
+			$temp['parameters'][$parameter->name] = $parameter->value;
+		}
+		return $temp;
+	}
+}
diff --git a/apps/contacts/lib/connector_sabre.php b/apps/contacts/lib/connector_sabre.php
new file mode 100644
index 0000000000000000000000000000000000000000..96a90dfc5deed343f5e43552d6934f67b8a4df39
--- /dev/null
+++ b/apps/contacts/lib/connector_sabre.php
@@ -0,0 +1,196 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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 CardDAV backend uses PDO to store addressbooks
+ */
+class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract {
+	/**
+	 * Returns the list of addressbooks for a specific user.
+	 *
+	 * @param string $principaluri
+	 * @return array
+	 */
+	public function getAddressBooksForUser($principaluri) {
+		$data = OC_Contacts_Addressbook::allAddressbooksWherePrincipalURIIs($principaluri);
+		$addressbooks = array();
+		
+		foreach($data as $i) {
+			$addressbooks[] = array(
+				'id'  => $i['id'],
+				'uri' => $i['uri'],
+				'principaluri' => 'principals/'.$i['userid'],
+				'{DAV:}displayname' => $i['displayname'],
+				'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $i['description'],
+				'{http://calendarserver.org/ns/}getctag' => $i['ctag'],
+			);
+		}
+
+		return $addressbooks;
+	}
+
+
+	/**
+	 * Updates an addressbook's properties
+	 *
+	 * See Sabre_DAV_IProperties for a description of the mutations array, as
+	 * well as the return value.
+	 *
+	 * @param mixed $addressbookid
+	 * @param array $mutations
+	 * @see Sabre_DAV_IProperties::updateProperties
+	 * @return bool|array
+	 */
+	public function updateAddressBook($addressbookid, array $mutations) {
+		$name = null;
+		$description = null;
+
+		foreach($mutations as $property=>$newvalue) {
+			switch($property) {
+				case '{DAV:}displayname' :
+					$name = $newvalue;
+					break;
+				case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+					$description = $newvalue;
+					break;
+				default :
+					// If any unsupported values were being updated, we must
+					// let the entire request fail.
+					return false;
+			}
+		}
+
+		OC_Contacts_Addressbook::editAddressbook($addressbookid,$name,$description);
+
+		return true;
+
+	}
+
+	/**
+	 * Creates a new address book
+	 *
+	 * @param string $principaluri
+	 * @param string $url Just the 'basename' of the url.
+	 * @param array $properties
+	 * @return void
+	 */
+	public function createAddressBook($principaluri, $url, array $properties) {
+
+		$displayname = null;
+		$description = null;
+
+		foreach($properties as $property=>$newvalue) {
+
+			switch($property) {
+				case '{DAV:}displayname' :
+					$displayname = $newvalue;
+					break;
+				case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+					$description = $newvalue;
+					break;
+				default :
+					throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
+			}
+
+		}
+
+		OC_Contacts_Addressbook::addAddressbookFromDAVData($principaluri,$url,$name,$description);
+	}
+
+	/**
+	 * Deletes an entire addressbook and all its contents
+	 *
+	 * @param int $addressbookid
+	 * @return void
+	 */
+	public function deleteAddressBook($addressbookid) {
+		OC_Contacts_Addressbook::deleteAddressbook($addressbookid);
+	}
+
+	/**
+	 * Returns all cards for a specific addressbook id.
+	 *
+	 * @param mixed $addressbookid
+	 * @return array
+	 */
+	public function getCards($addressbookid) {
+		$data = OC_Contacts_Addressbook::allCards($addressbookid);
+		$cards = array();
+		foreach($data as $i){
+			$cards[] = array(
+				'id' => $i['id'],
+				'carddata' => $i['carddata'],
+				'uri' => $i['uri'],
+				'lastmodified' => $i['lastmodified'] );
+		}
+
+		return $cards;
+	}
+	
+	/**
+	 * Returns a specfic card
+	 *
+	 * @param mixed $addressbookid
+	 * @param string $carduri
+	 * @return array
+	 */
+	public function getCard($addressbookid, $carduri) {
+		return OC_Contacts_Addressbook::findCardWhereDAVDataIs($addressbookid,$carduri);
+
+	}
+
+	/**
+	 * Creates a new card
+	 *
+	 * @param mixed $addressbookid
+	 * @param string $carduri
+	 * @param string $carddata
+	 * @return bool
+	 */
+	public function createCard($addressbookid, $carduri, $carddata) {
+		OC_Contacts_Addressbook::addCardFromDAVData($addressbookid, $carduri, $carddata);
+		return true;
+	}
+
+	/**
+	 * Updates a card
+	 *
+	 * @param mixed $addressbookid
+	 * @param string $carduri
+	 * @param string $carddata
+	 * @return bool
+	 */
+	public function updateCard($addressbookid, $carduri, $carddata) {
+		return OC_Contacts_Addressbook::editCardFromDAVData($addressbookid, $carduri, $carddata);
+	}
+
+	/**
+	 * Deletes a card
+	 *
+	 * @param mixed $addressbookid
+	 * @param string $carduri
+	 * @return bool
+	 */
+	public function deleteCard($addressbookid, $carduri) {
+		return OC_Contacts_Addressbook::deleteCardFromDAVData($addressbookid, $carduri);
+	}
+}
diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php
new file mode 100644
index 0000000000000000000000000000000000000000..70f1fe185187b1caceca034dede5466d547973f4
--- /dev/null
+++ b/apps/contacts/lib/hooks.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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 contains all hooks.
+ */
+class OC_Contacts_Hooks{
+	/**
+	 * @brief Deletes all Addressbooks of a certain user
+	 * @param paramters parameters from postDeleteUser-Hook
+	 * @return array
+	 */
+	public function deleteUser($parameters) {
+		$addressbooks = OC_Contacts_Addressbook::allAddressbooks($parameters['uid']);
+		
+		foreach($addressbooks as $addressbook) {
+			OC_Contacts_Addressbook::deleteAddressbook($addressbook['id']);
+		}
+
+		return true;
+	}
+}
diff --git a/apps/contacts/photo.php b/apps/contacts/photo.php
new file mode 100644
index 0000000000000000000000000000000000000000..62386421cdcff836cefe5c2464409ae7c6e7b2db
--- /dev/null
+++ b/apps/contacts/photo.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * ownCloud - Addressbook
+ *
+ * @author Jakob Sack
+ * @copyright 2011 Jakob Sack mail@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+$id = $_GET['id'];
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo $l10n->t('You need to log in!');
+	exit();
+}
+
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+	echo $l10n->t('Can not find Contact!');
+	exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+	echo $l10n->t('This is not your contact!');
+	exit();
+}
+
+$content = Sabre_VObject_Reader::read($card['carddata']);
+
+// Photo :-)
+foreach($content->children as $child){
+	if($child->name == 'PHOTO'){
+		$mime = 'image/jpeg';
+		foreach($child->parameters as $parameter){
+			if( $parameter->name == 'TYPE' ){
+				$mime = $parameter->value;
+			}
+		}
+		$photo = base64_decode($child->value);
+		header('Content-Type: '.$mime);
+		header('Content-Length: ' . strlen($photo));
+		echo $photo;
+		exit();
+	}
+}
+// Logo :-/
+foreach($content->children as $child){
+	if($child->name == 'PHOTO'){
+		$mime = 'image/jpeg';
+		foreach($child->parameters as $parameter){
+			if($parameter->name == 'TYPE'){
+				$mime = $parameter->value;
+			}
+		}
+		$photo = base64_decode($child->value());
+		header('Content-Type: '.$mime);
+		header('Content-Length: ' . strlen($photo));
+		echo $photo;
+		exit();
+	}
+}
+
+// Not found :-(
+echo $l10n->t('This card does not contain photo data!');
diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..46e85a09d11e7bfaa2b39d4975c3f6625ee98cee
--- /dev/null
+++ b/apps/contacts/templates/index.php
@@ -0,0 +1,17 @@
+<?php // Include Style and Script
+OC_Util::addScript('contacts','interface');
+OC_Util::addStyle('contacts','styles');
+?>
+
+<div id="leftcontent" class="leftcontent">
+	<ul>
+		<?php echo $this->inc("part.contacts"); ?>
+	</ul>
+	<a id="contacts_newcontact"><?php echo $l->t('Add Contact'); ?></a>
+</div>
+<div id="rightcontent" class="rightcontent" data-id="<?php echo $_['id']; ?>">
+	<?php echo $this->inc("part.details"); ?>
+</div>
+<?php if(count($_['addressbooks']) == 1 ): ?>
+	<?php echo $l->t('The path to this addressbook is %s', array(((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].OC::$WEBROOT.'/apps/contacts/carddav.php/addressbooks/'.OC_User::getUser().'/'.$_['addressbooks'][0]['uri'])); ?>
+<?php endif; ?>
diff --git a/apps/contacts/templates/part.addcardform.php b/apps/contacts/templates/part.addcardform.php
new file mode 100644
index 0000000000000000000000000000000000000000..94a59fe097cca777b8bbc5ed78af3af1695c6d83
--- /dev/null
+++ b/apps/contacts/templates/part.addcardform.php
@@ -0,0 +1,13 @@
+<form id="contacts_addcardform">
+	<?php if(count($_['addressbooks'])==1): ?>
+		<input type="hidden" name="id" value="<?php echo $_['addressbooks'][0]['id']; ?>">
+	<?php else: ?>
+		<select name="id" size="1">
+			<?php foreach($_['addressbooks'] as $addressbook): ?>
+				<option value="<?php echo $addressbook['id']; ?>"><?php echo $addressbook['displayname']; ?></option>
+			<?php endforeach; ?>
+		</select>
+	<?php endif; ?>
+	<input type="text" name="fn" value=""><br>
+	<input type="submit">
+</form>
diff --git a/apps/contacts/templates/part.addpropertyform.php b/apps/contacts/templates/part.addpropertyform.php
new file mode 100644
index 0000000000000000000000000000000000000000..32affde952688024d7c529a7b75c68ae981b5f59
--- /dev/null
+++ b/apps/contacts/templates/part.addpropertyform.php
@@ -0,0 +1,44 @@
+<form id="contacts_addpropertyform">
+	<input type="hidden" name="id" value="<?php echo $_['id']; ?>">
+	<select name="name" size="1">
+		<option value="ADR"><?php echo $l->t('Address'); ?></option>
+		<option value="TEL"><?php echo $l->t('Telephone'); ?></option>
+		<option value="EMAIL" selected="selected"><?php echo $l->t('Email'); ?></option>
+		<option value="ORG"><?php echo $l->t('Organization'); ?></option>
+	</select>
+	<div id="contacts_generic">
+		<input type="text" name="value" value="">
+	</div>
+	<input type="submit">
+</form>
+<div id="contacts_addcontactsparts" style="display:none;">
+	<div id="contacts_addresspart">
+		<select name="parameters[TYPE]" size="1">
+			<option value="adr_work"><?php echo $l->t('Work'); ?></option>
+			<option value="adr_home" selected="selected"><?php echo $l->t('Home'); ?></option>
+		</select>
+		<?php echo $l->t('PO Box'); ?> <input type="text" name="value[0]" value="">
+		<?php echo $l->t('Extended Address'); ?> <input type="text" name="value[1]" value="">
+		<?php echo $l->t('Street Name'); ?> <input type="text" name="value[2]" value="">
+		<?php echo $l->t('City'); ?> <input type="text" name="value[3]" value="">
+		<?php echo $l->t('Region'); ?> <input type="text" name="value[4]" value="">
+		<?php echo $l->t('Postal Code'); ?> <input type="text" name="value[5]" value="">
+		<?php echo $l->t('Country'); ?> <input type="text" name="value[6]" value="">
+	</div>
+	<div id="contacts_phonepart">
+		<select name="parameters[TYPE]" size="1">
+			<option value="home"><?php echo $l->t('tel_home'); ?></option>
+			<option value="cell" selected="selected"><?php echo $l->t('tel_cell'); ?></option>
+			<option value="work"><?php echo $l->t('tel_work'); ?></option>
+			<option value="text"><?php echo $l->t('tel_text'); ?></option>
+			<option value="voice"><?php echo $l->t('tel_voice'); ?></option>
+			<option value="fax"><?php echo $l->t('tel_fax'); ?></option>
+			<option value="video"><?php echo $l->t('tel_video'); ?></option>
+			<option value="pager"><?php echo $l->t('tel_pager'); ?></option>
+		</select>
+		<input type="text" name="value" value="">
+	</div>
+	<div id="contacts_generic">
+		<input type="text" name="value" value="">
+	</div>
+</div>
diff --git a/apps/contacts/templates/part.contacts.php b/apps/contacts/templates/part.contacts.php
new file mode 100644
index 0000000000000000000000000000000000000000..6664a3671acddda8c70d6e0df5d4183ce27fd6ec
--- /dev/null
+++ b/apps/contacts/templates/part.contacts.php
@@ -0,0 +1,3 @@
+<?php foreach( $_['contacts'] as $contact ): ?>
+	<li data-id="<?php echo $contact['id']; ?>"><a href="index.php?id=<?php echo $contact['id']; ?>"><?php echo $contact['name']; ?></a> </li>
+<?php endforeach; ?>
diff --git a/apps/contacts/templates/part.details.php b/apps/contacts/templates/part.details.php
new file mode 100644
index 0000000000000000000000000000000000000000..81b32f2ff569c76abeb685ff3b16af76dba01cad
--- /dev/null
+++ b/apps/contacts/templates/part.details.php
@@ -0,0 +1,35 @@
+<?php if(array_key_exists('FN',$_['details'])): ?>
+	<table>
+		<?php if(isset($_['details']['PHOTO'])): // Emails first ?>
+			<tr class="contacts_details_property">
+				<td class="contacts_details_left">&nbsp;</td>
+				<td class="contacts_details_right">
+					<img src="photo.php?id=<?php echo $_['id']; ?>">
+				</td>
+			</tr>
+		<?php endif; ?>
+		
+		<?php echo $this->inc('part.property', array('property' => $_['details']['FN'][0])); ?>
+		
+		<?php if(isset($_['details']['BDAY'])): // Emails first ?>
+			<?php echo $this->inc('part.property', array('property' => $_['details']['BDAY'][0])); ?>
+		<?php endif; ?>
+
+		<?php if(isset($_['details']['ORG'])): // Emails first ?>
+			<?php echo $this->inc('part.property', array('property' => $_['details']['ORG'][0])); ?>
+		<?php endif; ?>
+
+		<?php foreach(array('EMAIL','TEL','ADR') as $type): ?>
+			<?php if(isset($_['details'][$type])): // Emails first ?>
+				<?php foreach($_['details'][$type] as $property): ?>
+					<?php echo $this->inc('part.property',array('property' => $property )); ?>
+				<?php endforeach; ?>
+			<?php endif; ?>
+		<?php endforeach; ?>
+	</table>
+<?php endif; ?>
+
+<div id="contacts_cardoptions">
+	<a id="contacts_deletecard"><img class="svg action" alt="<?php echo $l->t('Delete');?>" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></a>
+	<a id="contacts_addproperty"><img class="svg action" alt="<?php echo $l->t('Download');?>" src="<?php echo image_path('', 'actions/download.svg'); ?>" /></a>
+</div>
diff --git a/apps/contacts/templates/part.property.php b/apps/contacts/templates/part.property.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e2a5cabad465bffd83b690bd3ea1d43b3bc4c3f
--- /dev/null
+++ b/apps/contacts/templates/part.property.php
@@ -0,0 +1,58 @@
+<tr class="contacts_details_property" data-checksum="<?php echo $_['property']['checksum']; ?>">
+	<?php if($_['property']['name'] == 'FN'): ?>
+		<td class="contacts_details_left"><?php echo $l->t('Name'); ?></td>
+		<td class="contacts_details_right">
+			<?php echo $_['property']['value']; ?>
+			<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
+		</td>
+	<?php elseif($_['property']['name'] == 'BDAY'): ?>
+		<td class="contacts_details_left"><?php echo $l->t('Birthday'); ?></td>
+		<td class="contacts_details_right">
+		<?php echo $l->l('date',new DateTime($_['property']['value'])); ?>
+			<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
+		</td>
+	<?php elseif($_['property']['name'] == 'ORG'): ?>
+		<td class="contacts_details_left"><?php echo $l->t('Organisation'); ?></td>
+		<td class="contacts_details_right">
+			<?php echo $_['property']['value']; ?>
+			<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
+			<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
+		</td>
+	<?php elseif($_['property']['name'] == 'EMAIL'): ?>
+		<td class="contacts_details_left"><?php echo $l->t('Email'); ?></td>
+		<td class="contacts_details_right">
+			<?php echo $_['property']['value']; ?>
+			<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
+			<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
+		</td>
+	<?php elseif($_['property']['name'] == 'TEL'): ?>
+		<td class="contacts_details_left"><?php echo $l->t('Telephone'); ?></td>
+		<td class="contacts_details_right">
+			<?php echo $_['property']['value']; ?>
+			<?php if(isset($_['property']['parameters']['TYPE'])): ?>
+				(<?php echo $l->t('tel_'.strtolower($_['property']['parameters']['TYPE'])); ?>)
+			<?php endif; ?>
+			<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
+			<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
+		</td>
+	<?php elseif($_['property']['name'] == 'ADR'): ?>
+		<td class="contacts_details_left">
+			<?php echo $l->t('Address'); ?>
+			<?php if(isset($_['property']['parameters']['TYPE'])): ?>
+				<br>
+				(<?php echo $l->t('adr_'.strtolower($_['property']['parameters']['TYPE'])); ?>)
+			<?php endif; ?>
+		</td>
+		<td class="contacts_details_right">
+			<?php echo $l->t('PO Box'); ?> <?php echo $_['property']['value'][0]; ?><br>
+			<?php echo $l->t('Extended Address'); ?> <?php echo $_['property']['value'][1]; ?><br>
+			<?php echo $l->t('Street Name'); ?> <?php echo $_['property']['value'][2]; ?><br>
+			<?php echo $l->t('City'); ?> <?php echo $_['property']['value'][3]; ?><br>
+			<?php echo $l->t('Region'); ?> <?php echo $_['property']['value'][4]; ?><br>
+			<?php echo $l->t('Postal Code'); ?> <?php echo $_['property']['value'][5]; ?><br>
+			<?php echo $l->t('Country'); ?> <?php echo $_['property']['value'][6]; ?> 
+			<span style="display:none;" data-use="edit"><img class="svg action" src="<?php echo image_path('', 'actions/rename.svg'); ?>" /></span>
+			<span style="display:none;" data-use="delete"><img class="svg action" src="<?php echo image_path('', 'actions/delete.svg'); ?>" /></span>
+		</td>
+	<?php endif; ?>
+</tr>
diff --git a/apps/contacts/templates/part.setpropertyform.php b/apps/contacts/templates/part.setpropertyform.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8127bb08b034211dfdda1cf38ba0c91a38a9317
--- /dev/null
+++ b/apps/contacts/templates/part.setpropertyform.php
@@ -0,0 +1,18 @@
+<form id="contacts_setpropertyform">
+	<input type="hidden" name="checksum" value="<?php echo $_['property']['checksum']; ?>">
+	<input type="hidden" name="id" value="<?php echo $_['id']; ?>">
+	<?php if($_['property']['name']=='ADR'): ?>
+		<?php echo $l->t('PO Box'); ?> <input type="text" name="value[0]" value="<?php echo $_['property']['value'][0]; ?>">
+		<?php echo $l->t('Extended Address'); ?> <input type="text" name="value[1]" value="<?php echo $_['property']['value'][1]; ?>">
+		<?php echo $l->t('Street Name'); ?> <input type="text" name="value[2]" value="<?php echo $_['property']['value'][2]; ?>">
+		<?php echo $l->t('City'); ?> <input type="text" name="value[3]" value="<?php echo $_['property']['value'][3]; ?>">
+		<?php echo $l->t('Region'); ?> <input type="text" name="value[4]" value="<?php echo $_['property']['value'][4]; ?>">
+		<?php echo $l->t('Postal Code'); ?> <input type="text" name="value[5]" value="<?php echo $_['property']['value'][5]; ?>">
+		<?php echo $l->t('Country'); ?> <input type="text" name="value[6]" value="<?php echo $_['property']['value'][6]; ?>">
+	<?php elseif($_['property']['name']=='TEL'): ?>
+		<input type="text" name="value" value="<?php echo $_['property']['value']; ?>">
+	<?php else: ?>
+		<input type="text" name="value" value="<?php echo $_['property']['value']; ?>">
+	<?php endif; ?>
+	<input type="submit">
+</form>
diff --git a/apps/files_imageviewer/appinfo/app.php b/apps/files_imageviewer/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..3dfbb76ceb09beea8395ffcee481c76794cb231c
--- /dev/null
+++ b/apps/files_imageviewer/appinfo/app.php
@@ -0,0 +1,6 @@
+<?php
+
+OC_Util::addScript( 'files_imageviewer', 'lightbox' );
+OC_Util::addStyle( 'files_imageviewer', 'lightbox' );
+
+?>
diff --git a/apps/files_imageviewer/appinfo/info.xml b/apps/files_imageviewer/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f3b5a67960ebcb905942648d66dfd3b027dd2b66
--- /dev/null
+++ b/apps/files_imageviewer/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>files_imageviewer</id>
+	<name>Image Viewer</name>
+	<description>Simple image viewer for owncloud</description>
+	<version>1.0</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>2</require>
+</info>
\ No newline at end of file
diff --git a/apps/files_imageviewer/css/lightbox.css b/apps/files_imageviewer/css/lightbox.css
new file mode 100644
index 0000000000000000000000000000000000000000..a6e844a2e28b58e08051c6f19c1fd6abcccb9df8
--- /dev/null
+++ b/apps/files_imageviewer/css/lightbox.css
@@ -0,0 +1,23 @@
+#lightbox_overlay{
+	position:fixed;
+	display:none;
+	height:100%;
+	width:100%;
+	top:0px;
+	left:0px;
+	opacity:0.5;
+	filter: alpha(opacity = 50);
+	background-color:black;
+	z-index:9999;
+}
+
+#lightbox{
+	position:fixed;
+	display:none;
+	max-height:90%;
+	max-width:90%;
+	top:10px;
+	margin-left:auto;
+	margin-right:auto;
+	z-index:9999;
+}
\ No newline at end of file
diff --git a/apps/files_imageviewer/js/lightbox.js b/apps/files_imageviewer/js/lightbox.js
new file mode 100644
index 0000000000000000000000000000000000000000..847954d2f15bc7ca9dd2328a9d0733141d488af3
--- /dev/null
+++ b/apps/files_imageviewer/js/lightbox.js
@@ -0,0 +1,76 @@
+
+var lightBoxShown=false;
+$(document).ready(function() {
+	images={};//image cache
+	var overlay=$('<div id="lightbox_overlay"/>');
+	$( 'body' ).append(overlay);
+	var container=$('<div id="lightbox"/>');
+	$( 'body' ).append(container);
+	$( 'body' ).click(hideLightbox);
+	if(typeof FileActions!=='undefined'){
+		FileActions.register('image','View','',function(filename){
+			viewImage($('#dir').val(),filename);
+		});
+		FileActions.setDefault('image','View');
+	}
+	OC.search.customResults.Images=function(row,item){
+		var image=item.link.substr(item.link.indexOf('file=')+5);
+		var a=row.find('a');
+		var container=$('<div id="lightbox"/>');
+		a.attr('href','#');
+		a.click(function(){
+			var file=image.split('/').pop();
+			var dir=image.substr(0,image.length-file.length-1);
+			viewImage(dir,file);
+		});
+	}
+});
+
+function viewImage(dir,file){
+	var location=OC.filePath('files','ajax','download.php')+'?files='+file+'&dir='+dir;
+	var overlay=$('#lightbox_overlay');
+	var container=$('#lightbox');
+	overlay.show();
+	if(!images[location]){
+		var img = new Image();
+		img.onload = function(){
+			images[location]=img;
+			showLightbox(container,img);
+		}
+		img.src = location;
+	}else{
+		showLightbox(container,images[location]);
+	}
+}
+
+function showLightbox(container,img){
+	var maxWidth = $( window ).width() - 50;
+	var maxHeight = $( window ).height() - 50;
+	if( img.width > maxWidth || img.height > maxHeight ) { // One of these is larger than the window
+		var ratio = img.width / img.height;
+		if( img.height >= maxHeight ) {
+			img.height = maxHeight;
+			img.width = maxHeight * ratio;
+		} else {
+			img.width = maxWidth;
+			img.height = maxWidth / ratio;
+		}
+	}
+	container.empty();
+	container.append(img);
+	container.css('top',Math.round( ($( window ).height() - img.height)/2));
+	container.css('left',Math.round( ($( window ).width() - img.width)/2));
+	$('#lightbox').show();
+	setTimeout(function(){
+		lightBoxShown=true;
+	},100);
+}
+
+function hideLightbox(event){
+	if(lightBoxShown){
+		event.stopPropagation();
+		$('#lightbox_overlay').hide();
+		$('#lightbox').hide();
+		lightBoxShown=false;
+	}
+}
diff --git a/apps/files_publiclink/admin.php b/apps/files_publiclink/admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..f5163547963dabd04d53a5646abf5bfd79e35162
--- /dev/null
+++ b/apps/files_publiclink/admin.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+
+// Init owncloud
+require_once('../../lib/base.php');
+require_once( 'lib_public.php' );
+
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( "index.php" ));
+	exit();
+}
+
+OC_App::setActiveNavigationEntry( "files_publiclink_administration" );
+
+OC_Util::addScript( 'files_publiclink', 'admin' );
+
+if(isset($_SERVER['HTTPS'])) {
+	$baseUrl= "https://". $_SERVER['SERVER_NAME'] . OC_Helper::linkTo('files_publiclink','get.php');
+}else{
+	$baseUrl= "http://". $_SERVER['SERVER_NAME'] . OC_Helper::linkTo('files_publiclink','get.php');
+}
+
+
+// return template
+$tmpl = new OC_Template( "files_publiclink", "admin", "user" );
+$tmpl->assign( 'links', OC_PublicLink::getLinks());
+$tmpl->assign('baseUrl',$baseUrl);
+$tmpl->printPage();
+
+?>
diff --git a/apps/files_publiclink/ajax/deletelink.php b/apps/files_publiclink/ajax/deletelink.php
new file mode 100644
index 0000000000000000000000000000000000000000..e2e4ff944a6b8467058735e623ebbfb910119b46
--- /dev/null
+++ b/apps/files_publiclink/ajax/deletelink.php
@@ -0,0 +1,11 @@
+<?php
+$RUNTIME_NOAPPS=true; //no need to load the apps
+
+require_once '../../../lib/base.php';
+
+require_once '../lib_public.php';
+
+$token=$_GET['token'];
+
+OC_PublicLink::delete($token);
+?>
\ No newline at end of file
diff --git a/apps/files_publiclink/ajax/getlink.php b/apps/files_publiclink/ajax/getlink.php
new file mode 100644
index 0000000000000000000000000000000000000000..551bcc8780cd9d1cf14b8b2b8fb9e0766389b61a
--- /dev/null
+++ b/apps/files_publiclink/ajax/getlink.php
@@ -0,0 +1,8 @@
+<?php
+$RUNTIME_NOAPPS = true;
+
+require_once('../../../lib/base.php');
+require_once('../lib_public.php');
+
+$path = $_GET['path'];
+echo json_encode(OC_PublicLink::getLink($path));
\ No newline at end of file
diff --git a/apps/files_publiclink/ajax/makelink.php b/apps/files_publiclink/ajax/makelink.php
new file mode 100644
index 0000000000000000000000000000000000000000..685feb2fb890eecec3a9c1a6958ba0d7f692f3b1
--- /dev/null
+++ b/apps/files_publiclink/ajax/makelink.php
@@ -0,0 +1,13 @@
+<?php
+$RUNTIME_NOAPPS=true; //no need to load the apps
+
+require_once '../../../lib/base.php';
+
+require_once '../lib_public.php';
+
+$path=$_GET['path'];
+$expire=0;
+
+$link=new OC_PublicLink($path,$expire);
+echo $link->getToken();
+?>
diff --git a/apps/files_publiclink/appinfo/app.php b/apps/files_publiclink/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..5866e2d1c5e9baa6772826129f56008e55a1cf6f
--- /dev/null
+++ b/apps/files_publiclink/appinfo/app.php
@@ -0,0 +1,6 @@
+<?php
+
+// OC_App::addNavigationEntry(array( "id" => "files_publiclink_administration", "order" => 2, "href" => OC_Helper::linkTo( "files_publiclink", "admin.php" ), "name" => "Public Links"));
+
+
+?>
diff --git a/apps/files_publiclink/appinfo/database.xml b/apps/files_publiclink/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..4fe6be47d8ddce2f649a95430e871c316795a69c
--- /dev/null
+++ b/apps/files_publiclink/appinfo/database.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+	 <name>*dbname*</name>
+	 <create>true</create>
+	 <overwrite>false</overwrite>
+	 <charset>latin1</charset>
+	 <table>
+		<name>*dbprefix*publiclink</name>
+		<declaration>
+			<field>
+				<name>token</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>40</length>
+			</field>
+			<field>
+				<name>path</name>
+				<type>text</type>
+				<default></default>
+				<notnull>true</notnull>
+				<length>128</length>
+			</field>
+			<field>
+				<name>user</name>
+				<type>text</type>
+				<default>
+				</default>
+				<notnull>true</notnull>
+				<length>64</length>
+			 </field>
+			 <field>
+				<name>expire_time</name>
+				<type>timestamp</type>
+				<notnull>true</notnull>
+			</field>
+			<index>
+				<name>a_files_publiclink_token</name>
+				<unique>true</unique>
+				<field>
+					<name>token</name>
+					<sorting>ascending</sorting>
+				</field>
+			</index>
+		</declaration>
+	</table>
+</database>
diff --git a/apps/files_publiclink/appinfo/info.xml b/apps/files_publiclink/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..1d41ea96662cbd63fd296b81b88cc1b0eb7e8aef
--- /dev/null
+++ b/apps/files_publiclink/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>files_publiclink</id>
+	<name>Share by Publiclink</name>
+	<description>Simple file sharing by creating a public link to a file</description>
+	<version>0.2</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>2</require>
+</info>
\ No newline at end of file
diff --git a/apps/files_publiclink/get.php b/apps/files_publiclink/get.php
new file mode 100644
index 0000000000000000000000000000000000000000..2e1ba4bf363cc18bb67231d3a04c9c742696e503
--- /dev/null
+++ b/apps/files_publiclink/get.php
@@ -0,0 +1,83 @@
+<?php
+$RUNTIME_NOAPPS=true; //no need to load the apps
+$RUNTIME_NOSETUPFS=true; //don't setup the fs yet
+
+require_once '../../lib/base.php';
+
+require_once 'lib_public.php';
+
+//get the path of the shared file
+$token=$_GET['token'];
+$path=OC_PublicLink::getPath($token);
+$root=$path;
+
+if($path!==false){
+	if(isset($_GET['path']) and !strstr($_GET['path'],'..')){
+		$subPath=$_GET['path'];
+	}else{
+		$subPath='';
+	}
+	$path.=$subPath;
+	if(!OC_Filesystem::file_exists($path)){
+		header("HTTP/1.0 404 Not Found");
+		$tmpl = new OC_Template( '', '404', 'guest' );
+		$tmpl->assign('file',$subPath);
+		$tmpl->printPage();
+		exit;
+	}
+	if(OC_Filesystem::is_dir($path)){
+		$files = array();
+		$rootLength=strlen($root);
+		foreach( OC_Files::getdirectorycontent( $path ) as $i ){
+			$i['date'] = OC_Util::formatDate($i['mtime'] );
+			$i['directory']=substr($i['directory'],$rootLength);
+			if($i['directory']=='/'){
+				$i['directory']='';
+			}
+			$files[] = $i;
+		}
+		
+		// Make breadcrumb
+		$breadcrumb = array();
+		$pathtohere = "/";
+		foreach( explode( "/", $subPath ) as $i ){
+			if( $i != "" ){
+				$pathtohere .= "$i/";
+				$breadcrumb[] = array( "dir" => $pathtohere, "name" => $i );
+			}
+		}
+		
+		$breadcrumbNav = new OC_Template( "files_publiclink", "breadcrumb", "" );
+		$breadcrumbNav->assign( "breadcrumb", $breadcrumb );
+		$breadcrumbNav->assign('token',$token);
+		
+		$list = new OC_Template( 'files_publiclink', 'files', '' );
+		$list->assign( 'files', $files );
+		$list->assign('token',$token);
+		
+		$tmpl = new OC_Template( 'files_publiclink', 'index', 'user' );
+		$tmpl->assign('fileList', $list->fetchPage());
+		$tmpl->assign( "breadcrumb", $breadcrumbNav->fetchPage() );
+		$tmpl->printPage();
+	}else{
+		//get time mimetype and set the headers
+		$mimetype=OC_Filesystem::getMimeType($path);
+		header('Content-Transfer-Encoding: binary');
+		header('Expires: 0');
+		header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+		header('Pragma: public');
+		header('Content-Disposition: filename="'.basename($path).'"');
+		header('Content-Type: ' . $mimetype);
+		header('Content-Length: ' . OC_Filesystem::filesize($path));
+		
+		//download the file
+		@ob_clean();
+		OC_Filesystem::readfile($path);
+	}
+}else{
+	header("HTTP/1.0 404 Not Found");
+	$tmpl = new OC_Template( '', '404', 'guest' );
+	$tmpl->printPage();
+	die();
+}
+?>
\ No newline at end of file
diff --git a/apps/files_publiclink/js/admin.js b/apps/files_publiclink/js/admin.js
new file mode 100644
index 0000000000000000000000000000000000000000..91ee58beda8b3eb5e1e0dc9b63451e1bac364457
--- /dev/null
+++ b/apps/files_publiclink/js/admin.js
@@ -0,0 +1,45 @@
+$(document).ready(function() {
+	$( "#path" ).autocomplete({
+		source: "../../files/ajax/autocomplete.php",
+		minLength: 1
+	});
+	$(".delete").live('click', function( event ) {
+		event.preventDefault();
+		var token=$(this).attr('data-token');
+		var data="token="+token;
+		$.ajax({
+			type: 'GET',
+			url: 'ajax/deletelink.php',
+			cache: false,
+			data: data,
+			success: function(){
+				$('#'+token).remove();
+			}
+		});
+	});
+	$('#newlink').submit(function( event ){
+		event.preventDefault();
+		var path=$('#path').val();
+		var expire=0;
+		var data='path='+path+'&expire='+expire;
+		$.ajax({
+			type: 'GET',
+			url: 'ajax/makelink.php',
+			cache: false,
+			data: data,
+			success: function(token){
+				if(token){
+					var html="<tr class='link' id='"+token+"'>";
+					html+="<td class='path'>"+path+"</td>";
+					html+="<td class='link'><input type='text' value='"+$('#baseUrl').val()+"?token="+token+"' /></td>"
+					html+="<td><input type='submit' class='delete' data-token='"+token+" value='Delete' /></td>"
+					html+="</tr>"
+					$(html).insertAfter($('#newlink_row'));
+					$('#path').val('');
+					$('#'+token+' input').focus();
+					$('#'+token+' input').select();
+				}
+			}
+		});
+	});
+});
diff --git a/apps/files_publiclink/lib_public.php b/apps/files_publiclink/lib_public.php
new file mode 100644
index 0000000000000000000000000000000000000000..ece0a540d398403db612d6571827f4a4337dd5d0
--- /dev/null
+++ b/apps/files_publiclink/lib_public.php
@@ -0,0 +1,84 @@
+<?php
+class OC_PublicLink{
+	/**
+	 * create a new public link
+	 * @param string path
+	 * @param int (optional) expiretime time the link expires, as timestamp
+	 */
+	public function __construct($path,$expiretime=0){
+		if($path and  OC_Filesystem::file_exists($path) and OC_Filesystem::is_readable($path)){
+			$user=OC_User::getUser();
+			$token=sha1("$user-$path-$expiretime");
+			$query=OC_DB::prepare("INSERT INTO *PREFIX*publiclink VALUES(?,?,?,?)");
+			$result=$query->execute(array($token,$path,$user,$expiretime));
+			if( PEAR::isError($result)) {
+				$entry = 'DB Error: "'.$result->getMessage().'"<br />';
+				$entry .= 'Offending command was: '.$result->getDebugInfo().'<br />';
+				error_log( $entry );
+				die( $entry );
+			}
+			$this->token=$token;
+		}
+	}
+	
+	/**
+	 * get the path of that shared file
+	 */
+	public static function getPath($token) {
+		//get the path and the user
+		$query=OC_DB::prepare("SELECT user,path FROM *PREFIX*publiclink WHERE token=?");
+		$result=$query->execute(array($token));
+		$data=$result->fetchAll();
+		if(count($data)>0){
+			$path=$data[0]['path'];
+			$user=$data[0]['user'];
+			
+			//prepare the filesystem
+			OC_Util::setupFS($user);
+			
+			return $path;
+		}else{
+			return false;
+		}
+	}
+	
+	/**
+	 * get the token for the public link
+	 * @return string
+	 */
+	public function getToken(){
+		return $this->token;
+	}
+
+	public static function getLink($path) {
+		$query=OC_DB::prepare("SELECT token FROM *PREFIX*publiclink WHERE user=? AND path=? LIMIT 1");
+		$result=$query->execute(array(OC_User::getUser(),$path))->fetchAll();
+		if(count($result)>0){
+			return $result[0]['token'];
+		}
+	}
+
+	/**
+	 * gets all public links
+	 * @return array
+	 */
+	static public function getLinks(){
+		$query=OC_DB::prepare("SELECT * FROM *PREFIX*publiclink WHERE user=?");
+		return $query->execute(array(OC_User::getUser()))->fetchAll();
+	}
+
+	/**
+	 * delete a public link
+	 */
+	static public function delete($token){
+		$query=OC_DB::prepare("SELECT user,path FROM *PREFIX*publiclink WHERE token=?");
+		$result=$query->execute(array($token))->fetchAll();
+		if(count($result)>0 and $result[0]['user']==OC_User::getUser()){
+			$query=OC_DB::prepare("DELETE FROM *PREFIX*publiclink WHERE token=?");
+			$query->execute(array($token));
+		}
+	}
+	
+	private $token;
+}
+?>
diff --git a/apps/files_publiclink/templates/admin.php b/apps/files_publiclink/templates/admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..b5c04b838bdd1a9d01c7e05aa2ae59dd602c03d4
--- /dev/null
+++ b/apps/files_publiclink/templates/admin.php
@@ -0,0 +1,18 @@
+<input type="hidden" id="baseUrl" value="<?php echo $_['baseUrl'];?>"/>
+<table id="linklist">
+	<thead id="controls">
+		<tr id="newlink_row"><form action="#" id="newlink">
+			<td class="path"><input placeholder="Path" id="path"/></td>
+			<td><input type="submit" value="Share" /></td>
+		</form></tr>
+	</thead>
+	<tbody>
+		<?php foreach($_['links'] as $link):?>
+		<tr class="link" id="<?php echo $link['token'];?>">
+			<td class="path"><?php echo $link['path'];?></td>
+			<td class="link"><input type="text" value="<?php echo $_['baseUrl'];?>?token=<?php echo $link['token'];?>" /></td>
+			<td><input type="submit" class="delete" data-token="<?php echo $link['token'];?>" value="<?php echo $l->t( 'Delete' ); ?>" /></td>
+		</tr>
+		<?php endforeach;?>
+	</tbody>
+</table>
diff --git a/apps/files_sharing/ajax/getitem.php b/apps/files_sharing/ajax/getitem.php
new file mode 100644
index 0000000000000000000000000000000000000000..249af6cfa31b803c29808cea1bf8399936c4e5c7
--- /dev/null
+++ b/apps/files_sharing/ajax/getitem.php
@@ -0,0 +1,36 @@
+<?php
+$RUNTIME_NOAPPS = true;
+
+require_once('../../../lib/base.php');
+require_once('../lib_share.php');
+
+$userDirectory = "/".OC_User::getUser()."/files";
+$source = $userDirectory.$_GET['source'];
+$path = $source;
+if ($users = OC_Share::getMySharedItem($source)) {
+	for ($i = 0; $i < count($users); $i++) {
+		if ($users[$i]['uid_shared_with'] == OC_Share::PUBLICLINK) {
+			$users[$i]['token'] = OC_Share::getTokenFromSource($source);
+		}
+	}
+}
+$source = dirname($source);
+while ($source != "" && $source != "/" && $source != "." && $source != $userDirectory) {
+	if ($values = OC_Share::getMySharedItem($source)) {
+		$values = array_values($values);
+		$parentUsers = array();
+		for ($i = 0; $i < count($values); $i++) {
+			if ($values[$i]['uid_shared_with'] == OC_Share::PUBLICLINK) {
+				$values[$i]['token'] = OC_Share::getTokenFromSource($source)."&path=".substr($path, strlen($source));
+			}
+			$parentUsers[basename($source)."-".$i] = $values[$i];
+		}
+		$users = array_merge($users, $parentUsers);
+	}
+	$source = dirname($source);
+}
+if (!empty($users)) {
+	echo json_encode($users);
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/ajax/setpermissions.php b/apps/files_sharing/ajax/setpermissions.php
new file mode 100644
index 0000000000000000000000000000000000000000..8e0bac0b06f6dca8d2bd73cdfe26743ece80e826
--- /dev/null
+++ b/apps/files_sharing/ajax/setpermissions.php
@@ -0,0 +1,12 @@
+<?php
+$RUNTIME_NOAPPS = true;
+
+require_once('../../../lib/base.php');
+require_once('../lib_share.php');
+
+$source = "/".OC_User::getUser()."/files".$_GET['source'];
+$uid_shared_with = $_GET['uid_shared_with'];
+$permissions = $_GET['permissions'];
+OC_Share::setPermissions($source, $uid_shared_with, $permissions);
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/ajax/share.php b/apps/files_sharing/ajax/share.php
new file mode 100644
index 0000000000000000000000000000000000000000..e672cf0240342e5903205cf52b9a00eb7ec2f672
--- /dev/null
+++ b/apps/files_sharing/ajax/share.php
@@ -0,0 +1,29 @@
+<?php
+$RUNTIME_NOAPPS = true;
+
+require_once('../../../lib/base.php');
+require_once('../lib_share.php');
+
+$userDirectory = "/".OC_User::getUser()."/files";
+$sources = explode(";", $_POST['sources']);
+$uid_shared_with = $_POST['uid_shared_with'];
+$permissions = $_POST['permissions'];
+foreach ($sources as $source) {
+	// Make sure file exists and can be shared
+	if ($source && OC_FILESYSTEM::file_exists($source) && OC_FILESYSTEM::is_readable($source)) {
+		$source = $userDirectory.$source;
+	// If the file doesn't exist, it may be shared with the current user
+	} else if (!$source = OC_Share::getSource($userDirectory.$source)) {
+		echo "false";
+	}
+	try {
+		$shared = new OC_Share($source, $uid_shared_with, $permissions);
+		if ($uid_shared_with == OC_Share::PUBLICLINK) {
+			echo $shared->getToken();
+		}
+	} catch (Exception $exception) {
+		echo "false";
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/ajax/unshare.php b/apps/files_sharing/ajax/unshare.php
new file mode 100644
index 0000000000000000000000000000000000000000..b9230d257b767d9c32f01cb8446f9b34fedc082b
--- /dev/null
+++ b/apps/files_sharing/ajax/unshare.php
@@ -0,0 +1,11 @@
+<?php
+$RUNTIME_NOAPPS = true;
+
+require_once('../../../lib/base.php');
+require_once('../lib_share.php');
+
+$source = "/".OC_User::getUser()."/files".$_GET['source'];
+$uid_shared_with = $_GET['uid_shared_with'];
+OC_Share::unshare($source, $uid_shared_with);
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/ajax/userautocomplete.php b/apps/files_sharing/ajax/userautocomplete.php
new file mode 100644
index 0000000000000000000000000000000000000000..6da7afb659c957591e47e8a7e1d3006f8d51e623
--- /dev/null
+++ b/apps/files_sharing/ajax/userautocomplete.php
@@ -0,0 +1,28 @@
+<?php
+$RUNTIME_NOAPPS = true;
+
+require_once('../../../lib/base.php');
+
+if (!OC_User::isLoggedIn()) {
+	echo json_encode(array("status" => "error", "data" => array("message" => "Authentication error")));
+	exit();
+}
+$users = array();
+$ocusers = OC_User::getUsers();
+$self = OC_User::getUser();
+$groups = OC_Group::getUserGroups($self);
+$users[] = "<optgroup label='Users'>";
+foreach ($ocusers as $user) {
+	if ($user != $self) {
+		$users[] = "<option value='".$user."'>".$user."</option>";
+	}
+}
+$users[] = "</optgroup>";
+$users[] = "<optgroup label='Groups'>";
+foreach ($groups as $group) {
+	$users[] = "<option value='".$group."'>".$group."</option>";
+}
+$users[] = "</optgroup>";
+echo json_encode($users);
+
+?>
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..c175142319f1d41e4097a0718af2656d143f18d5
--- /dev/null
+++ b/apps/files_sharing/appinfo/app.php
@@ -0,0 +1,14 @@
+<?php
+
+require_once('apps/files_sharing/sharedstorage.php');
+
+OC::$CLASSPATH['OC_Share'] = "apps/files_sharing/lib_share.php";
+OC_Hook::connect("OC_Filesystem", "post_delete", "OC_Share", "deleteItem");
+OC_Hook::connect("OC_Filesystem", "post_rename", "OC_Share", "renameItem");
+OC_Filesystem::registerStorageType("shared", "OC_Filestorage_Shared", array("datadir" => "string"));
+OC_Util::addScript("files_sharing", "share");
+OC_Util::addScript("3rdparty", "chosen/chosen.jquery.min");
+OC_Util::addStyle( 'files_sharing', 'sharing' );
+OC_Util::addStyle("3rdparty", "chosen/chosen");
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/appinfo/database.xml b/apps/files_sharing/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..3378b6b09e53c0756ae1da961cd4db00dabff086
--- /dev/null
+++ b/apps/files_sharing/appinfo/database.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+	 <name>*dbname*</name>
+	 <create>true</create>
+	 <overwrite>false</overwrite>
+	 <charset>latin1</charset>
+	 <table>
+		<name>*dbprefix*sharing</name>
+		<declaration>
+			<field>
+				<name>uid_owner</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+			<field>
+				<name>uid_shared_with</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>64</length>
+			</field>
+			<field>
+				<name>source</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>128</length>
+			</field>
+			<field>
+				<name>target</name>
+				<type>text</type>
+				<notnull>true</notnull>
+				<length>128</length>
+			</field>
+			<field>
+				<name>permissions</name>
+				<type>integer</type>
+				<notnull>true</notnull>
+				<length>1</length>
+			 </field>
+		</declaration>
+	</table>
+</database>
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..2fbb3300f69f7b08671235b3efdc1dd94e0bd7ff
--- /dev/null
+++ b/apps/files_sharing/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>files_sharing</id>
+	<name>Share Files</name>
+	<description>File sharing between users</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Michael Gapczynski</author>
+	<require>2</require>
+</info>
\ No newline at end of file
diff --git a/apps/files_sharing/css/sharing.css b/apps/files_sharing/css/sharing.css
new file mode 100644
index 0000000000000000000000000000000000000000..0759af2c274425098ff35503bff4a914257d0bca
--- /dev/null
+++ b/apps/files_sharing/css/sharing.css
@@ -0,0 +1,8 @@
+#dropdown { display:block;  position:absolute; z-index:100; width:16em; right:0; margin-right:7em; background:#eee; padding:1em;
+-moz-box-shadow:0 1px 1px #777; -webkit-box-shadow:0 1px 1px #777; box-shadow:0 1px 1px #777;
+-moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em;
+-moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; }
+#shared_list { padding:0.5em; list-style-type: none; }
+#public { border-top:1px solid #ddd; padding-top:0.5em; }
+a.unshare { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; opacity:.5; }
+a.unshare:hover { opacity:1; }
\ No newline at end of file
diff --git a/apps/files_sharing/get.php b/apps/files_sharing/get.php
new file mode 100644
index 0000000000000000000000000000000000000000..a1b6c316cd526d49c1d5f00a1e4a67aba42d759b
--- /dev/null
+++ b/apps/files_sharing/get.php
@@ -0,0 +1,83 @@
+<?php
+$RUNTIME_NOAPPS=true; //no need to load the apps
+$RUNTIME_NOSETUPFS=true; //don't setup the fs yet
+
+require_once '../../lib/base.php';
+require_once 'lib_share.php';
+
+//get the path of the shared file
+$token = $_GET['token'];
+$source = OC_Share::getSource($token);
+if ($source !== false) {
+	// TODO Manipulating the string may not be the best choice. Is there an alternative?
+	$user = substr($source, 1, strpos($source, "/", 1) - 1);
+	OC_Util::setupFS($user);
+	$source = substr($source, strlen("/".$user."/files"));
+	$subPath = isset( $_GET['path'] ) ? $_GET['path'] : '';
+	$root = $source;
+	$source .= $subPath;
+	if (!OC_Filesystem::file_exists($source)) {
+		header("HTTP/1.0 404 Not Found");
+		$tmpl = new OC_Template("", "404", "guest");
+		$tmpl->assign("file", $subPath);
+		$tmpl->printPage();
+		exit;
+	}
+	if (OC_Filesystem::is_dir($source)) {
+		$files = array();
+		$rootLength = strlen($root);
+		foreach (OC_Files::getdirectorycontent($source) as $i) {
+			$i['date'] = OC_Util::formatDate($i['mtime'] );
+			if ($i['type'] == 'file') {
+				$i['extention'] = substr($i['name'], strrpos($i['name'], "."));
+				$i['basename'] = substr($i['name'], 0, strrpos($i['name'], "."));
+			}
+			$i['directory'] = substr($i['directory'], $rootLength);
+			if ($i['directory'] == "/") {
+				$i['directory'] = "";
+			}
+			$files[] = $i;
+		}
+		// Make breadcrumb
+		$breadcrumb = array();
+		$pathtohere = "";
+		foreach (explode("/", $subPath) as $i) {
+			if ($i != "") {
+				$pathtohere .= "/$i";
+				$breadcrumb[] = array("dir" => $pathtohere, "name" => $i);
+			}
+		}
+		// Load the files we need
+		OC_Util::addStyle("files", "files");
+		$breadcrumbNav = new OC_Template("files", "part.breadcrumb", "");
+		$breadcrumbNav->assign("breadcrumb", $breadcrumb);
+		$breadcrumbNav->assign("baseURL", OC_Helper::linkTo("files_sharing", "get.php")."?token=".$token."&path=");
+		$list = new OC_Template("files", "part.list", "");
+		$list->assign("files", $files);
+		$list->assign("baseURL", OC_Helper::linkTo("files_sharing", "get.php")."?token=".$token."&path=");
+		$list->assign("downloadURL", OC_Helper::linkTo("files_sharing", "get.php")."?token=".$token."&path=");
+		$tmpl = new OC_Template("files", "index", "user");
+		$tmpl->assign("fileList", $list->fetchPage());
+		$tmpl->assign("breadcrumb", $breadcrumbNav->fetchPage());
+		$tmpl->printPage();
+	} else {
+		//get time mimetype and set the headers
+		$mimetype = OC_Filesystem::getMimeType($source);
+		header("Content-Transfer-Encoding: binary");
+		header("Expires: 0");
+		header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
+		header("Pragma: public");
+		header("Content-Disposition: filename=".basename($source));
+		header("Content-Type: " . $mimetype);
+		header("Content-Length: " . OC_Filesystem::filesize($source));
+		//download the file
+		@ob_clean();
+		OC_Filesystem::readfile($source);
+	}
+} else {
+	header("HTTP/1.0 404 Not Found");
+	$tmpl = new OC_Template("", "404", "guest");
+	$tmpl->printPage();
+	die();
+}
+?>
diff --git a/apps/files_sharing/js/list.js b/apps/files_sharing/js/list.js
new file mode 100644
index 0000000000000000000000000000000000000000..41eabd1f4af7fe56caa71ef034f9f3cdd67179eb
--- /dev/null
+++ b/apps/files_sharing/js/list.js
@@ -0,0 +1,54 @@
+$(document).ready(function() {
+	$( "#source" ).autocomplete({
+		source: "../../files/ajax/autocomplete.php",
+		minLength: 1
+	});
+	$( "#uid_shared_with" ).autocomplete({
+		source: "ajax/userautocomplete.php",
+		minLength: 1
+	});
+	$("button.delete").live('click', function( event ) {
+		event.preventDefault();
+// 		var row=$(this);
+		var source=$(this).attr('data-source');
+		var uid_shared_with=$(this).attr('data-uid_shared_with');
+		var data='source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with);
+		$.ajax({
+			type: 'GET',
+			url: 'ajax/unshare.php',
+			cache: false,
+			data: data
+// 			success: function(){
+// 				row.remove();
+// 			}
+		});
+	});
+	$('#share_item').submit(function( event ){
+		event.preventDefault();
+		var source=$('#source').val();
+		var uid_shared_with=$('#uid_shared_with').val();
+		var permissions=$('#permissions').val()||0;
+		var data='source='+source+'&uid_shared_with='+uid_shared_with+'&permissions='+permissions;
+		$.ajax({
+			type: 'GET',
+			url: 'ajax/share.php',
+			cache: false,
+			data: data,
+// 			success: function(token){
+// 				if(token){
+// 					var html="<tr class='link' id='"+token+"'>";
+// 					html+="<td class='path'>"+path+"</td>";
+// 					var expire=($('#expire').val())?$('#expire').val():'Never'
+// 					html+="<td class='expire'>"+expire+"</td>"
+// 					html+="<td class='link'><a href='get.php?token="+token+"'>"+$('#baseUrl').val()+"?token="+token+"</a></td>"
+// 					html+="<td><button class='delete fancybutton' data-token='"+token+"'>Delete</button></td>"
+// 					html+="</tr>"
+// 					$(html).insertBefore($('#newlink_row'));
+// 					$('#expire').val('');
+// 					$('#expire_time').val('');
+// 					$('#path').val('');
+// 				}
+// 			}
+		});
+	});
+});
\ No newline at end of file
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
new file mode 100644
index 0000000000000000000000000000000000000000..1bd1ac1075b5e47345cd609d96f53ee688c0a6ee
--- /dev/null
+++ b/apps/files_sharing/js/share.js
@@ -0,0 +1,232 @@
+$(document).ready(function() {
+	if (typeof FileActions !== 'undefined') {
+		FileActions.register('all', 'Share', function(filename) {
+			var icon;
+			var file = $('#dir').val()+'/'+filename;
+			$.ajax({
+				type: 'GET',
+				url: OC.linkTo('files_sharing', 'ajax/getitem.php'),
+				dataType: 'json',
+				data: 'source='+file,
+				async: false,
+				success: function(users) {
+					if (users) {
+						icon = OC.imagePath('core', 'actions/shared');
+						$.each(users, function(index, row) {
+							if (row.uid_shared_with == 'public') {
+								icon = OC.imagePath('core', 'actions/public');
+							}
+						});
+					} else {
+						icon = OC.imagePath('core', 'actions/share');
+					}
+				}
+			});
+			return icon;
+		}, function(filename) {
+			if (($('#dropdown').length > 0)) {
+				$('#dropdown').hide('blind', function() {
+					var dropdownFile = $('#dropdown').data('file') 
+					var file = $('#dir').val()+'/'+filename;
+					$('#dropdown').remove();
+					$('tr').removeClass('mouseOver');
+					if (dropdownFile != file) {
+						createDropdown(filename, file);
+					}
+				});
+			} else {
+				createDropdown(filename, $('#dir').val()+'/'+filename);
+			}
+		});
+	};
+
+	$('.share').click(function(event) {
+		event.preventDefault();
+		var filenames = getSelectedFiles('name');
+		var length = filenames.length;
+		var files = '';
+		for (var i = 0; i < length; i++) {
+			files += $('#dir').val()+'/'+filenames[i]+';';
+		}
+		createDropdown(false, files);
+	});
+	
+	$(this).click(function(event) {
+		if (!($(event.target).hasClass('drop')) && $(event.target).parents().index($('#dropdown')) == -1) {
+			if ($('#dropdown').is(':visible')) {
+				$('#dropdown').hide('blind', function() {
+					$('#dropdown').remove();
+					$('tr').removeClass('mouseOver');
+				});
+			}
+		}
+	});
+	
+	$('#share_with').live('change', function() {
+		var source = $('#dropdown').data('file');
+		var uid_shared_with = $(this).val();
+		var permissions = 0;
+		var data = 'sources='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with)+'&permissions='+encodeURIComponent(permissions);
+		$.ajax({
+			type: 'POST',
+			url: OC.linkTo('files_sharing','ajax/share.php'),
+			cache: false,
+			data: data,
+			success: function(result) {
+				if (result !== 'false') {
+					addUser(uid_shared_with, permissions, false);
+				}
+			}
+		});
+	});
+	
+	$('#shared_list > li').live('mouseenter', function(event) {
+		$(':hidden', this).show();
+	});
+	
+	$('#shared_list > li').live('mouseleave', function(event) {
+		$('a', this).hide();
+		if (!$('input:[type=checkbox]', this).is(':checked')) {
+			$('input:[type=checkbox]', this).hide();
+			$('label', this).hide();
+		}
+	});
+	
+	$('.permissions').live('change', function() {
+		var permissions = (this.checked) ? 1 : 0;
+		var source = $('#dropdown').data('file');
+		var uid_shared_with = $(this).parent().data('uid_shared_with');
+		var data = 'source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with)+'&permissions='+encodeURIComponent(permissions);
+		$.ajax({
+			type: 'GET',
+			url: OC.linkTo('files_sharing','ajax/setpermissions.php'),
+			cache: false,
+			data: data
+		});
+	});
+
+	$('.unshare').live('click', function(event) {
+		event.preventDefault();
+		var user = $(this).parent();
+		var source = $('#dropdown').data('file');
+		var uid_shared_with = user.data('uid_shared_with');
+		var data = 'source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with);
+		$.ajax({
+			type: 'GET',
+			url: OC.linkTo('files_sharing','ajax/unshare.php'),
+			cache: false,
+			data: data,
+			success: function() {
+				var option = '<option value="'+uid_shared_with+'">'+uid_shared_with+'</option>';
+				$(user).remove();
+				$(option).appendTo('#share_with');
+				$('#share_with').trigger('liszt:updated');
+			}
+		});
+	});
+	
+	$('#makelink').live('change', function() {
+		if (this.checked) {
+			var source = $('#dropdown').data('file');
+			var uid_shared_with = 'public';
+			var permissions = 0;
+			var data = 'sources='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with)+'&permissions='+encodeURIComponent(permissions);
+			$.ajax({
+				type: 'POST',
+				url: OC.linkTo('files_sharing','ajax/share.php'),
+				cache: false,
+				data: data,
+				success: function(token) {
+					if (token) {
+						showPublicLink(token);
+					}
+				}
+			});
+		} else {
+			var source = $('#dropdown').data('file');
+			var uid_shared_with = 'public';
+			var data = 'source='+encodeURIComponent(source)+'&uid_shared_with='+encodeURIComponent(uid_shared_with);
+			$.ajax({
+				type: 'GET',
+				url: OC.linkTo('files_sharing','ajax/unshare.php'),
+				cache: false,
+				data: data,
+				success: function(){
+					$('#link').hide('blind');
+				}
+			});
+		}
+	});
+	
+	$('#link').live('click', function() {
+		$(this).focus();
+		$(this).select();
+	});
+});
+
+function createDropdown(filename, files) {
+	var html = '<div id="dropdown" class="drop" data-file="'+files+'">';
+	html += '<div id="private">';
+	html += '<select data-placeholder="User or Group" style="width:220px;" id="share_with" class="chzen-select">';
+	html += '<option value=""></option>';
+	html += '</select>';
+	html += '<ul id="shared_list"></ul>';
+	html += '</div>';
+	html += '<div id="public">';
+	html += '<input type="checkbox" name="makelink" id="makelink" value="1" /><label for="makelink">make public</label>';
+	//html += '<input type="checkbox" name="public_link_write" id="public_link_write" value="1" /><label for="public_link_write">allow upload</label>';
+	html += '<br />';
+	html += '<input id="link" style="display:none; width:90%;" />';
+	html += '</div>';
+	if (filename) {
+		$('tr[data-file="'+filename+'"]').addClass('mouseOver');
+		$(html).appendTo($('tr[data-file="'+filename+'"] td.filename'));
+	} else {
+		$(html).appendTo($('thead .share'));
+	}
+	$.getJSON(OC.linkTo('files_sharing', 'ajax/userautocomplete.php'), function(users) {
+		if (users) {
+			$.each(users, function(index, row) {
+				$(row).appendTo('#share_with');
+			});
+			$('#share_with').trigger('liszt:updated');
+		}
+	});
+	$.getJSON(OC.linkTo('files_sharing', 'ajax/getitem.php'), { source: files }, function(users) {
+		if (users) {
+			$.each(users, function(index, row) {
+				if (row.uid_shared_with == 'public') {
+					showPublicLink(row.token);
+				} else if (isNaN(index)) {
+					addUser(row.uid_shared_with, row.permissions, index.substr(0, index.lastIndexOf('-')));
+				} else {
+					addUser(row.uid_shared_with, row.permissions, false);
+				}
+			});
+		}
+	});
+	$('#dropdown').show('blind');
+	$('#share_with').chosen();
+}
+
+function addUser(uid_shared_with, permissions, parentFolder) {
+	if (parentFolder) {
+		var user = '<li>Parent folder '+parentFolder+' shared with '+uid_shared_with+'</li>';
+	} else {
+		var checked = ((permissions > 0) ? 'checked="checked"' : 'style="display:none;"');
+		var style = ((permissions == 0) ? 'style="display:none;"' : '');
+		var user = '<li data-uid_shared_with="'+uid_shared_with+'">'+uid_shared_with;
+		user += '<input type="checkbox" name="permissions" id="'+uid_shared_with+'" class="permissions" "+checked+" /><label for="'+uid_shared_with+'" '+style+'>can edit</label>';
+		user += '<a href="" class="unshare" style="display:none;"><img class="svg" alt="Unshare" src="'+OC.imagePath('core','actions/delete')+'"/></a></li>';
+	}
+	$('#share_with option[value="'+uid_shared_with+'"]').remove();
+	$('#share_with').trigger('liszt:updated');
+	$(user).appendTo('#shared_list');
+}
+
+function showPublicLink(token) {
+	$('#makelink').attr('checked', true);
+	$('#link').data('token', token);
+	$('#link').val(parent.location.protocol+'//'+location.host+OC.linkTo('files_sharing','get.php')+'?token='+token);
+	$('#link').show('blind');
+}
diff --git a/apps/files_sharing/lib_share.php b/apps/files_sharing/lib_share.php
new file mode 100644
index 0000000000000000000000000000000000000000..978847f4a8f8ec3f514b1cd965bbe43ed241866f
--- /dev/null
+++ b/apps/files_sharing/lib_share.php
@@ -0,0 +1,399 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Michael Gapczynski
+ * @copyright 2011 Michael Gapczynski GapczynskiM@gmail.com
+ *
+ * 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 manages shared items within the database. 
+ */
+class OC_Share {
+
+	const WRITE = 1;
+	const DELETE = 2;
+	const UNSHARED = -1;
+	const PUBLICLINK = "public";
+
+	private $token;
+      
+	/**
+	 * Share an item, adds an entry into the database
+	 * @param $source The source location of the item
+	 * @param $uid_shared_with The user or group to share the item with
+	 * @param $permissions The permissions, use the constants WRITE and DELETE
+	 */
+	public function __construct($source, $uid_shared_with, $permissions) {
+		$uid_owner = OC_User::getUser();
+		$query = OC_DB::prepare("INSERT INTO *PREFIX*sharing VALUES(?,?,?,?,?)");
+		if ($uid_shared_with == self::PUBLICLINK) {
+			$token = sha1("$uid_shared_with-$source");
+			$query->execute(array($uid_owner, self::PUBLICLINK, $source, $token, $permissions));
+			$this->token = $token;
+		} else {
+			if (OC_Group::groupExists($uid_shared_with)) {
+				$gid = $uid_shared_with;
+				$uid_shared_with = OC_Group::usersInGroup($gid);
+				// Remove the owner from the list of users in the group
+				$uid_shared_with = array_diff($uid_shared_with, array($uid_owner));
+			} else if (OC_User::userExists($uid_shared_with)) {
+				$gid = null;
+				$uid_shared_with = array($uid_shared_with);
+			} else {
+				throw new Exception($uid_shared_with." is not a user");
+			}
+			foreach ($uid_shared_with as $uid) {
+				// Check if this item is already shared with the user
+				$checkSource = OC_DB::prepare("SELECT source FROM *PREFIX*sharing WHERE source = ? AND uid_shared_with ".self::getUsersAndGroups($uid));
+				$resultCheckSource = $checkSource->execute(array($source, $uid))->fetchAll();
+				// TODO Check if the source is inside a folder
+				if (count($resultCheckSource) > 0 && !isset($gid)) {
+					throw new Exception("This item is already shared with ".$uid);
+				}
+				// Check if the target already exists for the user, if it does append a number to the name
+				$sharedFolder = "/".$uid."/files/Shared";
+				$target = $sharedFolder."/".basename($source);
+				if (self::getSource($target)) {
+					if ($pos = strrpos($target, ".")) {
+						$name = substr($target, 0, $pos);
+						$ext = substr($target, $pos);
+					} else {
+						$name = $target;
+						$ext = "";
+					}
+					$counter = 1;
+					while ($checkTarget !== false) {
+						$newTarget = $name."_".$counter.$ext;
+						$checkTarget = self::getSource($newTarget);
+						$counter++;
+					}
+					$target = $newTarget;
+				}
+				if (isset($gid)) {
+					$uid = $uid."@".$gid;
+				}
+				$query->execute(array($uid_owner, $uid, $source, $target, $permissions));
+				// Clear the folder size cache for the 'Shared' folder
+				$clearFolderSize = OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
+				$clearFolderSize->execute(array($sharedFolder));
+			}
+		}
+	}
+
+	/**
+	* Remove any duplicate or trailing '/' from the path
+	* @return A clean path
+	*/
+	private static function cleanPath($path) {
+		$path = rtrim($path, "/");
+		return preg_replace('{(/)\1+}', "/", $path);
+	}
+
+	/**
+	* Generate a string to be used for searching for uid_shared_with that handles both users and groups
+	* @param $uid (Optional) The uid to get the user groups for, a gid to get the users in a group, or if not set the current user
+	* @return An IN operator as a string
+	*/
+	private static function getUsersAndGroups($uid = null) {
+		$in = " IN(";
+		if (isset($uid) && OC_Group::groupExists($uid)) {
+			$users = OC_Group::usersInGroup($uid);
+			foreach ($users as $user) {
+				// Add a comma only if the the current element isn't the last
+				if ($user !== end($users)) {
+					$in .= "'".$user."@".$uid."', ";
+				} else {
+					$in .= "'".$user."@".$uid."'";
+				}
+			}
+		} else if (isset($uid)) {
+			// TODO Check if this is necessary, only constructor needs it as IN. It would be better for other queries to just return =$uid
+			$in .= "'".$uid."'";
+			$groups = OC_Group::getUserGroups($uid);
+			foreach ($groups as $group) {
+				$in .= ", '".$uid."@".$group."'";
+			}
+		} else {
+			$uid = OC_User::getUser();
+			$in .= "'".$uid."'";
+			$groups = OC_Group::getUserGroups($uid);
+			foreach ($groups as $group) {
+				$in .= ", '".$uid."@".$group."'";
+			}
+		}
+		$in .= ", '".self::PUBLICLINK."'";
+		$in .= ")";
+		return $in;
+	}
+
+	/**
+	 * Create a new entry in the database for a file inside a shared folder
+	 *
+	 * $oldTarget and $newTarget may be the same value. $oldTarget exists in case the file is being moved outside of the folder
+	 *
+	 * @param $oldTarget The current target location
+	 * @param $newTarget The new target location
+	 */
+	public static function pullOutOfFolder($oldTarget, $newTarget) {
+		$folders = self::getParentFolders($oldTarget);
+		$source = $folders['source'].substr($oldTarget, strlen($folders['target']));
+		$item = self::getItem($folders['target']);
+		$query = OC_DB::prepare("INSERT INTO *PREFIX*sharing VALUES(?,?,?,?,?)");
+		$query->execute(array($item[0]['uid_owner'], OC_User::getUser(), $source, $newTarget, $item[0]['permissions']));
+	}
+
+	/**
+	 * Get the item with the specified target location
+	 * @param $target The target location of the item
+	 * @return An array with the item
+	 */
+	public static function getItem($target) {
+		$target = self::cleanPath($target);
+		$query = OC_DB::prepare("SELECT uid_owner, source, permissions FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with = ? LIMIT 1");
+		return $query->execute(array($target, OC_User::getUser()))->fetchAll();
+	}
+
+	 /**
+	 * Get the item with the specified source location
+	 * @param $source The source location of the item
+	 * @return An array with the users and permissions the item is shared with
+	 */
+	public static function getMySharedItem($source) {
+		$source = self::cleanPath($source);
+		$query = OC_DB::prepare("SELECT uid_shared_with, permissions FROM *PREFIX*sharing WHERE source = ? AND uid_owner = ?");
+		$result = $query->execute(array($source, OC_User::getUser()))->fetchAll();
+		if (count($result) > 0) {
+			return $result;
+		} else if ($originalSource = self::getSource($source)) {
+			return $query->execute(array($originalSource, OC_User::getUser()))->fetchAll();
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Get all items the current user is sharing
+	 * @return An array with all items the user is sharing
+	 */
+	public static function getMySharedItems() {
+		$query = OC_DB::prepare("SELECT uid_shared_with, source, permissions FROM *PREFIX*sharing WHERE uid_owner = ?");
+		return $query->execute(array(OC_User::getUser()))->fetchAll();
+	}
+
+	/**
+	 * Get the items within a shared folder that have their own entry for the purpose of name, location, or permissions that differ from the folder itself
+	 *
+	 * Works for both target and source folders. Can be used for getting all items shared with you e.g. pass '/MTGap/files'
+	 *
+	 * @param $folder The folder of the items to look for
+	 * @return An array with all items in the database that are in the folder
+	 */
+	public static function getItemsInFolder($folder) {
+		$folder = self::cleanPath($folder);
+		// Append '/' in order to filter out the folder itself if not already there
+		if (substr($folder, -1) !== "/") {
+			$folder .= "/";
+		}
+		$length = strlen($folder);
+		$query = OC_DB::prepare("SELECT uid_owner, source, target, permissions FROM *PREFIX*sharing WHERE SUBSTR(source, 1, ?) = ? OR SUBSTR(target, 1, ?) = ? AND uid_shared_with ".self::getUsersAndGroups());
+		return $query->execute(array($length, $folder, $length, $folder))->fetchAll();
+	}
+
+	/**
+	 * Get the source and target parent folders of the specified target location
+	 * @param $target The target location of the item
+	 * @return An array with the keys 'source' and 'target' with the values of the source and target parent folders
+	 */
+	public static function getParentFolders($target) {
+		$target = self::cleanPath($target);
+		$query = OC_DB::prepare("SELECT source FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with".self::getUsersAndGroups()." LIMIT 1");
+		// Prevent searching for user directory e.g. '/MTGap/files'
+		$userDirectory = substr($target, 0, strpos($target, "files") + 5);
+		$target = dirname($target);
+		$result = array();
+		while ($target != "" && $target != "/" && $target != "." && $target != $userDirectory) {
+			// Check if the parent directory of this target location is shared
+			$result = $query->execute(array($target))->fetchAll();
+			if (count($result) > 0) {
+				break;
+			}
+			$target = dirname($target);
+		}
+		if (count($result) > 0) {
+			// Return both the source folder and the target folder
+			return array("source" => $result[0]['source'], "target" => $target);
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Get the source location of the item at the specified target location
+	 * @param $target The target location of the item
+	 * @return Source location or false if target location is not valid
+	 */
+	public static function getSource($target) {
+		$target = self::cleanPath($target);
+		$query = OC_DB::prepare("SELECT source FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with ".self::getUsersAndGroups()." LIMIT 1");
+		$result = $query->execute(array($target))->fetchAll();
+		if (count($result) > 0) {
+			return $result[0]['source'];
+		} else {
+			$folders = self::getParentFolders($target);
+			if ($folders == true) {
+				return $folders['source'].substr($target, strlen($folders['target']));
+			} else {
+				return false;
+			}
+		}
+	}
+
+	/**
+	 * Get the user's permissions for the item at the specified target location
+	 * @param $target The target location of the item
+	 * @return The permissions, use bitwise operators to check against the constants WRITE and DELETE
+	 */
+	public static function getPermissions($target) {
+		$target = self::cleanPath($target);
+		$query = OC_DB::prepare("SELECT permissions FROM *PREFIX*sharing WHERE target = ? AND uid_shared_with ".self::getUsersAndGroups()." LIMIT 1");
+		$result = $query->execute(array($target))->fetchAll();
+		if (count($result) > 0) {
+			return $result[0]['permissions'];
+		} else {
+			$folders = self::getParentFolders($target);
+			if ($folders == true) {
+				$result = $query->execute(array($folders['target']))->fetchAll();
+				if (count($result) > 0) {
+					return $result[0]['permissions'];
+				}
+			} else {
+				return false;
+			}
+		}
+	}
+
+	/**
+	 * Get the token for a public link
+	 * @return The token of the public link, a sha1 hash
+	 */
+	public function getToken() {
+		return $this->token;
+	}
+
+	/**
+	 * Get the token for a public link
+	 * @param $source The source location of the item
+	 * @return The token of the public link, a sha1 hash
+	 */
+	public static function getTokenFromSource($source) {
+		$query = OC_DB::prepare("SELECT target FROM *PREFIX*sharing WHERE source = ? AND uid_shared_with = ? AND uid_owner = ? LIMIT 1");
+		$result = $query->execute(array($source, self::PUBLICLINK, OC_User::getUser()))->fetchAll();
+		if (count($result) > 0) {
+			return $result[0]['target'];
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * Set the target location to a new value
+	 *
+	 * You must use the pullOutOfFolder() function to change the target location of a file inside a shared folder if the target location differs from the folder
+	 *
+	 * @param $oldTarget The current target location
+	 * @param $newTarget The new target location 
+	 */
+	public static function setTarget($oldTarget, $newTarget) {
+		$oldTarget = self::cleanPath($oldTarget);
+		$newTarget = self::cleanPath($newTarget);
+		$query = OC_DB::prepare("UPDATE *PREFIX*sharing SET target = REPLACE(target, ?, ?) WHERE uid_shared_with ".self::getUsersAndGroups());
+		$query->execute(array($oldTarget, $newTarget));
+	}
+
+	/**
+	* Change the permissions for the specified item and user
+	*
+	* You must construct a new shared item to change the permissions of a file inside a shared folder if the permissions differ from the folder
+	*
+	* @param $source The source location of the item
+	* @param $uid_shared_with The user to change the permissions for
+	* @param $permissions The permissions, use the constants WRITE and DELETE
+	*/
+	public static function setPermissions($source, $uid_shared_with, $permissions) {
+		$source = self::cleanPath($source);
+		$query = OC_DB::prepare("UPDATE *PREFIX*sharing SET permissions = ? WHERE SUBSTR(source, 1, ?) = ? AND uid_owner = ? AND uid_shared_with ".self::getUsersAndGroups($uid_shared_with));
+		$query->execute(array($permissions, strlen($source), $source, OC_User::getUser()));
+	}
+
+	/**
+	* Unshare the item, removes it from all specified users
+	*
+	* You must use the pullOutOfFolder() function to unshare a file inside a shared folder and set $newTarget to nothing
+	*
+	* @param $source The source location of the item
+	* @param $uid_shared_with Array of users to unshare the item from
+	*/
+	public static function unshare($source, $uid_shared_with) {
+		$source = self::cleanPath($source);
+		$query = OC_DB::prepare("DELETE FROM *PREFIX*sharing WHERE SUBSTR(source, 1, ?) = ? AND uid_owner = ? AND uid_shared_with ".self::getUsersAndGroups($uid_shared_with));
+		$query->execute(array(strlen($source), $source, OC_User::getUser()));
+	}
+
+	/**
+	* Unshare the item from the current user, removes it only from the database and doesn't touch the source file
+	*
+	* You must use the pullOutOfFolder() function before you call unshareFromMySelf() and set the delete parameter to false to unshare from self a file inside a shared folder
+	*
+	* @param $target The target location of the item
+	* @param $delete (Optional) If true delete the entry from the database, if false the permission is set to UNSHARED
+	*/
+	public static function unshareFromMySelf($target, $delete = true) {
+		$target = self::cleanPath($target);
+		if ($delete) {
+			$query = OC_DB::prepare("DELETE FROM *PREFIX*sharing WHERE SUBSTR(target, 1, ?) = ? AND uid_shared_with ".self::getUsersAndGroups());
+			$query->execute(array(strlen($target), $target));
+		} else {
+			$query = OC_DB::prepare("UPDATE *PREFIX*sharing SET permissions = ? WHERE SUBSTR(target, 1, ?) = ? AND uid_shared_with ".self::getUsersAndGroups());
+			$query->execute(array(self::UNSHARED, strlen($target), $target));
+		}
+	}
+
+	/**
+	* Remove the item from the database, the owner deleted the file
+	* @param $arguments Array of arguments passed from OC_Hook
+	*/
+	public static function deleteItem($arguments) {
+		$source = "/".OC_User::getUser()."/files".self::cleanPath($arguments['path']);
+		$query = OC_DB::prepare("DELETE FROM *PREFIX*sharing WHERE SUBSTR(source, 1, ?) = ? AND uid_owner = ?");
+		$query->execute(array(strlen($source), $source, OC_User::getUser()));
+	}
+
+	/**
+	* Rename the item in the database, the owner renamed the file
+	* @param $arguments Array of arguments passed from OC_Hook
+	*/
+	public static function renameItem($arguments) {
+		$oldSource = "/".OC_User::getUser()."/files".self::cleanPath($arguments['oldpath']);
+		$newSource = "/".OC_User::getUser()."/files".self::cleanPath($arguments['newpath']);
+		$query = OC_DB::prepare("UPDATE *PREFIX*sharing SET source = REPLACE(source, ?, ?) WHERE uid_owner = ?");
+		$query->execute(array($oldSource, $newSource, OC_User::getUser()));
+	}
+
+}
+
+?>
diff --git a/apps/files_sharing/list.php b/apps/files_sharing/list.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a11f438eb7b59073a72b419e3584a9266e3a880
--- /dev/null
+++ b/apps/files_sharing/list.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Michael Gapczynski
+ * @copyright 2011 Michael Gapczynski GapczynskiM@gmail.com
+ *
+ * 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('../../lib/base.php');
+require_once('lib_share.php');
+
+if (!OC_User::isLoggedIn()){
+	header( "Location: ".OC_HELPER::linkTo( "index.php" ));
+	exit();
+}
+
+OC_App::setActiveNavigationEntry("files_sharing_list");
+
+OC_Util::addScript("files_sharing", "list");
+
+$tmpl = new OC_Template("files_sharing", "list", "user");
+$tmpl->assign("shared_items", OC_Share::getMySharedItems());
+$tmpl->printPage();
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..53f25b60e91cb734381c9f54b19c06ff984f5084
--- /dev/null
+++ b/apps/files_sharing/sharedstorage.php
@@ -0,0 +1,579 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Michael Gapczynski
+ * @copyright 2011 Michael Gapczynski GapczynskiM@gmail.com
+ *
+ * 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( 'lib_share.php' );
+
+/**
+ * Convert target path to source path and pass the function call to the correct storage provider
+ */
+class OC_Filestorage_Shared extends OC_Filestorage {
+	
+	private $datadir;
+	private $sourcePaths = array();
+	
+	public function __construct($arguments) {
+		$this->datadir = $arguments['datadir'];
+		if (OC_Share::getItemsInFolder($this->datadir)) {
+			if (!OC_Filesystem::is_dir($this->datadir)) { 
+				OC_Filesystem::mkdir($this->datadir);
+			}
+		} else  if (OC_Filesystem::is_dir($this->datadir)) {
+			OC_Filesystem::rmdir($this->datadir);
+		}
+		$this->datadir .= "/";
+	}
+	
+	public function getInternalPath($path) {
+		$mountPoint = OC_Filesystem::getMountPoint($path);
+		$internalPath = substr($path, strlen($mountPoint));
+		return $internalPath;
+	}
+	
+	public function getSource($target) {
+		$target = $this->datadir.$target;
+		if (array_key_exists($target, $this->sourcePaths)) {
+			return $this->sourcePaths[$target];
+		} else {
+			$source = OC_Share::getSource($target);
+			$this->sourcePaths[$target] = $source;
+			return $source;
+		}
+	}
+	
+	public function mkdir($path) {
+		if ($path == "" || $path == "/" || !$this->is_writeable($path)) {
+			return false; 
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->mkdir($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function rmdir($path) {
+		// The folder will be removed from the database, but won't be deleted from the owner's filesystem
+		OC_Share::unshareFromMySelf($this->datadir.$path);
+		$this->clearFolderSizeCache($path);
+	}
+	
+	public function opendir($path) {
+		if ($path == "" || $path == "/") {
+			$path = $this->datadir.$path;
+			$sharedItems = OC_Share::getItemsInFolder($path);
+			if (empty($sharedItems)) {
+				return false;
+			} else {
+				global $FAKEDIRS;
+				$files = array();
+				foreach ($sharedItems as $item) {
+					// If item is in the root of the shared storage provider and the item exists add it to the fakedirs
+					if (dirname($item['target'])."/" == $path && $this->file_exists(basename($item['target']))) {
+						$files[] = basename($item['target']);
+					}
+				}
+				$FAKEDIRS['shared'] = $files;
+				return opendir('fakedir://shared');
+			}
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				$dh = $storage->opendir($this->getInternalPath($source));
+				$modifiedItems = OC_Share::getItemsInFolder($source);
+				if ($modifiedItems && $dh) {
+					$sources = array();
+					$targets = array();
+					// Remove any duplicate or trailing '/'
+					$path = preg_replace('{(/)\1+}', "/", $path);
+					$targetFolder = rtrim($this->datadir.$path, "/");
+					foreach ($modifiedItems as $item) {
+						// If the item is in the current directory and the item exists add it to the arrays
+						if (dirname($item['target']) == $targetFolder && $this->file_exists($path."/".basename($item['target']))) {
+							// If the item was unshared from self, add it it to the arrays
+							if ($item['permissions'] == OC_Share::UNSHARED) {
+								$sources[] = basename($item['source']);
+								$targets[] = "";
+							} else {
+								$sources[] = basename($item['source']);
+								$targets[] = basename($item['target']);
+							}
+						}
+					}
+					// Don't waste time if there aren't any modified items in the current directory
+					if (empty($sources)) {
+						return $dh;
+					} else {
+						global $FAKEDIRS;
+						$files = array();
+						while (($filename = readdir($dh)) !== false) {
+							if ($filename != "." && $filename != "..") {
+								// If the file isn't in the sources array it isn't modified and can be added as is
+								if (!in_array($filename, $sources)) {
+									$files[] = $filename;
+								// The file has a different name than the source and is added to the fakedirs
+								} else {
+									$target = $targets[array_search($filename, $sources)];
+									// Don't add the file if it was unshared from self by the user
+									if ($target != "") {
+										$files[] = $target;
+									}
+								}
+							}
+						}
+						$FAKEDIRS['shared'] = $files;
+						return opendir('fakedir://shared');
+					}
+				} else {
+					return $dh;
+				}
+			}
+		}
+	}
+	
+	public function is_dir($path) {
+		if ($path == "" || $path == "/") {
+			return true;
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->is_dir($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function is_file($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->is_file($this->getInternalPath($source));
+		}
+	}
+	
+	// TODO fill in other components of array
+	public function stat($path) {
+		if ($path == "" || $path == "/") {
+			$stat["dev"] = "";
+			$stat["ino"] = "";
+			$stat["mode"] = "";
+			$stat["nlink"] = "";
+			$stat["uid"] = "";
+			$stat["gid"] = "";
+			$stat["rdev"] = "";
+			$stat["size"] = $this->filesize($path);
+			$stat["atime"] = $this->fileatime($path);
+			$stat["mtime"] = $this->filemtime($path);
+			$stat["ctime"] = $this->filectime($path);
+			$stat["blksize"] = "";
+			$stat["blocks"] = "";
+			return $stat;
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->stat($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function filetype($path) {
+		if ($path == "" || $path == "/") {
+			return "dir";
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->filetype($this->getInternalPath($source));
+			}
+		}
+
+	}
+	
+	public function filesize($path) {
+		if ($path == "" || $path == "/" || $this->is_dir($path)) {
+			return $this->getFolderSize($path);
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->filesize($this->getInternalPath($source));
+			}
+		}
+	}
+
+	public function getFolderSize($path) {
+		// Shared folder sizes are cached separately from the source folder sizes because folders can have different names
+		$path = rtrim($path, "/");
+		$path = ltrim($path, "/");
+		$path = preg_replace('{(/)\1+}', "/", $path);
+		$dbpath = rtrim($this->datadir.$path, "/");
+		$query = OC_DB::prepare("SELECT size FROM *PREFIX*foldersize WHERE path = ?");
+		$size = $query->execute(array($dbpath))->fetchAll();
+		if (count($size) > 0) {
+			return $size[0]['size'];
+		} else {
+			return $this->calculateFolderSize($path);
+		}
+	}
+	
+	private function calculateFolderSize($path) {
+		if ($this->is_file($path)) {
+			$path = dirname($path);
+		}
+		$size = 0;
+		if ($dh = $this->opendir($path)) {
+			while (($filename = readdir($dh)) !== false) {
+				if ($filename != "." && $filename != "..") {
+					$subFile = $path."/".$filename;
+					if ($this->is_file($subFile)) {
+						$size += $this->filesize($subFile);
+					} else {
+						$size += $this->getFolderSize($subFile);
+					}
+				}
+			}
+			if ($size > 0) {
+				$dbpath = rtrim($this->datadir.$path, "/");
+				$query = OC_DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)");
+				$result = $query->execute(array($dbpath, $size));
+			}
+		}
+		return $size;
+	}
+
+	private function clearFolderSizeCache($path) {
+		$path = rtrim($path, "/");
+		$path = preg_replace('{(/)\1+}', "/", $path);
+		if ($this->is_file($path)) {
+			$path = dirname($path);
+		}
+		$dbpath = rtrim($this->datadir.$path, "/");
+		$query = OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
+		$result = $query->execute(array($dbpath));
+		if ($path != "/" && $path != "") {
+			$parts = explode("/", $path);
+			$part = array_pop($parts);
+			if (empty($part)) {
+				array_pop($parts);
+			}
+			$parent = implode("/", $parts);
+			$this->clearFolderSizeCache($parent);
+		}
+	}
+
+	public function is_readable($path) {
+		return true;
+	}
+	
+	public function is_writeable($path) {
+		if ($path == "" || $path == "/" || OC_Share::getPermissions($this->datadir.$path) & OC_Share::WRITE) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	public function file_exists($path) {
+		if ($path == "" || $path == "/") {
+			return true;
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->file_exists($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function readfile($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->readfile($this->getInternalPath($source));
+		}	
+	}
+	
+	public function filectime($path) {
+		if ($path == "" || $path == "/") {
+			$ctime = 0; 
+			if ($dh = $this->opendir($path)) {
+				while (($filename = readdir($dh)) !== false) {
+					$tempctime = $this->filectime($filename);
+					if ($tempctime < $ctime) {
+						$ctime = $tempctime;
+					}
+				}
+				return $ctime;
+			}
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->filectime($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function filemtime($path) {
+		if ($path == "" || $path == "/") {
+			$mtime = 0; 
+			if ($dh = $this->opendir($path)) {
+				while (($filename = readdir($dh)) !== false) {
+					$tempmtime = $this->filemtime($filename);
+					if ($tempmtime > $mtime) {
+						$mtime = $tempmtime;
+					}
+				}
+				return $mtime;
+			}
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->filemtime($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function fileatime($path) {
+		if ($path == "" || $path == "/") {
+			$atime = 0; 
+			if ($dh = $this->opendir($path)) {
+				while (($filename = readdir($dh)) !== false) {
+					$tempatime = $this->fileatime($filename);
+					if ($tempatime > $atime) {
+						$atime = $tempatime;
+					}
+				}
+				return $atime;
+			}
+		} else {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				return $storage->fileatime($this->getInternalPath($source));
+			}
+		}
+	}
+	
+	public function file_get_contents($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->file_get_contents($this->getInternalPath($source));
+		}
+	}
+	
+	public function file_put_contents($path, $data) {
+		if ($this->is_writeable($path)) {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				$result = $storage->file_put_contents($this->getInternalPath($source), $data);
+				if ($result) {
+					$this->clearFolderSizeCache($path);
+				}
+				return $result;
+			}
+		}
+	}
+	
+	public function unlink($path) {
+		// The item will be removed from the database, but won't be touched on the owner's filesystem
+		$target = $this->datadir.$path;
+		// Check if the item is inside a shared folder
+		if (OC_Share::getParentFolders($target)) {
+			// If entry for item already exists
+			if (OC_Share::getItem($target)) {
+				OC_Share::unshareFromMySelf($target, false);
+			} else {
+				OC_Share::pullOutOfFolder($target, $target);
+				OC_Share::unshareFromMySelf($target, false);
+			}
+		// Delete the database entry
+		} else {
+			OC_Share::unshareFromMySelf($target);
+		}
+		$this->clearFolderSizeCache($this->getInternalPath($target));
+		return true;
+	}
+	
+	public function rename($path1, $path2) {
+		$oldTarget = $this->datadir.$path1;
+		$newTarget = $this->datadir.$path2;
+		// Check if the item is inside a shared folder
+		if ($folders = OC_Share::getParentFolders($oldTarget)) {
+			$root1 = substr($path1, 0, strpos($path1, "/"));
+			$root2 = substr($path1, 0, strpos($path2, "/"));
+			// Prevent items from being moved into different shared folders until versioning (cut and paste) and prevent items going into 'Shared'
+			if ($root1 !== $root2) {
+				return false;
+			// Check if both paths have write permission
+			} else if ($this->is_writeable($path1) && $this->is_writeable($path2)) {
+				$oldSource = $this->getSource($path1);
+				$newSource = $folders['source'].substr($newTarget, strlen($folders['target']));
+				if ($oldSource) {
+					$storage = OC_Filesystem::getStorage($oldSource);
+					return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource));
+				}
+			// If the user doesn't have write permission, items can only be renamed and not moved
+			} else if (dirname($path1) !== dirname($path2)) {
+				return false;
+			// The item will be renamed in the database, but won't be touched on the owner's filesystem
+			} else {
+				OC_Share::pullOutOfFolder($oldTarget, $newTarget);
+				// If this is a folder being renamed, call setTarget in case there are any database entries inside the folder
+				if (self::is_dir($path1)) {
+					OC_Share::setTarget($oldTarget, $newTarget);
+				}
+			}
+		} else {
+			OC_Share::setTarget($oldTarget, $newTarget);
+		}
+		$this->clearFolderSizeCache($this->getInternalPath($oldTarget));
+		$this->clearFolderSizeCache($this->getInternalPath($newTarget));
+		return true;
+	}
+	
+	public function copy($path1, $path2) {
+		if ($path2 == "" || $path2 == "/") {
+			// TODO Construct new shared item or should this not be allowed?
+		} else {
+			if ($this->is_writeable($path2)) {
+				$tmpFile = $this->toTmpFile($path1);
+				$result = $this->fromTmpFile($tmpFile, $path2);
+				if ($result) {
+					$this->clearFolderSizeCache($path2);
+				}
+				return $result;
+			} else {
+				return false;
+			}
+		}
+	}
+	
+	public function fopen($path, $mode) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->fopen($this->getInternalPath($source), $mode);
+		}
+	}
+	
+	public function toTmpFile($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->toTmpFile($this->getInternalPath($source));
+		}
+	}
+	
+	public function fromTmpFile($tmpFile, $path) {
+		if ($this->is_writeable($path)) {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				$result = $storage->fromTmpFile($tmpFile, $this->getInternalPath($source));
+				if ($result) {
+					$this->clearFolderSizeCache($path);
+				}
+				return $result;
+			}
+		} else {
+			return false;
+		}
+	}
+	
+	public function fromUploadedFile($tmpFile, $path) {
+		if ($this->is_writeable($path)) {
+			$source = $this->getSource($path);
+			if ($source) {
+				$storage = OC_Filesystem::getStorage($source);
+				$result = $storage->fromUploadedFile($tmpFile, $this->getInternalPath($source));
+				if ($result) {
+					$this->clearFolderSizeCache($path);
+				}
+				return $result;
+			}
+		} else {
+			return false;
+		}
+	}
+	
+	public function getMimeType($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->getMimeType($this->getInternalPath($source));
+		}
+	}
+	
+	public function hash($type, $path, $raw) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->hash($type, $this->getInternalPath($source), $raw);
+		}
+	}
+	
+	public function free_space($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->free_space($this->getInternalPath($source));
+		}
+	}
+	
+	public function search($query) {
+		return $this->searchInDir($query);
+	}
+
+	private function searchInDir($query, $path = "") {
+		$files = array();
+		if ($dh = $this->opendir($path)) {
+			while (($filename = readdir($dh)) !== false) {
+				if ($filename != "." && $filename != "..") {
+					if (strstr(strtolower($filename), strtolower($query))) {
+						$files[] = $path."/".$filename;
+					}
+					if ($this->is_dir($path."/".$filename)) {
+						$files = array_merge($files, $this->searchInDir($query, $path."/".$filename));
+					}
+				}
+			}
+		}
+		return $files;
+	}
+
+	public function getLocalFile($path) {
+		$source = $this->getSource($path);
+		if ($source) {
+			$storage = OC_Filesystem::getStorage($source);
+			return $storage->getLocalFile($this->getInternalPath($source));
+		}
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php
new file mode 100644
index 0000000000000000000000000000000000000000..7faf2cf4ba611676280bd92c838616bdad36ed0e
--- /dev/null
+++ b/apps/files_sharing/templates/list.php
@@ -0,0 +1,30 @@
+<fieldset>
+	<legend>Your Shared Files</legend>
+	<table id="itemlist">
+		<thead>
+			<tr>
+				<th>Item</th>
+				<th>Shared With</th>
+				<th>Permissions</th>
+			</tr>
+		</thead>
+		<tbody>
+			<?php foreach($_['shared_items'] as $item):?>
+				<tr class="item">
+					<td class="source"><?php echo substr($item['source'], strlen("/".$_SESSION['user_id']."/files/"));?></td>
+					<td class="uid_shared_with"><?php echo $item['uid_shared_with'];?></td>
+					<td class="permissions"><?php echo "Read"; echo($item['permissions'] & OC_SHARE::WRITE ? ", Edit" : ""); echo($item['permissions'] & OC_SHARE::DELETE ? ", Delete" : "");?></td>
+					<td><button class="delete" data-source="<?php echo $item['source'];?>" data-uid_shared_with="<?php echo $item['uid_shared_with'];?>">Delete</button></td>
+				</tr>
+			<?php endforeach;?>
+			<tr id="share_item_row">
+				<form action="#" id="share_item">
+					<td class="source"><input placeholder="Item" id="source" /></td>
+					<td class="uid_shared_with"><input placeholder="Share With" id="uid_shared_with" /></td>
+					<td class="permissions"><input placeholder="Permissions" id="permissions" /></td>
+					<td><input type="submit" value="Share" /></td>
+				</form>
+			</tr>
+		</tbody>
+	</table>
+</fieldset>
diff --git a/apps/files_textviewer/appinfo/app.php b/apps/files_textviewer/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..7cf1905a5302d9eaa9d9f18e331d5c9b641a913e
--- /dev/null
+++ b/apps/files_textviewer/appinfo/app.php
@@ -0,0 +1,5 @@
+<?php
+//load the required js file
+OC_UTIL::addScript('files_textviewer','textviewer');
+
+?>
diff --git a/apps/files_textviewer/appinfo/info.xml b/apps/files_textviewer/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..209b414034639a609d82b535a9cd26bf6ab3a73b
--- /dev/null
+++ b/apps/files_textviewer/appinfo/info.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0"?> 
+<info>
+	<id>files_textviewer</id>
+	<name>Text viewer</name>
+	<version>0.3</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>2</require>
+</info>
diff --git a/apps/files_textviewer/css/style.css b/apps/files_textviewer/css/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..b70fc0e572c38886f6ba1a4a3b2aca3a5a050932
--- /dev/null
+++ b/apps/files_textviewer/css/style.css
@@ -0,0 +1,38 @@
+#textframe{
+	position:fixed;
+	top:0px;
+	left:0px;
+	height:100%;
+	width:100%;
+	background:rgb(20,20,20);
+	background:rgba(20,20,20,0.9);
+	text-align:center;
+	z-index:100;
+}
+
+#textframe>div{
+	vertical-align:middle;
+	text-align:left;
+	height:auto;
+	max-height:90%;
+	max-width:90%;
+	margin:20px;
+	margin-left:auto;
+	margin-right:auto;
+	margin-bottom:10px;
+	padding:0px;
+	border: black solid 3px;
+	background:black;
+	color:white;
+	overflow:auto;
+}
+
+#textframe>div>div>div{
+	margin:0px !important;
+	top:0px !important;
+	height:100% !important;
+}
+
+#textframe>div div,#textframe>div td{
+	overflow:hidden !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCore.css b/apps/files_textviewer/css/syntaxhighlighter/shCore.css
new file mode 100644
index 0000000000000000000000000000000000000000..34f6864a155b18bcca36d86f39c3034cc6d49424
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCore.css
@@ -0,0 +1,226 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreDefault.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreDefault.css
new file mode 100644
index 0000000000000000000000000000000000000000..08f9e10e4ea57944826f5c22baedbd1dc1627771
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreDefault.css
@@ -0,0 +1,328 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #e0e0e0 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: black !important;
+}
+.syntaxhighlighter table caption {
+  color: black !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #6ce26c !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #6ce26c !important;
+  color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: blue !important;
+  background: white !important;
+  border: 1px solid #6ce26c !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: blue !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: red !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #6ce26c !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: black !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: blue !important;
+}
+.syntaxhighlighter .keyword {
+  color: #006699 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #006699 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreDjango.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreDjango.css
new file mode 100644
index 0000000000000000000000000000000000000000..1db1f70cb0d047242c62fc795a54dd61277544c2
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreDjango.css
@@ -0,0 +1,331 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #233729 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: #f8f8f8 !important;
+}
+.syntaxhighlighter .gutter {
+  color: #497958 !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #41a83e !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #41a83e !important;
+  color: #0a2b1d !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #96dd3b !important;
+  background: black !important;
+  border: 1px solid #41a83e !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #96dd3b !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #41a83e !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #ffe862 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #f8f8f8 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #336442 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #9df39f !important;
+}
+.syntaxhighlighter .keyword {
+  color: #96dd3b !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #91bb9e !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #96dd3b !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #eb939a !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #91bb9e !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #edef7d !important;
+}
+
+.syntaxhighlighter .comments {
+  font-style: italic !important;
+}
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreEclipse.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreEclipse.css
new file mode 100644
index 0000000000000000000000000000000000000000..a45de9fd8e34e6c73fe0b85043c9a068bdf88081
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreEclipse.css
@@ -0,0 +1,339 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #c3defe !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: black !important;
+}
+.syntaxhighlighter .gutter {
+  color: #787878 !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #d4d0c8 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #d4d0c8 !important;
+  color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #3f5fbf !important;
+  background: white !important;
+  border: 1px solid #d4d0c8 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #3f5fbf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: #a0a0a0 !important;
+  background: #d4d0c8 !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: #a0a0a0 !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: red !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #3f5fbf !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #2a00ff !important;
+}
+.syntaxhighlighter .keyword {
+  color: #7f0055 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #646464 !important;
+}
+.syntaxhighlighter .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #7f0055 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .xml .keyword {
+  color: #3f7f7f !important;
+  font-weight: normal !important;
+}
+.syntaxhighlighter .xml .color1, .syntaxhighlighter .xml .color1 a {
+  color: #7f007f !important;
+}
+.syntaxhighlighter .xml .string {
+  font-style: italic !important;
+  color: #2a00ff !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreEmacs.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreEmacs.css
new file mode 100644
index 0000000000000000000000000000000000000000..706c77a0a8562a24a8975e68d0af50dba06fbc51
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreEmacs.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: black !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: black !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: black !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #2a3133 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter {
+  color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #990000 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #990000 !important;
+  color: black !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #ebdb8d !important;
+  background: black !important;
+  border: 1px solid #990000 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #ebdb8d !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #ff7d27 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #990000 !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #d3d3d3 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #ff7d27 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #ff9e7b !important;
+}
+.syntaxhighlighter .keyword {
+  color: aqua !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #aec4de !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #81cef9 !important;
+}
+.syntaxhighlighter .constants {
+  color: #ff9e7b !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: aqua !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #ebdb8d !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff7d27 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #aec4de !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreFadeToGrey.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreFadeToGrey.css
new file mode 100644
index 0000000000000000000000000000000000000000..6101eba51f0bbe078a1d83e4b08066453778c8e6
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreFadeToGrey.css
@@ -0,0 +1,328 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #121212 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #2c2c29 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: white !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #3185b9 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #3185b9 !important;
+  color: #121212 !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #3185b9 !important;
+  background: black !important;
+  border: 1px solid #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #d01d33 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #3185b9 !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #96daff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: white !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #696854 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #e3e658 !important;
+}
+.syntaxhighlighter .keyword {
+  color: #d01d33 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+  color: #898989 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #aaaaaa !important;
+}
+.syntaxhighlighter .constants {
+  color: #96daff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #d01d33 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #ffc074 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #4a8cdb !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #96daff !important;
+}
+
+.syntaxhighlighter .functions {
+  font-weight: bold !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreMDUltra.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreMDUltra.css
new file mode 100644
index 0000000000000000000000000000000000000000..2923ce7367b54cb7124ae253e4d58a0fd141b96d
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreMDUltra.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #222222 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: lime !important;
+}
+.syntaxhighlighter .gutter {
+  color: #38566f !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #435a5f !important;
+  color: #222222 !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #428bdd !important;
+  background: black !important;
+  border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: lime !important;
+}
+.syntaxhighlighter .toolbar {
+  color: #aaaaff !important;
+  background: #435a5f !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: #aaaaff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: lime !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: lime !important;
+}
+.syntaxhighlighter .keyword {
+  color: #aaaaff !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+  color: aqua !important;
+}
+.syntaxhighlighter .value {
+  color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff8000 !important;
+}
+.syntaxhighlighter .constants {
+  color: yellow !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #aaaaff !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: red !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: yellow !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #ffaa3e !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreMidnight.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreMidnight.css
new file mode 100644
index 0000000000000000000000000000000000000000..e3733eed566d76ba97db8f15fdcb63f809a6fd30
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreMidnight.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: #38566f !important;
+}
+.syntaxhighlighter table caption {
+  color: #d1edff !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #435a5f !important;
+  color: #0f192a !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #428bdd !important;
+  background: black !important;
+  border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #1dc116 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: #d1edff !important;
+  background: #435a5f !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: #d1edff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #8aa6c1 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #d1edff !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #1dc116 !important;
+}
+.syntaxhighlighter .keyword {
+  color: #b43d3d !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #b43d3d !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #f8bb00 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #ffaa3e !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shCoreRDark.css b/apps/files_textviewer/css/syntaxhighlighter/shCoreRDark.css
new file mode 100644
index 0000000000000000000000000000000000000000..d09368384dafd9b1f9650fe012f4c590a52312d3
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shCoreRDark.css
@@ -0,0 +1,324 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter a,
+.syntaxhighlighter div,
+.syntaxhighlighter code,
+.syntaxhighlighter table,
+.syntaxhighlighter table td,
+.syntaxhighlighter table tr,
+.syntaxhighlighter table tbody,
+.syntaxhighlighter table thead,
+.syntaxhighlighter table caption,
+.syntaxhighlighter textarea {
+  -moz-border-radius: 0 0 0 0 !important;
+  -webkit-border-radius: 0 0 0 0 !important;
+  background: none !important;
+  border: 0 !important;
+  bottom: auto !important;
+  float: none !important;
+  height: auto !important;
+  left: auto !important;
+  line-height: 1.1em !important;
+  margin: 0 !important;
+  outline: 0 !important;
+  overflow: visible !important;
+  padding: 0 !important;
+  position: static !important;
+  right: auto !important;
+  text-align: left !important;
+  top: auto !important;
+  vertical-align: baseline !important;
+  width: auto !important;
+  box-sizing: content-box !important;
+  font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important;
+  font-weight: normal !important;
+  font-style: normal !important;
+  font-size: 1em !important;
+  min-height: inherit !important;
+  min-height: auto !important;
+}
+
+.syntaxhighlighter {
+  width: 100% !important;
+  margin: 1em 0 1em 0 !important;
+  position: relative !important;
+  overflow: auto !important;
+  font-size: 1em !important;
+}
+.syntaxhighlighter.source {
+  overflow: hidden !important;
+}
+.syntaxhighlighter .bold {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .italic {
+  font-style: italic !important;
+}
+.syntaxhighlighter .line {
+  white-space: pre !important;
+}
+.syntaxhighlighter table {
+  width: 100% !important;
+}
+.syntaxhighlighter table caption {
+  text-align: left !important;
+  padding: .5em 0 0.5em 1em !important;
+}
+.syntaxhighlighter table td.code {
+  width: 100% !important;
+}
+.syntaxhighlighter table td.code .container {
+  position: relative !important;
+}
+.syntaxhighlighter table td.code .container textarea {
+  box-sizing: border-box !important;
+  position: absolute !important;
+  left: 0 !important;
+  top: 0 !important;
+  width: 100% !important;
+  height: 100% !important;
+  border: none !important;
+  background: white !important;
+  padding-left: 1em !important;
+  overflow: hidden !important;
+  white-space: pre !important;
+}
+.syntaxhighlighter table td.gutter .line {
+  text-align: right !important;
+  padding: 0 0.5em 0 1em !important;
+}
+.syntaxhighlighter table td.code .line {
+  padding: 0 1em !important;
+}
+.syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line {
+  padding-left: 0em !important;
+}
+.syntaxhighlighter.show {
+  display: block !important;
+}
+.syntaxhighlighter.collapsed table {
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  padding: 0.1em 0.8em 0em 0.8em !important;
+  font-size: 1em !important;
+  position: static !important;
+  width: auto !important;
+  height: auto !important;
+}
+.syntaxhighlighter.collapsed .toolbar span {
+  display: inline !important;
+  margin-right: 1em !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a {
+  padding: 0 !important;
+  display: none !important;
+}
+.syntaxhighlighter.collapsed .toolbar span a.expandSource {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar {
+  position: absolute !important;
+  right: 1px !important;
+  top: 1px !important;
+  width: 11px !important;
+  height: 11px !important;
+  font-size: 10px !important;
+  z-index: 10 !important;
+}
+.syntaxhighlighter .toolbar span.title {
+  display: inline !important;
+}
+.syntaxhighlighter .toolbar a {
+  display: block !important;
+  text-align: center !important;
+  text-decoration: none !important;
+  padding-top: 1px !important;
+}
+.syntaxhighlighter .toolbar a.expandSource {
+  display: none !important;
+}
+.syntaxhighlighter.ie {
+  font-size: .9em !important;
+  padding: 1px 0 1px 0 !important;
+}
+.syntaxhighlighter.ie .toolbar {
+  line-height: 8px !important;
+}
+.syntaxhighlighter.ie .toolbar a {
+  padding-top: 0px !important;
+}
+.syntaxhighlighter.printing .line.alt1 .content,
+.syntaxhighlighter.printing .line.alt2 .content,
+.syntaxhighlighter.printing .line.highlighted .number,
+.syntaxhighlighter.printing .line.highlighted.alt1 .content,
+.syntaxhighlighter.printing .line.highlighted.alt2 .content {
+  background: none !important;
+}
+.syntaxhighlighter.printing .line .number {
+  color: #bbbbbb !important;
+}
+.syntaxhighlighter.printing .line .content {
+  color: black !important;
+}
+.syntaxhighlighter.printing .toolbar {
+  display: none !important;
+}
+.syntaxhighlighter.printing a {
+  text-decoration: none !important;
+}
+.syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a {
+  color: black !important;
+}
+.syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a {
+  color: blue !important;
+}
+.syntaxhighlighter.printing .keyword {
+  color: #006699 !important;
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter.printing .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter.printing .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter.printing .script {
+  font-weight: bold !important;
+}
+.syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a {
+  color: red !important;
+}
+.syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a {
+  color: black !important;
+}
+
+.syntaxhighlighter {
+  background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #323e41 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: #b9bdb6 !important;
+}
+.syntaxhighlighter table caption {
+  color: #b9bdb6 !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #435a5f !important;
+  color: #1b2426 !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #5ba1cf !important;
+  background: black !important;
+  border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #5ba1cf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #5ce638 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #435a5f !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #b9bdb6 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #878a85 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #5ce638 !important;
+}
+.syntaxhighlighter .keyword {
+  color: #5ba1cf !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #5ba1cf !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #ffaa3e !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeDefault.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeDefault.css
new file mode 100644
index 0000000000000000000000000000000000000000..136541172dc3be4c5d5e7b2731d6453cc38897ca
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeDefault.css
@@ -0,0 +1,117 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #e0e0e0 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: black !important;
+}
+.syntaxhighlighter table caption {
+  color: black !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #6ce26c !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #6ce26c !important;
+  color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: blue !important;
+  background: white !important;
+  border: 1px solid #6ce26c !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: blue !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: red !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #6ce26c !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: black !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #008200 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: blue !important;
+}
+.syntaxhighlighter .keyword {
+  color: #006699 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: gray !important;
+}
+.syntaxhighlighter .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #006699 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeDjango.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeDjango.css
new file mode 100644
index 0000000000000000000000000000000000000000..d8b4313433d107db3eaa13447994c62adc3cb339
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeDjango.css
@@ -0,0 +1,120 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #0a2b1d !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #233729 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: #f8f8f8 !important;
+}
+.syntaxhighlighter .gutter {
+  color: #497958 !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #41a83e !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #41a83e !important;
+  color: #0a2b1d !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #96dd3b !important;
+  background: black !important;
+  border: 1px solid #41a83e !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #96dd3b !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #41a83e !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #ffe862 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #f8f8f8 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #336442 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #9df39f !important;
+}
+.syntaxhighlighter .keyword {
+  color: #96dd3b !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #91bb9e !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #96dd3b !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #eb939a !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #91bb9e !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #edef7d !important;
+}
+
+.syntaxhighlighter .comments {
+  font-style: italic !important;
+}
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeEclipse.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeEclipse.css
new file mode 100644
index 0000000000000000000000000000000000000000..77377d9533477c6b808306a9c04bec2ffa528103
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeEclipse.css
@@ -0,0 +1,128 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: white !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #c3defe !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: black !important;
+}
+.syntaxhighlighter .gutter {
+  color: #787878 !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #d4d0c8 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #d4d0c8 !important;
+  color: white !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #3f5fbf !important;
+  background: white !important;
+  border: 1px solid #d4d0c8 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #3f5fbf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: #a0a0a0 !important;
+  background: #d4d0c8 !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: #a0a0a0 !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: red !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: black !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #3f5fbf !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #2a00ff !important;
+}
+.syntaxhighlighter .keyword {
+  color: #7f0055 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #646464 !important;
+}
+.syntaxhighlighter .variable {
+  color: #aa7700 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .constants {
+  color: #0066cc !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #7f0055 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: gray !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff1493 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: red !important;
+}
+
+.syntaxhighlighter .keyword {
+  font-weight: bold !important;
+}
+.syntaxhighlighter .xml .keyword {
+  color: #3f7f7f !important;
+  font-weight: normal !important;
+}
+.syntaxhighlighter .xml .color1, .syntaxhighlighter .xml .color1 a {
+  color: #7f007f !important;
+}
+.syntaxhighlighter .xml .string {
+  font-style: italic !important;
+  color: #2a00ff !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeEmacs.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeEmacs.css
new file mode 100644
index 0000000000000000000000000000000000000000..dae5053fea8a6cef6b763f97a6461e307a50107d
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeEmacs.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: black !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: black !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: black !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #2a3133 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter {
+  color: #d3d3d3 !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #990000 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #990000 !important;
+  color: black !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #ebdb8d !important;
+  background: black !important;
+  border: 1px solid #990000 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #ebdb8d !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #ff7d27 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #990000 !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #d3d3d3 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #ff7d27 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #ff9e7b !important;
+}
+.syntaxhighlighter .keyword {
+  color: aqua !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #aec4de !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #81cef9 !important;
+}
+.syntaxhighlighter .constants {
+  color: #ff9e7b !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: aqua !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #ebdb8d !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #ff7d27 !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #aec4de !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeFadeToGrey.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeFadeToGrey.css
new file mode 100644
index 0000000000000000000000000000000000000000..8fbd871fb5bc1a33b3a4b35bccdcbf5baab553da
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeFadeToGrey.css
@@ -0,0 +1,117 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #121212 !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #121212 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #2c2c29 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: white !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #3185b9 !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #3185b9 !important;
+  color: #121212 !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #3185b9 !important;
+  background: black !important;
+  border: 1px solid #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #3185b9 !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #d01d33 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #3185b9 !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #96daff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: white !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #696854 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #e3e658 !important;
+}
+.syntaxhighlighter .keyword {
+  color: #d01d33 !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+  color: #898989 !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #aaaaaa !important;
+}
+.syntaxhighlighter .constants {
+  color: #96daff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #d01d33 !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #ffc074 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: #4a8cdb !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #96daff !important;
+}
+
+.syntaxhighlighter .functions {
+  font-weight: bold !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeMDUltra.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeMDUltra.css
new file mode 100644
index 0000000000000000000000000000000000000000..f4db39cd8392096234ced3928d3d58cf806c03b2
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeMDUltra.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #222222 !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #222222 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: white !important;
+}
+.syntaxhighlighter table caption {
+  color: lime !important;
+}
+.syntaxhighlighter .gutter {
+  color: #38566f !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #435a5f !important;
+  color: #222222 !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #428bdd !important;
+  background: black !important;
+  border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: lime !important;
+}
+.syntaxhighlighter .toolbar {
+  color: #aaaaff !important;
+  background: #435a5f !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: #aaaaff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #9ccff4 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: lime !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: lime !important;
+}
+.syntaxhighlighter .keyword {
+  color: #aaaaff !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+  color: aqua !important;
+}
+.syntaxhighlighter .value {
+  color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ff8000 !important;
+}
+.syntaxhighlighter .constants {
+  color: yellow !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #aaaaff !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: red !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: yellow !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #ffaa3e !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeMidnight.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeMidnight.css
new file mode 100644
index 0000000000000000000000000000000000000000..c49563cc9dbf5d71be4157ae4805f0fc2a4d0e1d
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeMidnight.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #0f192a !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #253e5a !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: #38566f !important;
+}
+.syntaxhighlighter table caption {
+  color: #d1edff !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #435a5f !important;
+  color: #0f192a !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #428bdd !important;
+  background: black !important;
+  border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #1dc116 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: #d1edff !important;
+  background: #435a5f !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: #d1edff !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #8aa6c1 !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #d1edff !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #428bdd !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #1dc116 !important;
+}
+.syntaxhighlighter .keyword {
+  color: #b43d3d !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #8aa6c1 !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #f7e741 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #b43d3d !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #f8bb00 !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #ffaa3e !important;
+}
diff --git a/apps/files_textviewer/css/syntaxhighlighter/shThemeRDark.css b/apps/files_textviewer/css/syntaxhighlighter/shThemeRDark.css
new file mode 100644
index 0000000000000000000000000000000000000000..6305a10e4ebb09f56b8da07f8d15918f6001d863
--- /dev/null
+++ b/apps/files_textviewer/css/syntaxhighlighter/shThemeRDark.css
@@ -0,0 +1,113 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+.syntaxhighlighter {
+  background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt1 {
+  background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.alt2 {
+  background-color: #1b2426 !important;
+}
+.syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 {
+  background-color: #323e41 !important;
+}
+.syntaxhighlighter .line.highlighted.number {
+  color: #b9bdb6 !important;
+}
+.syntaxhighlighter table caption {
+  color: #b9bdb6 !important;
+}
+.syntaxhighlighter .gutter {
+  color: #afafaf !important;
+}
+.syntaxhighlighter .gutter .line {
+  border-right: 3px solid #435a5f !important;
+}
+.syntaxhighlighter .gutter .line.highlighted {
+  background-color: #435a5f !important;
+  color: #1b2426 !important;
+}
+.syntaxhighlighter.printing .line .content {
+  border: none !important;
+}
+.syntaxhighlighter.collapsed {
+  overflow: visible !important;
+}
+.syntaxhighlighter.collapsed .toolbar {
+  color: #5ba1cf !important;
+  background: black !important;
+  border: 1px solid #435a5f !important;
+}
+.syntaxhighlighter.collapsed .toolbar a {
+  color: #5ba1cf !important;
+}
+.syntaxhighlighter.collapsed .toolbar a:hover {
+  color: #5ce638 !important;
+}
+.syntaxhighlighter .toolbar {
+  color: white !important;
+  background: #435a5f !important;
+  border: none !important;
+}
+.syntaxhighlighter .toolbar a {
+  color: white !important;
+}
+.syntaxhighlighter .toolbar a:hover {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .plain, .syntaxhighlighter .plain a {
+  color: #b9bdb6 !important;
+}
+.syntaxhighlighter .comments, .syntaxhighlighter .comments a {
+  color: #878a85 !important;
+}
+.syntaxhighlighter .string, .syntaxhighlighter .string a {
+  color: #5ce638 !important;
+}
+.syntaxhighlighter .keyword {
+  color: #5ba1cf !important;
+}
+.syntaxhighlighter .preprocessor {
+  color: #435a5f !important;
+}
+.syntaxhighlighter .variable {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .value {
+  color: #009900 !important;
+}
+.syntaxhighlighter .functions {
+  color: #ffaa3e !important;
+}
+.syntaxhighlighter .constants {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .script {
+  font-weight: bold !important;
+  color: #5ba1cf !important;
+  background-color: none !important;
+}
+.syntaxhighlighter .color1, .syntaxhighlighter .color1 a {
+  color: #e0e8ff !important;
+}
+.syntaxhighlighter .color2, .syntaxhighlighter .color2 a {
+  color: white !important;
+}
+.syntaxhighlighter .color3, .syntaxhighlighter .color3 a {
+  color: #ffaa3e !important;
+}
diff --git a/apps/files_textviewer/js/syntaxhighlighter/LGPL-LICENSE b/apps/files_textviewer/js/syntaxhighlighter/LGPL-LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..3f9959fc566752e1cb49215957764d4b135fccdd
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/LGPL-LICENSE
@@ -0,0 +1,165 @@
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions. 
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version. 
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
\ No newline at end of file
diff --git a/apps/files_textviewer/js/syntaxhighlighter/MIT-LICENSE b/apps/files_textviewer/js/syntaxhighlighter/MIT-LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..e7c70ba14a4a2ba8771dcf6dcd9fb460d6a0352c
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2003, 2004 Jim Weirich
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shAutoloader.js b/apps/files_textviewer/js/syntaxhighlighter/shAutoloader.js
new file mode 100644
index 0000000000000000000000000000000000000000..4e29bddecbbcdb01c669fb2c89aca4a2505a59bc
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shAutoloader.js
@@ -0,0 +1,17 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('(2(){1 h=5;h.I=2(){2 n(c,a){4(1 d=0;d<c.9;d++)i[c[d]]=a}2 o(c){1 a=r.H("J"),d=3;a.K=c;a.M="L/t";a.G="t";a.u=a.v=2(){6(!d&&(!8.7||8.7=="F"||8.7=="z")){d=q;e[c]=q;a:{4(1 p y e)6(e[p]==3)B a;j&&5.C(k)}a.u=a.v=x;a.D.O(a)}};r.N.R(a)}1 f=Q,l=h.P(),i={},e={},j=3,k=x,b;5.T=2(c){k=c;j=q};4(b=0;b<f.9;b++){1 m=f[b].w?f[b]:f[b].S(/\\s+/),g=m.w();n(m,g)}4(b=0;b<l.9;b++)6(g=i[l[b].E.A]){e[g]=3;o(g)}}})();',56,56,'|var|function|false|for|SyntaxHighlighter|if|readyState|this|length|||||||||||||||||true|document||javascript|onload|onreadystatechange|pop|null|in|complete|brush|break|highlight|parentNode|params|loaded|language|createElement|autoloader|script|src|text|type|body|removeChild|findElements|arguments|appendChild|split|all'.split('|'),0,{}))
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushAS3.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushAS3.js
new file mode 100644
index 0000000000000000000000000000000000000000..8aa3ed2732adc3bb5ca65124348d2438efdd2ba0
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushAS3.js
@@ -0,0 +1,59 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Created by Peter Atoria @ http://iAtoria.com
+	
+		var inits 	 =  'class interface function package';
+	
+		var keywords =	'-Infinity ...rest Array as AS3 Boolean break case catch const continue Date decodeURI ' + 
+						'decodeURIComponent default delete do dynamic each else encodeURI encodeURIComponent escape ' + 
+						'extends false final finally flash_proxy for get if implements import in include Infinity ' + 
+						'instanceof int internal is isFinite isNaN isXMLName label namespace NaN native new null ' + 
+						'Null Number Object object_proxy override parseFloat parseInt private protected public ' + 
+						'return set static String super switch this throw true try typeof uint undefined unescape ' + 
+						'use void while with'
+						;
+	
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'comments' },		// one line comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'comments' },		// multiline comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },		// double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },		// single quoted strings
+			{ regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi,				css: 'value' },			// numbers
+			{ regex: new RegExp(this.getKeywords(inits), 'gm'),			css: 'color3' },		// initializations
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword' },		// keywords
+			{ regex: new RegExp('var', 'gm'),							css: 'variable' },		// variable
+			{ regex: new RegExp('trace', 'gm'),							css: 'color1' }			// trace
+			];
+	
+		this.forHtmlScript(SyntaxHighlighter.regexLib.scriptScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['actionscript3', 'as3'];
+
+	SyntaxHighlighter.brushes.AS3 = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushAppleScript.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushAppleScript.js
new file mode 100644
index 0000000000000000000000000000000000000000..d40bbd7dd262cad9bad0184b46f1641cfe5e9950
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushAppleScript.js
@@ -0,0 +1,75 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// AppleScript brush by David Chambers
+		// http://davidchambersdesign.com/
+		var keywords   = 'after before beginning continue copy each end every from return get global in local named of set some that the then times to where whose with without';
+		var ordinals   = 'first second third fourth fifth sixth seventh eighth ninth tenth last front back middle';
+		var specials   = 'activate add alias AppleScript ask attachment boolean class constant delete duplicate empty exists false id integer list make message modal modified new no paragraph pi properties quit real record remove rest result reveal reverse run running save string true word yes';
+
+		this.regexList = [
+
+			{ regex: /(--|#).*$/gm,
+			    css: 'comments' },
+
+			{ regex: /\(\*(?:[\s\S]*?\(\*[\s\S]*?\*\))*[\s\S]*?\*\)/gm, // support nested comments
+			    css: 'comments' },
+
+			{ regex: /"[\s\S]*?"/gm,
+			    css: 'string' },
+
+			{ regex: /(?:,|:|¬|'s\b|\(|\)|\{|\}|«|\b\w*»)/g,
+			    css: 'color1' },
+
+			{ regex: /(-)?(\d)+(\.(\d)?)?(E\+(\d)+)?/g, // numbers
+			    css: 'color1' },
+
+			{ regex: /(?:&(amp;|gt;|lt;)?|=|� |>|<|≥|>=|≤|<=|\*|\+|-|\/|÷|\^)/g,
+			    css: 'color2' },
+
+			{ regex: /\b(?:and|as|div|mod|not|or|return(?!\s&)(ing)?|equals|(is(n't| not)? )?equal( to)?|does(n't| not) equal|(is(n't| not)? )?(greater|less) than( or equal( to)?)?|(comes|does(n't| not) come) (after|before)|is(n't| not)?( in)? (back|front) of|is(n't| not)? behind|is(n't| not)?( (in|contained by))?|does(n't| not) contain|contain(s)?|(start|begin|end)(s)? with|((but|end) )?(consider|ignor)ing|prop(erty)?|(a )?ref(erence)?( to)?|repeat (until|while|with)|((end|exit) )?repeat|((else|end) )?if|else|(end )?(script|tell|try)|(on )?error|(put )?into|(of )?(it|me)|its|my|with (timeout( of)?|transaction)|end (timeout|transaction))\b/g,
+			    css: 'keyword' },
+
+			{ regex: /\b\d+(st|nd|rd|th)\b/g, // ordinals
+			    css: 'keyword' },
+
+			{ regex: /\b(?:about|above|against|around|at|below|beneath|beside|between|by|(apart|aside) from|(instead|out) of|into|on(to)?|over|since|thr(ough|u)|under)\b/g,
+			    css: 'color3' },
+
+			{ regex: /\b(?:adding folder items to|after receiving|choose( ((remote )?application|color|folder|from list|URL))?|clipboard info|set the clipboard to|(the )?clipboard|entire contents|display(ing| (alert|dialog|mode))?|document( (edited|file|nib name))?|file( (name|type))?|(info )?for|giving up after|(name )?extension|quoted form|return(ed)?|second(?! item)(s)?|list (disks|folder)|text item(s| delimiters)?|(Unicode )?text|(disk )?item(s)?|((current|list) )?view|((container|key) )?window|with (data|icon( (caution|note|stop))?|parameter(s)?|prompt|properties|seed|title)|case|diacriticals|hyphens|numeric strings|punctuation|white space|folder creation|application(s( folder)?| (processes|scripts position|support))?|((desktop )?(pictures )?|(documents|downloads|favorites|home|keychain|library|movies|music|public|scripts|sites|system|users|utilities|workflows) )folder|desktop|Folder Action scripts|font(s| panel)?|help|internet plugins|modem scripts|(system )?preferences|printer descriptions|scripting (additions|components)|shared (documents|libraries)|startup (disk|items)|temporary items|trash|on server|in AppleTalk zone|((as|long|short) )?user name|user (ID|locale)|(with )?password|in (bundle( with identifier)?|directory)|(close|open for) access|read|write( permission)?|(g|s)et eof|using( delimiters)?|starting at|default (answer|button|color|country code|entr(y|ies)|identifiers|items|name|location|script editor)|hidden( answer)?|open(ed| (location|untitled))?|error (handling|reporting)|(do( shell)?|load|run|store) script|administrator privileges|altering line endings|get volume settings|(alert|boot|input|mount|output|set) volume|output muted|(fax|random )?number|round(ing)?|up|down|toward zero|to nearest|as taught in school|system (attribute|info)|((AppleScript( Studio)?|system) )?version|(home )?directory|(IPv4|primary Ethernet) address|CPU (type|speed)|physical memory|time (stamp|to GMT)|replacing|ASCII (character|number)|localized string|from table|offset|summarize|beep|delay|say|(empty|multiple) selections allowed|(of|preferred) type|invisibles|showing( package contents)?|editable URL|(File|FTP|News|Media|Web) [Ss]ervers|Telnet hosts|Directory services|Remote applications|waiting until completion|saving( (in|to))?|path (for|to( (((current|frontmost) )?application|resource))?)|POSIX (file|path)|(background|RGB) color|(OK|cancel) button name|cancel button|button(s)?|cubic ((centi)?met(re|er)s|yards|feet|inches)|square ((kilo)?met(re|er)s|miles|yards|feet)|(centi|kilo)?met(re|er)s|miles|yards|feet|inches|lit(re|er)s|gallons|quarts|(kilo)?grams|ounces|pounds|degrees (Celsius|Fahrenheit|Kelvin)|print( (dialog|settings))?|clos(e(able)?|ing)|(de)?miniaturized|miniaturizable|zoom(ed|able)|attribute run|action (method|property|title)|phone|email|((start|end)ing|home) page|((birth|creation|current|custom|modification) )?date|((((phonetic )?(first|last|middle))|computer|host|maiden|related) |nick)?name|aim|icq|jabber|msn|yahoo|address(es)?|save addressbook|should enable action|city|country( code)?|formatte(r|d address)|(palette )?label|state|street|zip|AIM [Hh]andle(s)?|my card|select(ion| all)?|unsaved|(alpha )?value|entr(y|ies)|group|(ICQ|Jabber|MSN) handle|person|people|company|department|icon image|job title|note|organization|suffix|vcard|url|copies|collating|pages (across|down)|request print time|target( printer)?|((GUI Scripting|Script menu) )?enabled|show Computer scripts|(de)?activated|awake from nib|became (key|main)|call method|of (class|object)|center|clicked toolbar item|closed|for document|exposed|(can )?hide|idle|keyboard (down|up)|event( (number|type))?|launch(ed)?|load (image|movie|nib|sound)|owner|log|mouse (down|dragged|entered|exited|moved|up)|move|column|localization|resource|script|register|drag (info|types)|resigned (active|key|main)|resiz(e(d)?|able)|right mouse (down|dragged|up)|scroll wheel|(at )?index|should (close|open( untitled)?|quit( after last window closed)?|zoom)|((proposed|screen) )?bounds|show(n)?|behind|in front of|size (mode|to fit)|update(d| toolbar item)?|was (hidden|miniaturized)|will (become active|close|finish launching|hide|miniaturize|move|open|quit|(resign )?active|((maximum|minimum|proposed) )?size|show|zoom)|bundle|data source|movie|pasteboard|sound|tool(bar| tip)|(color|open|save) panel|coordinate system|frontmost|main( (bundle|menu|window))?|((services|(excluded from )?windows) )?menu|((executable|frameworks|resource|scripts|shared (frameworks|support)) )?path|(selected item )?identifier|data|content(s| view)?|character(s)?|click count|(command|control|option|shift) key down|context|delta (x|y|z)|key( code)?|location|pressure|unmodified characters|types|(first )?responder|playing|(allowed|selectable) identifiers|allows customization|(auto saves )?configuration|visible|image( name)?|menu form representation|tag|user(-| )defaults|associated file name|(auto|needs) display|current field editor|floating|has (resize indicator|shadow)|hides when deactivated|level|minimized (image|title)|opaque|position|release when closed|sheet|title(d)?)\b/g,
+			    css: 'color3' },
+
+			{ regex: new RegExp(this.getKeywords(specials), 'gm'), css: 'color3' },
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'), css: 'keyword' },
+			{ regex: new RegExp(this.getKeywords(ordinals), 'gm'), css: 'keyword' }
+		];
+	};
+
+	Brush.prototype = new SyntaxHighlighter.Highlighter();
+	Brush.aliases = ['applescript'];
+
+	SyntaxHighlighter.brushes.AppleScript = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushBash.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushBash.js
new file mode 100644
index 0000000000000000000000000000000000000000..8c296969ff440083931c6208855617723db152f1
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushBash.js
@@ -0,0 +1,59 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'if fi then elif else for do done until while break continue case function return in eq ne ge le';
+		var commands =  'alias apropos awk basename bash bc bg builtin bzip2 cal cat cd cfdisk chgrp chmod chown chroot' +
+						'cksum clear cmp comm command cp cron crontab csplit cut date dc dd ddrescue declare df ' +
+						'diff diff3 dig dir dircolors dirname dirs du echo egrep eject enable env ethtool eval ' +
+						'exec exit expand export expr false fdformat fdisk fg fgrep file find fmt fold format ' +
+						'free fsck ftp gawk getopts grep groups gzip hash head history hostname id ifconfig ' +
+						'import install join kill less let ln local locate logname logout look lpc lpr lprint ' +
+						'lprintd lprintq lprm ls lsof make man mkdir mkfifo mkisofs mknod more mount mtools ' +
+						'mv netstat nice nl nohup nslookup open op passwd paste pathchk ping popd pr printcap ' +
+						'printenv printf ps pushd pwd quota quotacheck quotactl ram rcp read readonly renice ' +
+						'remsync rm rmdir rsync screen scp sdiff sed select seq set sftp shift shopt shutdown ' +
+						'sleep sort source split ssh strace su sudo sum symlink sync tail tar tee test time ' +
+						'times touch top traceroute trap tr true tsort tty type ulimit umask umount unalias ' +
+						'uname unexpand uniq units unset unshar useradd usermod users uuencode uudecode v vdir ' +
+						'vi watch wc whereis which who whoami Wget xargs yes'
+						;
+
+		this.regexList = [
+			{ regex: /^#!.*$/gm,											css: 'preprocessor bold' },
+			{ regex: /\/[\w-\/]+/gm,										css: 'plain' },
+			{ regex: SyntaxHighlighter.regexLib.singleLinePerlComments,		css: 'comments' },		// one line comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,			css: 'string' },		// double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,			css: 'string' },		// single quoted strings
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),			css: 'keyword' },		// keywords
+			{ regex: new RegExp(this.getKeywords(commands), 'gm'),			css: 'functions' }		// commands
+			];
+	}
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['bash', 'shell'];
+
+	SyntaxHighlighter.brushes.Bash = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushCSharp.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushCSharp.js
new file mode 100644
index 0000000000000000000000000000000000000000..079214efe11da48fd686db1dab4ae700de86dfa5
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushCSharp.js
@@ -0,0 +1,65 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'abstract as base bool break byte case catch char checked class const ' +
+						'continue decimal default delegate do double else enum event explicit ' +
+						'extern false finally fixed float for foreach get goto if implicit in int ' +
+						'interface internal is lock long namespace new null object operator out ' +
+						'override params private protected public readonly ref return sbyte sealed set ' +
+						'short sizeof stackalloc static string struct switch this throw true try ' +
+						'typeof uint ulong unchecked unsafe ushort using virtual void while';
+
+		function fixComments(match, regexInfo)
+		{
+			var css = (match[0].indexOf("///") == 0)
+				? 'color1'
+				: 'comments'
+				;
+			
+			return [new SyntaxHighlighter.Match(match[0], match.index, css)];
+		}
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	func : fixComments },		// one line comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'comments' },			// multiline comments
+			{ regex: /@"(?:[^"]|"")*"/g,								css: 'string' },			// @-quoted strings
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },			// strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },			// strings
+			{ regex: /^\s*#.*/gm,										css: 'preprocessor' },		// preprocessor tags like #region and #endregion
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword' },			// c# keyword
+			{ regex: /\bpartial(?=\s+(?:class|interface|struct)\b)/g,	css: 'keyword' },			// contextual keyword: 'partial'
+			{ regex: /\byield(?=\s+(?:return|break)\b)/g,				css: 'keyword' }			// contextual keyword: 'yield'
+			];
+		
+		this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['c#', 'c-sharp', 'csharp'];
+
+	SyntaxHighlighter.brushes.CSharp = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
+
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushColdFusion.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushColdFusion.js
new file mode 100644
index 0000000000000000000000000000000000000000..627dbb9b76e181ea7ae7253099bf41b7142cd45d
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushColdFusion.js
@@ -0,0 +1,100 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Jen
+		// http://www.jensbits.com/2009/05/14/coldfusion-brush-for-syntaxhighlighter-plus
+	
+		var funcs	=	'Abs ACos AddSOAPRequestHeader AddSOAPResponseHeader AjaxLink AjaxOnLoad ArrayAppend ArrayAvg ArrayClear ArrayDeleteAt ' + 
+						'ArrayInsertAt ArrayIsDefined ArrayIsEmpty ArrayLen ArrayMax ArrayMin ArraySet ArraySort ArraySum ArraySwap ArrayToList ' + 
+						'Asc ASin Atn BinaryDecode BinaryEncode BitAnd BitMaskClear BitMaskRead BitMaskSet BitNot BitOr BitSHLN BitSHRN BitXor ' + 
+						'Ceiling CharsetDecode CharsetEncode Chr CJustify Compare CompareNoCase Cos CreateDate CreateDateTime CreateObject ' + 
+						'CreateODBCDate CreateODBCDateTime CreateODBCTime CreateTime CreateTimeSpan CreateUUID DateAdd DateCompare DateConvert ' + 
+						'DateDiff DateFormat DatePart Day DayOfWeek DayOfWeekAsString DayOfYear DaysInMonth DaysInYear DE DecimalFormat DecrementValue ' + 
+						'Decrypt DecryptBinary DeleteClientVariable DeserializeJSON DirectoryExists DollarFormat DotNetToCFType Duplicate Encrypt ' + 
+						'EncryptBinary Evaluate Exp ExpandPath FileClose FileCopy FileDelete FileExists FileIsEOF FileMove FileOpen FileRead ' + 
+						'FileReadBinary FileReadLine FileSetAccessMode FileSetAttribute FileSetLastModified FileWrite Find FindNoCase FindOneOf ' + 
+						'FirstDayOfMonth Fix FormatBaseN GenerateSecretKey GetAuthUser GetBaseTagData GetBaseTagList GetBaseTemplatePath ' + 
+						'GetClientVariablesList GetComponentMetaData GetContextRoot GetCurrentTemplatePath GetDirectoryFromPath GetEncoding ' + 
+						'GetException GetFileFromPath GetFileInfo GetFunctionList GetGatewayHelper GetHttpRequestData GetHttpTimeString ' + 
+						'GetK2ServerDocCount GetK2ServerDocCountLimit GetLocale GetLocaleDisplayName GetLocalHostIP GetMetaData GetMetricData ' + 
+						'GetPageContext GetPrinterInfo GetProfileSections GetProfileString GetReadableImageFormats GetSOAPRequest GetSOAPRequestHeader ' + 
+						'GetSOAPResponse GetSOAPResponseHeader GetTempDirectory GetTempFile GetTemplatePath GetTickCount GetTimeZoneInfo GetToken ' + 
+						'GetUserRoles GetWriteableImageFormats Hash Hour HTMLCodeFormat HTMLEditFormat IIf ImageAddBorder ImageBlur ImageClearRect ' + 
+						'ImageCopy ImageCrop ImageDrawArc ImageDrawBeveledRect ImageDrawCubicCurve ImageDrawLine ImageDrawLines ImageDrawOval ' + 
+						'ImageDrawPoint ImageDrawQuadraticCurve ImageDrawRect ImageDrawRoundRect ImageDrawText ImageFlip ImageGetBlob ImageGetBufferedImage ' + 
+						'ImageGetEXIFTag ImageGetHeight ImageGetIPTCTag ImageGetWidth ImageGrayscale ImageInfo ImageNegative ImageNew ImageOverlay ImagePaste ' + 
+						'ImageRead ImageReadBase64 ImageResize ImageRotate ImageRotateDrawingAxis ImageScaleToFit ImageSetAntialiasing ImageSetBackgroundColor ' + 
+						'ImageSetDrawingColor ImageSetDrawingStroke ImageSetDrawingTransparency ImageSharpen ImageShear ImageShearDrawingAxis ImageTranslate ' + 
+						'ImageTranslateDrawingAxis ImageWrite ImageWriteBase64 ImageXORDrawingMode IncrementValue InputBaseN Insert Int IsArray IsBinary ' + 
+						'IsBoolean IsCustomFunction IsDate IsDDX IsDebugMode IsDefined IsImage IsImageFile IsInstanceOf IsJSON IsLeapYear IsLocalHost ' + 
+						'IsNumeric IsNumericDate IsObject IsPDFFile IsPDFObject IsQuery IsSimpleValue IsSOAPRequest IsStruct IsUserInAnyRole IsUserInRole ' + 
+						'IsUserLoggedIn IsValid IsWDDX IsXML IsXmlAttribute IsXmlDoc IsXmlElem IsXmlNode IsXmlRoot JavaCast JSStringFormat LCase Left Len ' + 
+						'ListAppend ListChangeDelims ListContains ListContainsNoCase ListDeleteAt ListFind ListFindNoCase ListFirst ListGetAt ListInsertAt ' + 
+						'ListLast ListLen ListPrepend ListQualify ListRest ListSetAt ListSort ListToArray ListValueCount ListValueCountNoCase LJustify Log ' + 
+						'Log10 LSCurrencyFormat LSDateFormat LSEuroCurrencyFormat LSIsCurrency LSIsDate LSIsNumeric LSNumberFormat LSParseCurrency LSParseDateTime ' + 
+						'LSParseEuroCurrency LSParseNumber LSTimeFormat LTrim Max Mid Min Minute Month MonthAsString Now NumberFormat ParagraphFormat ParseDateTime ' + 
+						'Pi PrecisionEvaluate PreserveSingleQuotes Quarter QueryAddColumn QueryAddRow QueryConvertForGrid QueryNew QuerySetCell QuotedValueList Rand ' + 
+						'Randomize RandRange REFind REFindNoCase ReleaseComObject REMatch REMatchNoCase RemoveChars RepeatString Replace ReplaceList ReplaceNoCase ' + 
+						'REReplace REReplaceNoCase Reverse Right RJustify Round RTrim Second SendGatewayMessage SerializeJSON SetEncoding SetLocale SetProfileString ' + 
+						'SetVariable Sgn Sin Sleep SpanExcluding SpanIncluding Sqr StripCR StructAppend StructClear StructCopy StructCount StructDelete StructFind ' + 
+						'StructFindKey StructFindValue StructGet StructInsert StructIsEmpty StructKeyArray StructKeyExists StructKeyList StructKeyList StructNew ' + 
+						'StructSort StructUpdate Tan TimeFormat ToBase64 ToBinary ToScript ToString Trim UCase URLDecode URLEncodedFormat URLSessionFormat Val ' + 
+						'ValueList VerifyClient Week Wrap Wrap WriteOutput XmlChildPos XmlElemNew XmlFormat XmlGetNodeType XmlNew XmlParse XmlSearch XmlTransform ' + 
+						'XmlValidate Year YesNoFormat';
+
+		var keywords =	'cfabort cfajaximport cfajaxproxy cfapplet cfapplication cfargument cfassociate cfbreak cfcache cfcalendar ' + 
+						'cfcase cfcatch cfchart cfchartdata cfchartseries cfcol cfcollection cfcomponent cfcontent cfcookie cfdbinfo ' + 
+						'cfdefaultcase cfdirectory cfdiv cfdocument cfdocumentitem cfdocumentsection cfdump cfelse cfelseif cferror ' + 
+						'cfexchangecalendar cfexchangeconnection cfexchangecontact cfexchangefilter cfexchangemail cfexchangetask ' + 
+						'cfexecute cfexit cffeed cffile cfflush cfform cfformgroup cfformitem cfftp cffunction cfgrid cfgridcolumn ' + 
+						'cfgridrow cfgridupdate cfheader cfhtmlhead cfhttp cfhttpparam cfif cfimage cfimport cfinclude cfindex ' + 
+						'cfinput cfinsert cfinterface cfinvoke cfinvokeargument cflayout cflayoutarea cfldap cflocation cflock cflog ' + 
+						'cflogin cfloginuser cflogout cfloop cfmail cfmailparam cfmailpart cfmenu cfmenuitem cfmodule cfNTauthenticate ' + 
+						'cfobject cfobjectcache cfoutput cfparam cfpdf cfpdfform cfpdfformparam cfpdfparam cfpdfsubform cfpod cfpop ' + 
+						'cfpresentation cfpresentationslide cfpresenter cfprint cfprocessingdirective cfprocparam cfprocresult ' + 
+						'cfproperty cfquery cfqueryparam cfregistry cfreport cfreportparam cfrethrow cfreturn cfsavecontent cfschedule ' + 
+						'cfscript cfsearch cfselect cfset cfsetting cfsilent cfslider cfsprydataset cfstoredproc cfswitch cftable ' + 
+						'cftextarea cfthread cfthrow cftimer cftooltip cftrace cftransaction cftree cftreeitem cftry cfupdate cfwddx ' + 
+						'cfwindow cfxml cfzip cfzipparam';
+
+		var operators =	'all and any between cross in join like not null or outer some';
+
+		this.regexList = [
+			{ regex: new RegExp('--(.*)$', 'gm'),						css: 'comments' },  // one line and multiline comments
+			{ regex: SyntaxHighlighter.regexLib.xmlComments,			css: 'comments' },    // single quoted strings
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },    // double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },    // single quoted strings
+			{ regex: new RegExp(this.getKeywords(funcs), 'gmi'),		css: 'functions' }, // functions
+			{ regex: new RegExp(this.getKeywords(operators), 'gmi'),	css: 'color1' },    // operators and such
+			{ regex: new RegExp(this.getKeywords(keywords), 'gmi'),		css: 'keyword' }    // keyword
+			];
+	}
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['coldfusion','cf'];
+	
+	SyntaxHighlighter.brushes.ColdFusion = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushCpp.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushCpp.js
new file mode 100644
index 0000000000000000000000000000000000000000..9f70d3aed60d815bb2700f4e29565a4c8be51009
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushCpp.js
@@ -0,0 +1,97 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Copyright 2006 Shin, YoungJin
+	
+		var datatypes =	'ATOM BOOL BOOLEAN BYTE CHAR COLORREF DWORD DWORDLONG DWORD_PTR ' +
+						'DWORD32 DWORD64 FLOAT HACCEL HALF_PTR HANDLE HBITMAP HBRUSH ' +
+						'HCOLORSPACE HCONV HCONVLIST HCURSOR HDC HDDEDATA HDESK HDROP HDWP ' +
+						'HENHMETAFILE HFILE HFONT HGDIOBJ HGLOBAL HHOOK HICON HINSTANCE HKEY ' +
+						'HKL HLOCAL HMENU HMETAFILE HMODULE HMONITOR HPALETTE HPEN HRESULT ' +
+						'HRGN HRSRC HSZ HWINSTA HWND INT INT_PTR INT32 INT64 LANGID LCID LCTYPE ' +
+						'LGRPID LONG LONGLONG LONG_PTR LONG32 LONG64 LPARAM LPBOOL LPBYTE LPCOLORREF ' +
+						'LPCSTR LPCTSTR LPCVOID LPCWSTR LPDWORD LPHANDLE LPINT LPLONG LPSTR LPTSTR ' +
+						'LPVOID LPWORD LPWSTR LRESULT PBOOL PBOOLEAN PBYTE PCHAR PCSTR PCTSTR PCWSTR ' +
+						'PDWORDLONG PDWORD_PTR PDWORD32 PDWORD64 PFLOAT PHALF_PTR PHANDLE PHKEY PINT ' +
+						'PINT_PTR PINT32 PINT64 PLCID PLONG PLONGLONG PLONG_PTR PLONG32 PLONG64 POINTER_32 ' +
+						'POINTER_64 PSHORT PSIZE_T PSSIZE_T PSTR PTBYTE PTCHAR PTSTR PUCHAR PUHALF_PTR ' +
+						'PUINT PUINT_PTR PUINT32 PUINT64 PULONG PULONGLONG PULONG_PTR PULONG32 PULONG64 ' +
+						'PUSHORT PVOID PWCHAR PWORD PWSTR SC_HANDLE SC_LOCK SERVICE_STATUS_HANDLE SHORT ' +
+						'SIZE_T SSIZE_T TBYTE TCHAR UCHAR UHALF_PTR UINT UINT_PTR UINT32 UINT64 ULONG ' +
+						'ULONGLONG ULONG_PTR ULONG32 ULONG64 USHORT USN VOID WCHAR WORD WPARAM WPARAM WPARAM ' +
+						'char bool short int __int32 __int64 __int8 __int16 long float double __wchar_t ' +
+						'clock_t _complex _dev_t _diskfree_t div_t ldiv_t _exception _EXCEPTION_POINTERS ' +
+						'FILE _finddata_t _finddatai64_t _wfinddata_t _wfinddatai64_t __finddata64_t ' +
+						'__wfinddata64_t _FPIEEE_RECORD fpos_t _HEAPINFO _HFILE lconv intptr_t ' +
+						'jmp_buf mbstate_t _off_t _onexit_t _PNH ptrdiff_t _purecall_handler ' +
+						'sig_atomic_t size_t _stat __stat64 _stati64 terminate_function ' +
+						'time_t __time64_t _timeb __timeb64 tm uintptr_t _utimbuf ' +
+						'va_list wchar_t wctrans_t wctype_t wint_t signed';
+
+		var keywords =	'break case catch class const __finally __exception __try ' +
+						'const_cast continue private public protected __declspec ' +
+						'default delete deprecated dllexport dllimport do dynamic_cast ' +
+						'else enum explicit extern if for friend goto inline ' +
+						'mutable naked namespace new noinline noreturn nothrow ' +
+						'register reinterpret_cast return selectany ' +
+						'sizeof static static_cast struct switch template this ' +
+						'thread throw true false try typedef typeid typename union ' +
+						'using uuid virtual void volatile whcar_t while';
+					
+		var functions =	'assert isalnum isalpha iscntrl isdigit isgraph islower isprint' +
+						'ispunct isspace isupper isxdigit tolower toupper errno localeconv ' +
+						'setlocale acos asin atan atan2 ceil cos cosh exp fabs floor fmod ' +
+						'frexp ldexp log log10 modf pow sin sinh sqrt tan tanh jmp_buf ' +
+						'longjmp setjmp raise signal sig_atomic_t va_arg va_end va_start ' +
+						'clearerr fclose feof ferror fflush fgetc fgetpos fgets fopen ' +
+						'fprintf fputc fputs fread freopen fscanf fseek fsetpos ftell ' +
+						'fwrite getc getchar gets perror printf putc putchar puts remove ' +
+						'rename rewind scanf setbuf setvbuf sprintf sscanf tmpfile tmpnam ' +
+						'ungetc vfprintf vprintf vsprintf abort abs atexit atof atoi atol ' +
+						'bsearch calloc div exit free getenv labs ldiv malloc mblen mbstowcs ' +
+						'mbtowc qsort rand realloc srand strtod strtol strtoul system ' +
+						'wcstombs wctomb memchr memcmp memcpy memmove memset strcat strchr ' +
+						'strcmp strcoll strcpy strcspn strerror strlen strncat strncmp ' +
+						'strncpy strpbrk strrchr strspn strstr strtok strxfrm asctime ' +
+						'clock ctime difftime gmtime localtime mktime strftime time';
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'comments' },			// one line comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'comments' },			// multiline comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },			// strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },			// strings
+			{ regex: /^ *#.*/gm,										css: 'preprocessor' },
+			{ regex: new RegExp(this.getKeywords(datatypes), 'gm'),		css: 'color1 bold' },
+			{ regex: new RegExp(this.getKeywords(functions), 'gm'),		css: 'functions bold' },
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword bold' }
+			];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['cpp', 'c'];
+
+	SyntaxHighlighter.brushes.Cpp = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushCss.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushCss.js
new file mode 100644
index 0000000000000000000000000000000000000000..4297a9a6486b6c93e335a2d2805c2bf91e04dbf8
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushCss.js
@@ -0,0 +1,91 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		function getKeywordsCSS(str)
+		{
+			return '\\b([a-z_]|)' + str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') + '(?=:)\\b';
+		};
+	
+		function getValuesCSS(str)
+		{
+			return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + '\:\\b';
+		};
+
+		var keywords =	'ascent azimuth background-attachment background-color background-image background-position ' +
+						'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
+						'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
+						'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
+						'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
+						'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
+						'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
+						'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
+						'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
+						'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
+						'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
+						'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
+						'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
+						'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
+
+		var values =	'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+
+						'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+
+						'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero default digits disc dotted double '+
+						'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+
+						'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+
+						'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+
+						'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+
+						'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+
+						'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+
+						'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+
+						'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize '+
+						'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal '+
+						'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin '+
+						'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
+
+		var fonts =		'[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
+	
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'comments' },	// multiline comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },	// double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },	// single quoted strings
+			{ regex: /\#[a-fA-F0-9]{3,6}/g,								css: 'value' },		// html colors
+			{ regex: /(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)/g,				css: 'value' },		// sizes
+			{ regex: /!important/g,										css: 'color3' },	// !important
+			{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'),		css: 'keyword' },	// keywords
+			{ regex: new RegExp(getValuesCSS(values), 'g'),				css: 'value' },		// values
+			{ regex: new RegExp(this.getKeywords(fonts), 'g'),			css: 'color1' }		// fonts
+			];
+
+		this.forHtmlScript({ 
+			left: /(&lt;|<)\s*style.*?(&gt;|>)/gi, 
+			right: /(&lt;|<)\/\s*style\s*(&gt;|>)/gi 
+			});
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['css'];
+
+	SyntaxHighlighter.brushes.CSS = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushDelphi.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushDelphi.js
new file mode 100644
index 0000000000000000000000000000000000000000..e1060d44688c3b7db155c61f39e53f152e9b4007
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushDelphi.js
@@ -0,0 +1,55 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'abs addr and ansichar ansistring array as asm begin boolean byte cardinal ' +
+						'case char class comp const constructor currency destructor div do double ' +
+						'downto else end except exports extended false file finalization finally ' +
+						'for function goto if implementation in inherited int64 initialization ' +
+						'integer interface is label library longint longword mod nil not object ' +
+						'of on or packed pansichar pansistring pchar pcurrency pdatetime pextended ' +
+						'pint64 pointer private procedure program property pshortstring pstring ' +
+						'pvariant pwidechar pwidestring protected public published raise real real48 ' +
+						'record repeat set shl shortint shortstring shr single smallint string then ' +
+						'threadvar to true try type unit until uses val var varirnt while widechar ' +
+						'widestring with word write writeln xor';
+
+		this.regexList = [
+			{ regex: /\(\*[\s\S]*?\*\)/gm,								css: 'comments' },  	// multiline comments (* *)
+			{ regex: /{(?!\$)[\s\S]*?}/gm,								css: 'comments' },  	// multiline comments { }
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'comments' },  	// one line
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },		// strings
+			{ regex: /\{\$[a-zA-Z]+ .+\}/g,								css: 'color1' },		// compiler Directives and Region tags
+			{ regex: /\b[\d\.]+\b/g,									css: 'value' },			// numbers 12345
+			{ regex: /\$[a-zA-Z0-9]+\b/g,								css: 'value' },			// numbers $F5D3
+			{ regex: new RegExp(this.getKeywords(keywords), 'gmi'),		css: 'keyword' }		// keyword
+			];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['delphi', 'pascal', 'pas'];
+
+	SyntaxHighlighter.brushes.Delphi = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushDiff.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushDiff.js
new file mode 100644
index 0000000000000000000000000000000000000000..e9b14fc580a4826ad762b742e9102f4bfb8a152a
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushDiff.js
@@ -0,0 +1,41 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		this.regexList = [
+			{ regex: /^\+\+\+.*$/gm,		css: 'color2' },
+			{ regex: /^\-\-\-.*$/gm,		css: 'color2' },
+			{ regex: /^\s.*$/gm,			css: 'color1' },
+			{ regex: /^@@.*@@$/gm,			css: 'variable' },
+			{ regex: /^\+[^\+]{1}.*$/gm,	css: 'string' },
+			{ regex: /^\-[^\-]{1}.*$/gm,	css: 'comments' }
+			];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['diff', 'patch'];
+
+	SyntaxHighlighter.brushes.Diff = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushErlang.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushErlang.js
new file mode 100644
index 0000000000000000000000000000000000000000..6ba7d9da871a2fb656bfb736715a2433289f3400
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushErlang.js
@@ -0,0 +1,52 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Jean-Lou Dupont
+		// http://jldupont.blogspot.com/2009/06/erlang-syntax-highlighter.html  
+
+		// According to: http://erlang.org/doc/reference_manual/introduction.html#1.5
+		var keywords = 'after and andalso band begin bnot bor bsl bsr bxor '+
+			'case catch cond div end fun if let not of or orelse '+
+			'query receive rem try when xor'+
+			// additional
+			' module export import define';
+
+		this.regexList = [
+			{ regex: new RegExp("[A-Z][A-Za-z0-9_]+", 'g'), 			css: 'constants' },
+			{ regex: new RegExp("\\%.+", 'gm'), 						css: 'comments' },
+			{ regex: new RegExp("\\?[A-Za-z0-9_]+", 'g'), 				css: 'preprocessor' },
+			{ regex: new RegExp("[a-z0-9_]+:[a-z0-9_]+", 'g'), 			css: 'functions' },
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },
+			{ regex: new RegExp(this.getKeywords(keywords),	'gm'),		css: 'keyword' }
+			];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['erl', 'erlang'];
+
+	SyntaxHighlighter.brushes.Erland = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushGroovy.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushGroovy.js
new file mode 100644
index 0000000000000000000000000000000000000000..6ec5c18521a1830d653a01080ccba937d1541533
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushGroovy.js
@@ -0,0 +1,67 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Andres Almiray
+		// http://jroller.com/aalmiray/entry/nice_source_code_syntax_highlighter
+
+		var keywords =	'as assert break case catch class continue def default do else extends finally ' +
+						'if in implements import instanceof interface new package property return switch ' +
+						'throw throws try while public protected private static';
+		var types    =  'void boolean byte char short int long float double';
+		var constants = 'null';
+		var methods   = 'allProperties count get size '+
+						'collect each eachProperty eachPropertyName eachWithIndex find findAll ' +
+						'findIndexOf grep inject max min reverseEach sort ' +
+						'asImmutable asSynchronized flatten intersect join pop reverse subMap toList ' +
+						'padRight padLeft contains eachMatch toCharacter toLong toUrl tokenize ' +
+						'eachFile eachFileRecurse eachB yte eachLine readBytes readLine getText ' +
+						'splitEachLine withReader append encodeBase64 decodeBase64 filterLine ' +
+						'transformChar transformLine withOutputStream withPrintWriter withStream ' +
+						'withStreams withWriter withWriterAppend write writeLine '+
+						'dump inspect invokeMethod print println step times upto use waitForOrKill '+
+						'getText';
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,				css: 'comments' },		// one line comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,					css: 'comments' },		// multiline comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,					css: 'string' },		// strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,					css: 'string' },		// strings
+			{ regex: /""".*"""/g,													css: 'string' },		// GStrings
+			{ regex: new RegExp('\\b([\\d]+(\\.[\\d]+)?|0x[a-f0-9]+)\\b', 'gi'),	css: 'value' },			// numbers
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),					css: 'keyword' },		// goovy keyword
+			{ regex: new RegExp(this.getKeywords(types), 'gm'),						css: 'color1' },		// goovy/java type
+			{ regex: new RegExp(this.getKeywords(constants), 'gm'),					css: 'constants' },		// constants
+			{ regex: new RegExp(this.getKeywords(methods), 'gm'),					css: 'functions' }		// methods
+			];
+
+		this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+	}
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['groovy'];
+
+	SyntaxHighlighter.brushes.Groovy = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushJScript.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushJScript.js
new file mode 100644
index 0000000000000000000000000000000000000000..ff98daba16e4974421df1396ba26053091c0f58b
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushJScript.js
@@ -0,0 +1,52 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'break case catch continue ' +
+						'default delete do else false  ' +
+						'for function if in instanceof ' +
+						'new null return super switch ' +
+						'this throw true try typeof var while with'
+						;
+
+		var r = SyntaxHighlighter.regexLib;
+		
+		this.regexList = [
+			{ regex: r.multiLineDoubleQuotedString,					css: 'string' },			// double quoted strings
+			{ regex: r.multiLineSingleQuotedString,					css: 'string' },			// single quoted strings
+			{ regex: r.singleLineCComments,							css: 'comments' },			// one line comments
+			{ regex: r.multiLineCComments,							css: 'comments' },			// multiline comments
+			{ regex: /\s*#.*/gm,									css: 'preprocessor' },		// preprocessor tags like #region and #endregion
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),	css: 'keyword' }			// keywords
+			];
+	
+		this.forHtmlScript(r.scriptScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['js', 'jscript', 'javascript'];
+
+	SyntaxHighlighter.brushes.JScript = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushJava.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushJava.js
new file mode 100644
index 0000000000000000000000000000000000000000..d692fd63828bce4de6b961299f2dd23cc271c4b0
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushJava.js
@@ -0,0 +1,57 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'abstract assert boolean break byte case catch char class const ' +
+						'continue default do double else enum extends ' +
+						'false final finally float for goto if implements import ' +
+						'instanceof int interface long native new null ' +
+						'package private protected public return ' +
+						'short static strictfp super switch synchronized this throw throws true ' +
+						'transient try void volatile while';
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'comments' },		// one line comments
+			{ regex: /\/\*([^\*][\s\S]*)?\*\//gm,						css: 'comments' },	 	// multiline comments
+			{ regex: /\/\*(?!\*\/)\*[\s\S]*?\*\//gm,					css: 'preprocessor' },	// documentation comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },		// strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },		// strings
+			{ regex: /\b([\d]+(\.[\d]+)?|0x[a-f0-9]+)\b/gi,				css: 'value' },			// numbers
+			{ regex: /(?!\@interface\b)\@[\$\w]+\b/g,					css: 'color1' },		// annotation @anno
+			{ regex: /\@interface\b/g,									css: 'color2' },		// @interface keyword
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword' }		// java keyword
+			];
+
+		this.forHtmlScript({
+			left	: /(&lt;|<)%[@!=]?/g, 
+			right	: /%(&gt;|>)/g 
+		});
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['java'];
+
+	SyntaxHighlighter.brushes.Java = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushJavaFX.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushJavaFX.js
new file mode 100644
index 0000000000000000000000000000000000000000..1a150a6ad33469887aa4b747723580b0360afad6
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushJavaFX.js
@@ -0,0 +1,58 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Patrick Webster
+		// http://patrickwebster.blogspot.com/2009/04/javafx-brush-for-syntaxhighlighter.html
+		var datatypes =	'Boolean Byte Character Double Duration '
+						+ 'Float Integer Long Number Short String Void'
+						;
+
+		var keywords = 'abstract after and as assert at before bind bound break catch class '
+						+ 'continue def delete else exclusive extends false finally first for from '
+						+ 'function if import in indexof init insert instanceof into inverse last '
+						+ 'lazy mixin mod nativearray new not null on or override package postinit '
+						+ 'protected public public-init public-read replace return reverse sizeof '
+						+ 'step super then this throw true try tween typeof var where while with '
+						+ 'attribute let private readonly static trigger'
+						;
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'comments' },
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'comments' },
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },
+			{ regex: /(-?\.?)(\b(\d*\.?\d+|\d+\.?\d*)(e[+-]?\d+)?|0x[a-f\d]+)\b\.?/gi, css: 'color2' },	// numbers
+			{ regex: new RegExp(this.getKeywords(datatypes), 'gm'),		css: 'variable' },	// datatypes
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword' }
+		];
+		this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['jfx', 'javafx'];
+
+	SyntaxHighlighter.brushes.JavaFX = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushPerl.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushPerl.js
new file mode 100644
index 0000000000000000000000000000000000000000..d94a2e0ec52b65a8ac81409dd7098f2da8e867dd
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushPerl.js
@@ -0,0 +1,72 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by David Simmons-Duffin and Marty Kube
+	
+		var funcs = 
+			'abs accept alarm atan2 bind binmode chdir chmod chomp chop chown chr ' + 
+			'chroot close closedir connect cos crypt defined delete each endgrent ' + 
+			'endhostent endnetent endprotoent endpwent endservent eof exec exists ' + 
+			'exp fcntl fileno flock fork format formline getc getgrent getgrgid ' + 
+			'getgrnam gethostbyaddr gethostbyname gethostent getlogin getnetbyaddr ' + 
+			'getnetbyname getnetent getpeername getpgrp getppid getpriority ' + 
+			'getprotobyname getprotobynumber getprotoent getpwent getpwnam getpwuid ' + 
+			'getservbyname getservbyport getservent getsockname getsockopt glob ' + 
+			'gmtime grep hex index int ioctl join keys kill lc lcfirst length link ' + 
+			'listen localtime lock log lstat map mkdir msgctl msgget msgrcv msgsnd ' + 
+			'oct open opendir ord pack pipe pop pos print printf prototype push ' + 
+			'quotemeta rand read readdir readline readlink readpipe recv rename ' + 
+			'reset reverse rewinddir rindex rmdir scalar seek seekdir select semctl ' + 
+			'semget semop send setgrent sethostent setnetent setpgrp setpriority ' + 
+			'setprotoent setpwent setservent setsockopt shift shmctl shmget shmread ' + 
+			'shmwrite shutdown sin sleep socket socketpair sort splice split sprintf ' + 
+			'sqrt srand stat study substr symlink syscall sysopen sysread sysseek ' + 
+			'system syswrite tell telldir time times tr truncate uc ucfirst umask ' + 
+			'undef unlink unpack unshift utime values vec wait waitpid warn write';
+    
+		var keywords =  
+			'bless caller continue dbmclose dbmopen die do dump else elsif eval exit ' +
+			'for foreach goto if import last local my next no our package redo ref ' + 
+			'require return sub tie tied unless untie until use wantarray while';
+    
+		this.regexList = [
+			{ regex: new RegExp('#[^!].*$', 'gm'),					css: 'comments' },
+			{ regex: new RegExp('^\\s*#!.*$', 'gm'),				css: 'preprocessor' }, // shebang
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,	css: 'string' },
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,	css: 'string' },
+			{ regex: new RegExp('(\\$|@|%)\\w+', 'g'),				css: 'variable' },
+			{ regex: new RegExp(this.getKeywords(funcs), 'gmi'),	css: 'functions' },
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),	css: 'keyword' }
+		    ];
+
+		this.forHtmlScript(SyntaxHighlighter.regexLib.phpScriptTags);
+	}
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases		= ['perl', 'Perl', 'pl'];
+
+	SyntaxHighlighter.brushes.Perl = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushPhp.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushPhp.js
new file mode 100644
index 0000000000000000000000000000000000000000..95e6e4325bb44aa6caf4c40d914810f25616db88
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushPhp.js
@@ -0,0 +1,88 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var funcs	=	'abs acos acosh addcslashes addslashes ' +
+						'array_change_key_case array_chunk array_combine array_count_values array_diff '+
+						'array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_fill '+
+						'array_filter array_flip array_intersect array_intersect_assoc array_intersect_key '+
+						'array_intersect_uassoc array_intersect_ukey array_key_exists array_keys array_map '+
+						'array_merge array_merge_recursive array_multisort array_pad array_pop array_product '+
+						'array_push array_rand array_reduce array_reverse array_search array_shift '+
+						'array_slice array_splice array_sum array_udiff array_udiff_assoc '+
+						'array_udiff_uassoc array_uintersect array_uintersect_assoc '+
+						'array_uintersect_uassoc array_unique array_unshift array_values array_walk '+
+						'array_walk_recursive atan atan2 atanh base64_decode base64_encode base_convert '+
+						'basename bcadd bccomp bcdiv bcmod bcmul bindec bindtextdomain bzclose bzcompress '+
+						'bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite ceil chdir '+
+						'checkdate checkdnsrr chgrp chmod chop chown chr chroot chunk_split class_exists '+
+						'closedir closelog copy cos cosh count count_chars date decbin dechex decoct '+
+						'deg2rad delete ebcdic2ascii echo empty end ereg ereg_replace eregi eregi_replace error_log '+
+						'error_reporting escapeshellarg escapeshellcmd eval exec exit exp explode extension_loaded '+
+						'feof fflush fgetc fgetcsv fgets fgetss file_exists file_get_contents file_put_contents '+
+						'fileatime filectime filegroup fileinode filemtime fileowner fileperms filesize filetype '+
+						'floatval flock floor flush fmod fnmatch fopen fpassthru fprintf fputcsv fputs fread fscanf '+
+						'fseek fsockopen fstat ftell ftok getallheaders getcwd getdate getenv gethostbyaddr gethostbyname '+
+						'gethostbynamel getimagesize getlastmod getmxrr getmygid getmyinode getmypid getmyuid getopt '+
+						'getprotobyname getprotobynumber getrandmax getrusage getservbyname getservbyport gettext '+
+						'gettimeofday gettype glob gmdate gmmktime ini_alter ini_get ini_get_all ini_restore ini_set '+
+						'interface_exists intval ip2long is_a is_array is_bool is_callable is_dir is_double '+
+						'is_executable is_file is_finite is_float is_infinite is_int is_integer is_link is_long '+
+						'is_nan is_null is_numeric is_object is_readable is_real is_resource is_scalar is_soap_fault '+
+						'is_string is_subclass_of is_uploaded_file is_writable is_writeable mkdir mktime nl2br '+
+						'parse_ini_file parse_str parse_url passthru pathinfo print readlink realpath rewind rewinddir rmdir '+
+						'round str_ireplace str_pad str_repeat str_replace str_rot13 str_shuffle str_split '+
+						'str_word_count strcasecmp strchr strcmp strcoll strcspn strftime strip_tags stripcslashes '+
+						'stripos stripslashes stristr strlen strnatcasecmp strnatcmp strncasecmp strncmp strpbrk '+
+						'strpos strptime strrchr strrev strripos strrpos strspn strstr strtok strtolower strtotime '+
+						'strtoupper strtr strval substr substr_compare';
+
+		var keywords =	'abstract and array as break case catch cfunction class clone const continue declare default die do ' +
+						'else elseif enddeclare endfor endforeach endif endswitch endwhile extends final for foreach ' +
+						'function include include_once global goto if implements interface instanceof namespace new ' +
+						'old_function or private protected public return require require_once static switch ' +
+						'throw try use var while xor ';
+		
+		var constants	= '__FILE__ __LINE__ __METHOD__ __FUNCTION__ __CLASS__';
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,	css: 'comments' },			// one line comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,		css: 'comments' },			// multiline comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },			// double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },			// single quoted strings
+			{ regex: /\$\w+/g,											css: 'variable' },			// variables
+			{ regex: new RegExp(this.getKeywords(funcs), 'gmi'),		css: 'functions' },			// common functions
+			{ regex: new RegExp(this.getKeywords(constants), 'gmi'),	css: 'constants' },			// constants
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword' }			// keyword
+			];
+
+		this.forHtmlScript(SyntaxHighlighter.regexLib.phpScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['php'];
+
+	SyntaxHighlighter.brushes.Php = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushPlain.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushPlain.js
new file mode 100644
index 0000000000000000000000000000000000000000..9f7d9e90c32f797e89bd00efd24162ec1c1e33a8
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushPlain.js
@@ -0,0 +1,33 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['text', 'plain'];
+
+	SyntaxHighlighter.brushes.Plain = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushPowerShell.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushPowerShell.js
new file mode 100644
index 0000000000000000000000000000000000000000..0be175296894671b466de8b4548243ba8e333c06
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushPowerShell.js
@@ -0,0 +1,74 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributes by B.v.Zanten, Getronics
+		// http://confluence.atlassian.com/display/CONFEXT/New+Code+Macro
+
+		var keywords = 'Add-Content Add-History Add-Member Add-PSSnapin Clear(-Content)? Clear-Item ' +
+					'Clear-ItemProperty Clear-Variable Compare-Object ConvertFrom-SecureString Convert-Path ' +
+					'ConvertTo-Html ConvertTo-SecureString Copy(-Item)? Copy-ItemProperty Export-Alias ' +
+					'Export-Clixml Export-Console Export-Csv ForEach(-Object)? Format-Custom Format-List ' +
+					'Format-Table Format-Wide Get-Acl Get-Alias Get-AuthenticodeSignature Get-ChildItem Get-Command ' +
+					'Get-Content Get-Credential Get-Culture Get-Date Get-EventLog Get-ExecutionPolicy ' +
+					'Get-Help Get-History Get-Host Get-Item Get-ItemProperty Get-Location Get-Member ' +
+					'Get-PfxCertificate Get-Process Get-PSDrive Get-PSProvider Get-PSSnapin Get-Service ' +
+					'Get-TraceSource Get-UICulture Get-Unique Get-Variable Get-WmiObject Group-Object ' +
+					'Import-Alias Import-Clixml Import-Csv Invoke-Expression Invoke-History Invoke-Item ' +
+					'Join-Path Measure-Command Measure-Object Move(-Item)? Move-ItemProperty New-Alias ' +
+					'New-Item New-ItemProperty New-Object New-PSDrive New-Service New-TimeSpan ' +
+					'New-Variable Out-Default Out-File Out-Host Out-Null Out-Printer Out-String Pop-Location ' +
+					'Push-Location Read-Host Remove-Item Remove-ItemProperty Remove-PSDrive Remove-PSSnapin ' +
+					'Remove-Variable Rename-Item Rename-ItemProperty Resolve-Path Restart-Service Resume-Service ' +
+					'Select-Object Select-String Set-Acl Set-Alias Set-AuthenticodeSignature Set-Content ' +
+					'Set-Date Set-ExecutionPolicy Set-Item Set-ItemProperty Set-Location Set-PSDebug ' +
+					'Set-Service Set-TraceSource Set(-Variable)? Sort-Object Split-Path Start-Service ' +
+					'Start-Sleep Start-Transcript Stop-Process Stop-Service Stop-Transcript Suspend-Service ' +
+					'Tee-Object Test-Path Trace-Command Update-FormatData Update-TypeData Where(-Object)? ' +
+					'Write-Debug Write-Error Write(-Host)? Write-Output Write-Progress Write-Verbose Write-Warning';
+		var alias = 'ac asnp clc cli clp clv cpi cpp cvpa diff epal epcsv fc fl ' +
+					'ft fw gal gc gci gcm gdr ghy gi gl gm gp gps group gsv ' +
+					'gsnp gu gv gwmi iex ihy ii ipal ipcsv mi mp nal ndr ni nv oh rdr ' +
+					'ri rni rnp rp rsnp rv rvpa sal sasv sc select si sl sleep sort sp ' +
+					'spps spsv sv tee cat cd cp h history kill lp ls ' +
+					'mount mv popd ps pushd pwd r rm rmdir echo cls chdir del dir ' +
+					'erase rd ren type % \\?';
+
+		this.regexList = [
+			{ regex: /#.*$/gm,										css: 'comments' },  // one line comments
+			{ regex: /\$[a-zA-Z0-9]+\b/g,							css: 'value'   },   // variables $Computer1
+			{ regex: /\-[a-zA-Z]+\b/g,								css: 'keyword' },   // Operators    -not  -and  -eq
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,	css: 'string' },    // strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,	css: 'string' },    // strings
+			{ regex: new RegExp(this.getKeywords(keywords), 'gmi'),	css: 'keyword' },
+			{ regex: new RegExp(this.getKeywords(alias), 'gmi'),	css: 'keyword' }
+		];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['powershell', 'ps'];
+
+	SyntaxHighlighter.brushes.PowerShell = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushPython.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushPython.js
new file mode 100644
index 0000000000000000000000000000000000000000..ce77462975f8c1b9ddc2d4e6d3bfb384971303de
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushPython.js
@@ -0,0 +1,64 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Gheorghe Milas and Ahmad Sherif
+	
+		var keywords =  'and assert break class continue def del elif else ' +
+						'except exec finally for from global if import in is ' +
+						'lambda not or pass print raise return try yield while';
+
+		var funcs = '__import__ abs all any apply basestring bin bool buffer callable ' +
+					'chr classmethod cmp coerce compile complex delattr dict dir ' +
+					'divmod enumerate eval execfile file filter float format frozenset ' +
+					'getattr globals hasattr hash help hex id input int intern ' +
+					'isinstance issubclass iter len list locals long map max min next ' +
+					'object oct open ord pow print property range raw_input reduce ' +
+					'reload repr reversed round set setattr slice sorted staticmethod ' +
+					'str sum super tuple type type unichr unicode vars xrange zip';
+
+		var special =  'None True False self cls class_';
+
+		this.regexList = [
+				{ regex: SyntaxHighlighter.regexLib.singleLinePerlComments, css: 'comments' },
+				{ regex: /^\s*@\w+/gm, 										css: 'decorator' },
+				{ regex: /(['\"]{3})([^\1])*?\1/gm, 						css: 'comments' },
+				{ regex: /"(?!")(?:\.|\\\"|[^\""\n])*"/gm, 					css: 'string' },
+				{ regex: /'(?!')(?:\.|(\\\')|[^\''\n])*'/gm, 				css: 'string' },
+				{ regex: /\+|\-|\*|\/|\%|=|==/gm, 							css: 'keyword' },
+				{ regex: /\b\d+\.?\w*/g, 									css: 'value' },
+				{ regex: new RegExp(this.getKeywords(funcs), 'gmi'),		css: 'functions' },
+				{ regex: new RegExp(this.getKeywords(keywords), 'gm'), 		css: 'keyword' },
+				{ regex: new RegExp(this.getKeywords(special), 'gm'), 		css: 'color1' }
+				];
+			
+		this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['py', 'python'];
+
+	SyntaxHighlighter.brushes.Python = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushRuby.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushRuby.js
new file mode 100644
index 0000000000000000000000000000000000000000..ff82130a7af0dffa8723e5c00622924c13451a53
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushRuby.js
@@ -0,0 +1,55 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Erik Peterson.
+	
+		var keywords =	'alias and BEGIN begin break case class def define_method defined do each else elsif ' +
+						'END end ensure false for if in module new next nil not or raise redo rescue retry return ' +
+						'self super then throw true undef unless until when while yield';
+
+		var builtins =	'Array Bignum Binding Class Continuation Dir Exception FalseClass File::Stat File Fixnum Fload ' +
+						'Hash Integer IO MatchData Method Module NilClass Numeric Object Proc Range Regexp String Struct::TMS Symbol ' +
+						'ThreadGroup Thread Time TrueClass';
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLinePerlComments,	css: 'comments' },		// one line comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,		css: 'string' },		// double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,		css: 'string' },		// single quoted strings
+			{ regex: /\b[A-Z0-9_]+\b/g,									css: 'constants' },		// constants
+			{ regex: /:[a-z][A-Za-z0-9_]*/g,							css: 'color2' },		// symbols
+			{ regex: /(\$|@@|@)\w+/g,									css: 'variable bold' },	// $global, @instance, and @@class variables
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),		css: 'keyword' },		// keywords
+			{ regex: new RegExp(this.getKeywords(builtins), 'gm'),		css: 'color1' }			// builtins
+			];
+
+		this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['ruby', 'rails', 'ror', 'rb'];
+
+	SyntaxHighlighter.brushes.Ruby = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushSass.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushSass.js
new file mode 100644
index 0000000000000000000000000000000000000000..aa04da0996b13343d3faa59aaf968288915c7cc3
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushSass.js
@@ -0,0 +1,94 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		function getKeywordsCSS(str)
+		{
+			return '\\b([a-z_]|)' + str.replace(/ /g, '(?=:)\\b|\\b([a-z_\\*]|\\*|)') + '(?=:)\\b';
+		};
+	
+		function getValuesCSS(str)
+		{
+			return '\\b' + str.replace(/ /g, '(?!-)(?!:)\\b|\\b()') + '\:\\b';
+		};
+
+		var keywords =	'ascent azimuth background-attachment background-color background-image background-position ' +
+						'background-repeat background baseline bbox border-collapse border-color border-spacing border-style border-top ' +
+						'border-right border-bottom border-left border-top-color border-right-color border-bottom-color border-left-color ' +
+						'border-top-style border-right-style border-bottom-style border-left-style border-top-width border-right-width ' +
+						'border-bottom-width border-left-width border-width border bottom cap-height caption-side centerline clear clip color ' +
+						'content counter-increment counter-reset cue-after cue-before cue cursor definition-src descent direction display ' +
+						'elevation empty-cells float font-size-adjust font-family font-size font-stretch font-style font-variant font-weight font ' +
+						'height left letter-spacing line-height list-style-image list-style-position list-style-type list-style margin-top ' +
+						'margin-right margin-bottom margin-left margin marker-offset marks mathline max-height max-width min-height min-width orphans ' +
+						'outline-color outline-style outline-width outline overflow padding-top padding-right padding-bottom padding-left padding page ' +
+						'page-break-after page-break-before page-break-inside pause pause-after pause-before pitch pitch-range play-during position ' +
+						'quotes right richness size slope src speak-header speak-numeral speak-punctuation speak speech-rate stemh stemv stress ' +
+						'table-layout text-align top text-decoration text-indent text-shadow text-transform unicode-bidi unicode-range units-per-em ' +
+						'vertical-align visibility voice-family volume white-space widows width widths word-spacing x-height z-index';
+		
+		var values =	'above absolute all always aqua armenian attr aural auto avoid baseline behind below bidi-override black blink block blue bold bolder '+
+						'both bottom braille capitalize caption center center-left center-right circle close-quote code collapse compact condensed '+
+						'continuous counter counters crop cross crosshair cursive dashed decimal decimal-leading-zero digits disc dotted double '+
+						'embed embossed e-resize expanded extra-condensed extra-expanded fantasy far-left far-right fast faster fixed format fuchsia '+
+						'gray green groove handheld hebrew help hidden hide high higher icon inline-table inline inset inside invert italic '+
+						'justify landscape large larger left-side left leftwards level lighter lime line-through list-item local loud lower-alpha '+
+						'lowercase lower-greek lower-latin lower-roman lower low ltr marker maroon medium message-box middle mix move narrower '+
+						'navy ne-resize no-close-quote none no-open-quote no-repeat normal nowrap n-resize nw-resize oblique olive once open-quote outset '+
+						'outside overline pointer portrait pre print projection purple red relative repeat repeat-x repeat-y rgb ridge right right-side '+
+						'rightwards rtl run-in screen scroll semi-condensed semi-expanded separate se-resize show silent silver slower slow '+
+						'small small-caps small-caption smaller soft solid speech spell-out square s-resize static status-bar sub super sw-resize '+
+						'table-caption table-cell table-column table-column-group table-footer-group table-header-group table-row table-row-group teal '+
+						'text-bottom text-top thick thin top transparent tty tv ultra-condensed ultra-expanded underline upper-alpha uppercase upper-latin '+
+						'upper-roman url visible wait white wider w-resize x-fast x-high x-large x-loud x-low x-slow x-small x-soft xx-large xx-small yellow';
+		
+		var fonts =		'[mM]onospace [tT]ahoma [vV]erdana [aA]rial [hH]elvetica [sS]ans-serif [sS]erif [cC]ourier mono sans serif';
+		
+		var statements		= '!important !default';
+		var preprocessor	= '@import @extend @debug @warn @if @for @while @mixin @include';
+		
+		var r = SyntaxHighlighter.regexLib;
+		
+		this.regexList = [
+			{ regex: r.multiLineCComments,								css: 'comments' },		// multiline comments
+			{ regex: r.singleLineCComments,								css: 'comments' },		// singleline comments
+			{ regex: r.doubleQuotedString,								css: 'string' },		// double quoted strings
+			{ regex: r.singleQuotedString,								css: 'string' },		// single quoted strings
+			{ regex: /\#[a-fA-F0-9]{3,6}/g,								css: 'value' },			// html colors
+			{ regex: /\b(-?\d+)(\.\d+)?(px|em|pt|\:|\%|)\b/g,			css: 'value' },			// sizes
+			{ regex: /\$\w+/g,											css: 'variable' },		// variables
+			{ regex: new RegExp(this.getKeywords(statements), 'g'),		css: 'color3' },		// statements
+			{ regex: new RegExp(this.getKeywords(preprocessor), 'g'),	css: 'preprocessor' },	// preprocessor
+			{ regex: new RegExp(getKeywordsCSS(keywords), 'gm'),		css: 'keyword' },		// keywords
+			{ regex: new RegExp(getValuesCSS(values), 'g'),				css: 'value' },			// values
+			{ regex: new RegExp(this.getKeywords(fonts), 'g'),			css: 'color1' }			// fonts
+			];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['sass', 'scss'];
+
+	SyntaxHighlighter.brushes.Sass = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushScala.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushScala.js
new file mode 100644
index 0000000000000000000000000000000000000000..4b0b6f04d2982779675a4e005072cdec4c63b9d5
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushScala.js
@@ -0,0 +1,51 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		// Contributed by Yegor Jbanov and David Bernard.
+	
+		var keywords =	'val sealed case def true trait implicit forSome import match object null finally super ' +
+						'override try lazy for var catch throw type extends class while with new final yield abstract ' +
+						'else do if return protected private this package false';
+
+		var keyops =	'[_:=><%#@]+';
+
+		this.regexList = [
+			{ regex: SyntaxHighlighter.regexLib.singleLineCComments,			css: 'comments' },	// one line comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineCComments,				css: 'comments' },	// multiline comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineSingleQuotedString,	css: 'string' },	// multi-line strings
+			{ regex: SyntaxHighlighter.regexLib.multiLineDoubleQuotedString,    css: 'string' },	// double-quoted string
+			{ regex: SyntaxHighlighter.regexLib.singleQuotedString,				css: 'string' },	// strings
+			{ regex: /0x[a-f0-9]+|\d+(\.\d+)?/gi,								css: 'value' },		// numbers
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),				css: 'keyword' },	// keywords
+			{ regex: new RegExp(keyops, 'gm'),									css: 'keyword' }	// scala keyword
+			];
+	}
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['scala'];
+
+	SyntaxHighlighter.brushes.Scala = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushSql.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushSql.js
new file mode 100644
index 0000000000000000000000000000000000000000..5c2cd8806ffed9b2002446b3cb6e1ddc70231f3a
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushSql.js
@@ -0,0 +1,66 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var funcs	=	'abs avg case cast coalesce convert count current_timestamp ' +
+						'current_user day isnull left lower month nullif replace right ' +
+						'session_user space substring sum system_user upper user year';
+
+		var keywords =	'absolute action add after alter as asc at authorization begin bigint ' +
+						'binary bit by cascade char character check checkpoint close collate ' +
+						'column commit committed connect connection constraint contains continue ' +
+						'create cube current current_date current_time cursor database date ' +
+						'deallocate dec decimal declare default delete desc distinct double drop ' +
+						'dynamic else end end-exec escape except exec execute false fetch first ' +
+						'float for force foreign forward free from full function global goto grant ' +
+						'group grouping having hour ignore index inner insensitive insert instead ' +
+						'int integer intersect into is isolation key last level load local max min ' +
+						'minute modify move name national nchar next no numeric of off on only ' +
+						'open option order out output partial password precision prepare primary ' +
+						'prior privileges procedure public read real references relative repeatable ' +
+						'restrict return returns revoke rollback rollup rows rule schema scroll ' +
+						'second section select sequence serializable set size smallint static ' +
+						'statistics table temp temporary then time timestamp to top transaction ' +
+						'translation trigger true truncate uncommitted union unique update values ' +
+						'varchar varying view when where with work';
+
+		var operators =	'all and any between cross in join like not null or outer some';
+
+		this.regexList = [
+			{ regex: /--(.*)$/gm,												css: 'comments' },			// one line and multiline comments
+			{ regex: SyntaxHighlighter.regexLib.multiLineDoubleQuotedString,	css: 'string' },			// double quoted strings
+			{ regex: SyntaxHighlighter.regexLib.multiLineSingleQuotedString,	css: 'string' },			// single quoted strings
+			{ regex: new RegExp(this.getKeywords(funcs), 'gmi'),				css: 'color2' },			// functions
+			{ regex: new RegExp(this.getKeywords(operators), 'gmi'),			css: 'color1' },			// operators and such
+			{ regex: new RegExp(this.getKeywords(keywords), 'gmi'),				css: 'keyword' }			// keyword
+			];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['sql'];
+
+	SyntaxHighlighter.brushes.Sql = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
+
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushVb.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushVb.js
new file mode 100644
index 0000000000000000000000000000000000000000..be845dc0b30131bcc5294f7313225e2f59ab1aae
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushVb.js
@@ -0,0 +1,56 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		var keywords =	'AddHandler AddressOf AndAlso Alias And Ansi As Assembly Auto ' +
+						'Boolean ByRef Byte ByVal Call Case Catch CBool CByte CChar CDate ' +
+						'CDec CDbl Char CInt Class CLng CObj Const CShort CSng CStr CType ' +
+						'Date Decimal Declare Default Delegate Dim DirectCast Do Double Each ' +
+						'Else ElseIf End Enum Erase Error Event Exit False Finally For Friend ' +
+						'Function Get GetType GoSub GoTo Handles If Implements Imports In ' +
+						'Inherits Integer Interface Is Let Lib Like Long Loop Me Mod Module ' +
+						'MustInherit MustOverride MyBase MyClass Namespace New Next Not Nothing ' +
+						'NotInheritable NotOverridable Object On Option Optional Or OrElse ' +
+						'Overloads Overridable Overrides ParamArray Preserve Private Property ' +
+						'Protected Public RaiseEvent ReadOnly ReDim REM RemoveHandler Resume ' +
+						'Return Select Set Shadows Shared Short Single Static Step Stop String ' +
+						'Structure Sub SyncLock Then Throw To True Try TypeOf Unicode Until ' +
+						'Variant When While With WithEvents WriteOnly Xor';
+
+		this.regexList = [
+			{ regex: /'.*$/gm,										css: 'comments' },			// one line comments
+			{ regex: SyntaxHighlighter.regexLib.doubleQuotedString,	css: 'string' },			// strings
+			{ regex: /^\s*#.*$/gm,									css: 'preprocessor' },		// preprocessor tags like #region and #endregion
+			{ regex: new RegExp(this.getKeywords(keywords), 'gm'),	css: 'keyword' }			// vb keyword
+			];
+
+		this.forHtmlScript(SyntaxHighlighter.regexLib.aspScriptTags);
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['vb', 'vbnet'];
+
+	SyntaxHighlighter.brushes.Vb = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shBrushXml.js b/apps/files_textviewer/js/syntaxhighlighter/shBrushXml.js
new file mode 100644
index 0000000000000000000000000000000000000000..69d9fd0b1f4405a03aaa8c41d0b089168aefa43e
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shBrushXml.js
@@ -0,0 +1,69 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+;(function()
+{
+	// CommonJS
+	typeof(require) != 'undefined' ? SyntaxHighlighter = require('shCore').SyntaxHighlighter : null;
+
+	function Brush()
+	{
+		function process(match, regexInfo)
+		{
+			var constructor = SyntaxHighlighter.Match,
+				code = match[0],
+				tag = new XRegExp('(&lt;|<)[\\s\\/\\?]*(?<name>[:\\w-\\.]+)', 'xg').exec(code),
+				result = []
+				;
+		
+			if (match.attributes != null) 
+			{
+				var attributes,
+					regex = new XRegExp('(?<name> [\\w:\\-\\.]+)' +
+										'\\s*=\\s*' +
+										'(?<value> ".*?"|\'.*?\'|\\w+)',
+										'xg');
+
+				while ((attributes = regex.exec(code)) != null) 
+				{
+					result.push(new constructor(attributes.name, match.index + attributes.index, 'color1'));
+					result.push(new constructor(attributes.value, match.index + attributes.index + attributes[0].indexOf(attributes.value), 'string'));
+				}
+			}
+
+			if (tag != null)
+				result.push(
+					new constructor(tag.name, match.index + tag[0].indexOf(tag.name), 'keyword')
+				);
+
+			return result;
+		}
+	
+		this.regexList = [
+			{ regex: new XRegExp('(\\&lt;|<)\\!\\[[\\w\\s]*?\\[(.|\\s)*?\\]\\](\\&gt;|>)', 'gm'),			css: 'color2' },	// <![ ... [ ... ]]>
+			{ regex: SyntaxHighlighter.regexLib.xmlComments,												css: 'comments' },	// <!-- ... -->
+			{ regex: new XRegExp('(&lt;|<)[\\s\\/\\?]*(\\w+)(?<attributes>.*?)[\\s\\/\\?]*(&gt;|>)', 'sg'), func: process }
+		];
+	};
+
+	Brush.prototype	= new SyntaxHighlighter.Highlighter();
+	Brush.aliases	= ['xml', 'xhtml', 'xslt', 'html'];
+
+	SyntaxHighlighter.brushes.Xml = Brush;
+
+	// CommonJS
+	typeof(exports) != 'undefined' ? exports.Brush = Brush : null;
+})();
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shCore.js b/apps/files_textviewer/js/syntaxhighlighter/shCore.js
new file mode 100644
index 0000000000000000000000000000000000000000..b47b6454721504abf3ebe05dad4c903142f843f7
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shCore.js
@@ -0,0 +1,17 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('K M;I(M)1S 2U("2a\'t 4k M 4K 2g 3l 4G 4H");(6(){6 r(f,e){I(!M.1R(f))1S 3m("3s 15 4R");K a=f.1w;f=M(f.1m,t(f)+(e||""));I(a)f.1w={1m:a.1m,19:a.19?a.19.1a(0):N};H f}6 t(f){H(f.1J?"g":"")+(f.4s?"i":"")+(f.4p?"m":"")+(f.4v?"x":"")+(f.3n?"y":"")}6 B(f,e,a,b){K c=u.L,d,h,g;v=R;5K{O(;c--;){g=u[c];I(a&g.3r&&(!g.2p||g.2p.W(b))){g.2q.12=e;I((h=g.2q.X(f))&&h.P===e){d={3k:g.2b.W(b,h,a),1C:h};1N}}}}5v(i){1S i}5q{v=11}H d}6 p(f,e,a){I(3b.Z.1i)H f.1i(e,a);O(a=a||0;a<f.L;a++)I(f[a]===e)H a;H-1}M=6(f,e){K a=[],b=M.1B,c=0,d,h;I(M.1R(f)){I(e!==1d)1S 3m("2a\'t 5r 5I 5F 5B 5C 15 5E 5p");H r(f)}I(v)1S 2U("2a\'t W 3l M 59 5m 5g 5x 5i");e=e||"";O(d={2N:11,19:[],2K:6(g){H e.1i(g)>-1},3d:6(g){e+=g}};c<f.L;)I(h=B(f,c,b,d)){a.U(h.3k);c+=h.1C[0].L||1}Y I(h=n.X.W(z[b],f.1a(c))){a.U(h[0]);c+=h[0].L}Y{h=f.3a(c);I(h==="[")b=M.2I;Y I(h==="]")b=M.1B;a.U(h);c++}a=15(a.1K(""),n.Q.W(e,w,""));a.1w={1m:f,19:d.2N?d.19:N};H a};M.3v="1.5.0";M.2I=1;M.1B=2;K C=/\\$(?:(\\d\\d?|[$&`\'])|{([$\\w]+)})/g,w=/[^5h]+|([\\s\\S])(?=[\\s\\S]*\\1)/g,A=/^(?:[?*+]|{\\d+(?:,\\d*)?})\\??/,v=11,u=[],n={X:15.Z.X,1A:15.Z.1A,1C:1r.Z.1C,Q:1r.Z.Q,1e:1r.Z.1e},x=n.X.W(/()??/,"")[1]===1d,D=6(){K f=/^/g;n.1A.W(f,"");H!f.12}(),y=6(){K f=/x/g;n.Q.W("x",f,"");H!f.12}(),E=15.Z.3n!==1d,z={};z[M.2I]=/^(?:\\\\(?:[0-3][0-7]{0,2}|[4-7][0-7]?|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S]))/;z[M.1B]=/^(?:\\\\(?:0(?:[0-3][0-7]{0,2}|[4-7][0-7]?)?|[1-9]\\d*|x[\\29-26-f]{2}|u[\\29-26-f]{4}|c[A-3o-z]|[\\s\\S])|\\(\\?[:=!]|[?*+]\\?|{\\d+(?:,\\d*)?}\\??)/;M.1h=6(f,e,a,b){u.U({2q:r(f,"g"+(E?"y":"")),2b:e,3r:a||M.1B,2p:b||N})};M.2n=6(f,e){K a=f+"/"+(e||"");H M.2n[a]||(M.2n[a]=M(f,e))};M.3c=6(f){H r(f,"g")};M.5l=6(f){H f.Q(/[-[\\]{}()*+?.,\\\\^$|#\\s]/g,"\\\\$&")};M.5e=6(f,e,a,b){e=r(e,"g"+(b&&E?"y":""));e.12=a=a||0;f=e.X(f);H b?f&&f.P===a?f:N:f};M.3q=6(){M.1h=6(){1S 2U("2a\'t 55 1h 54 3q")}};M.1R=6(f){H 53.Z.1q.W(f)==="[2m 15]"};M.3p=6(f,e,a,b){O(K c=r(e,"g"),d=-1,h;h=c.X(f);){a.W(b,h,++d,f,c);c.12===h.P&&c.12++}I(e.1J)e.12=0};M.57=6(f,e){H 6 a(b,c){K d=e[c].1I?e[c]:{1I:e[c]},h=r(d.1I,"g"),g=[],i;O(i=0;i<b.L;i++)M.3p(b[i],h,6(k){g.U(d.3j?k[d.3j]||"":k[0])});H c===e.L-1||!g.L?g:a(g,c+1)}([f],0)};15.Z.1p=6(f,e){H J.X(e[0])};15.Z.W=6(f,e){H J.X(e)};15.Z.X=6(f){K e=n.X.1p(J,14),a;I(e){I(!x&&e.L>1&&p(e,"")>-1){a=15(J.1m,n.Q.W(t(J),"g",""));n.Q.W(f.1a(e.P),a,6(){O(K c=1;c<14.L-2;c++)I(14[c]===1d)e[c]=1d})}I(J.1w&&J.1w.19)O(K b=1;b<e.L;b++)I(a=J.1w.19[b-1])e[a]=e[b];!D&&J.1J&&!e[0].L&&J.12>e.P&&J.12--}H e};I(!D)15.Z.1A=6(f){(f=n.X.W(J,f))&&J.1J&&!f[0].L&&J.12>f.P&&J.12--;H!!f};1r.Z.1C=6(f){M.1R(f)||(f=15(f));I(f.1J){K e=n.1C.1p(J,14);f.12=0;H e}H f.X(J)};1r.Z.Q=6(f,e){K a=M.1R(f),b,c;I(a&&1j e.58()==="3f"&&e.1i("${")===-1&&y)H n.Q.1p(J,14);I(a){I(f.1w)b=f.1w.19}Y f+="";I(1j e==="6")c=n.Q.W(J,f,6(){I(b){14[0]=1f 1r(14[0]);O(K d=0;d<b.L;d++)I(b[d])14[0][b[d]]=14[d+1]}I(a&&f.1J)f.12=14[14.L-2]+14[0].L;H e.1p(N,14)});Y{c=J+"";c=n.Q.W(c,f,6(){K d=14;H n.Q.W(e,C,6(h,g,i){I(g)5b(g){24"$":H"$";24"&":H d[0];24"`":H d[d.L-1].1a(0,d[d.L-2]);24"\'":H d[d.L-1].1a(d[d.L-2]+d[0].L);5a:i="";g=+g;I(!g)H h;O(;g>d.L-3;){i=1r.Z.1a.W(g,-1)+i;g=1Q.3i(g/10)}H(g?d[g]||"":"$")+i}Y{g=+i;I(g<=d.L-3)H d[g];g=b?p(b,i):-1;H g>-1?d[g+1]:h}})})}I(a&&f.1J)f.12=0;H c};1r.Z.1e=6(f,e){I(!M.1R(f))H n.1e.1p(J,14);K a=J+"",b=[],c=0,d,h;I(e===1d||+e<0)e=5D;Y{e=1Q.3i(+e);I(!e)H[]}O(f=M.3c(f);d=f.X(a);){I(f.12>c){b.U(a.1a(c,d.P));d.L>1&&d.P<a.L&&3b.Z.U.1p(b,d.1a(1));h=d[0].L;c=f.12;I(b.L>=e)1N}f.12===d.P&&f.12++}I(c===a.L){I(!n.1A.W(f,"")||h)b.U("")}Y b.U(a.1a(c));H b.L>e?b.1a(0,e):b};M.1h(/\\(\\?#[^)]*\\)/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"});M.1h(/\\((?!\\?)/,6(){J.19.U(N);H"("});M.1h(/\\(\\?<([$\\w]+)>/,6(f){J.19.U(f[1]);J.2N=R;H"("});M.1h(/\\\\k<([\\w$]+)>/,6(f){K e=p(J.19,f[1]);H e>-1?"\\\\"+(e+1)+(3R(f.2S.3a(f.P+f[0].L))?"":"(?:)"):f[0]});M.1h(/\\[\\^?]/,6(f){H f[0]==="[]"?"\\\\b\\\\B":"[\\\\s\\\\S]"});M.1h(/^\\(\\?([5A]+)\\)/,6(f){J.3d(f[1]);H""});M.1h(/(?:\\s+|#.*)+/,6(f){H n.1A.W(A,f.2S.1a(f.P+f[0].L))?"":"(?:)"},M.1B,6(){H J.2K("x")});M.1h(/\\./,6(){H"[\\\\s\\\\S]"},M.1B,6(){H J.2K("s")})})();1j 2e!="1d"&&(2e.M=M);K 1v=6(){6 r(a,b){a.1l.1i(b)!=-1||(a.1l+=" "+b)}6 t(a){H a.1i("3e")==0?a:"3e"+a}6 B(a){H e.1Y.2A[t(a)]}6 p(a,b,c){I(a==N)H N;K d=c!=R?a.3G:[a.2G],h={"#":"1c",".":"1l"}[b.1o(0,1)]||"3h",g,i;g=h!="3h"?b.1o(1):b.5u();I((a[h]||"").1i(g)!=-1)H a;O(a=0;d&&a<d.L&&i==N;a++)i=p(d[a],b,c);H i}6 C(a,b){K c={},d;O(d 2g a)c[d]=a[d];O(d 2g b)c[d]=b[d];H c}6 w(a,b,c,d){6 h(g){g=g||1P.5y;I(!g.1F){g.1F=g.52;g.3N=6(){J.5w=11}}c.W(d||1P,g)}a.3g?a.3g("4U"+b,h):a.4y(b,h,11)}6 A(a,b){K c=e.1Y.2j,d=N;I(c==N){c={};O(K h 2g e.1U){K g=e.1U[h];d=g.4x;I(d!=N){g.1V=h.4w();O(g=0;g<d.L;g++)c[d[g]]=h}}e.1Y.2j=c}d=e.1U[c[a]];d==N&&b!=11&&1P.1X(e.13.1x.1X+(e.13.1x.3E+a));H d}6 v(a,b){O(K c=a.1e("\\n"),d=0;d<c.L;d++)c[d]=b(c[d],d);H c.1K("\\n")}6 u(a,b){I(a==N||a.L==0||a=="\\n")H a;a=a.Q(/</g,"&1y;");a=a.Q(/ {2,}/g,6(c){O(K d="",h=0;h<c.L-1;h++)d+=e.13.1W;H d+" "});I(b!=N)a=v(a,6(c){I(c.L==0)H"";K d="";c=c.Q(/^(&2s;| )+/,6(h){d=h;H""});I(c.L==0)H d;H d+\'<17 1g="\'+b+\'">\'+c+"</17>"});H a}6 n(a,b){a.1e("\\n");O(K c="",d=0;d<50;d++)c+="                    ";H a=v(a,6(h){I(h.1i("\\t")==-1)H h;O(K g=0;(g=h.1i("\\t"))!=-1;)h=h.1o(0,g)+c.1o(0,b-g%b)+h.1o(g+1,h.L);H h})}6 x(a){H a.Q(/^\\s+|\\s+$/g,"")}6 D(a,b){I(a.P<b.P)H-1;Y I(a.P>b.P)H 1;Y I(a.L<b.L)H-1;Y I(a.L>b.L)H 1;H 0}6 y(a,b){6 c(k){H k[0]}O(K d=N,h=[],g=b.2D?b.2D:c;(d=b.1I.X(a))!=N;){K i=g(d,b);I(1j i=="3f")i=[1f e.2L(i,d.P,b.23)];h=h.1O(i)}H h}6 E(a){K b=/(.*)((&1G;|&1y;).*)/;H a.Q(e.3A.3M,6(c){K d="",h=N;I(h=b.X(c)){c=h[1];d=h[2]}H\'<a 2h="\'+c+\'">\'+c+"</a>"+d})}6 z(){O(K a=1E.36("1k"),b=[],c=0;c<a.L;c++)a[c].3s=="20"&&b.U(a[c]);H b}6 f(a){a=a.1F;K b=p(a,".20",R);a=p(a,".3O",R);K c=1E.4i("3t");I(!(!a||!b||p(a,"3t"))){B(b.1c);r(b,"1m");O(K d=a.3G,h=[],g=0;g<d.L;g++)h.U(d[g].4z||d[g].4A);h=h.1K("\\r");c.39(1E.4D(h));a.39(c);c.2C();c.4C();w(c,"4u",6(){c.2G.4E(c);b.1l=b.1l.Q("1m","")})}}I(1j 3F!="1d"&&1j M=="1d")M=3F("M").M;K e={2v:{"1g-27":"","2i-1s":1,"2z-1s-2t":11,1M:N,1t:N,"42-45":R,"43-22":4,1u:R,16:R,"3V-17":R,2l:11,"41-40":R,2k:11,"1z-1k":11},13:{1W:"&2s;",2M:R,46:11,44:11,34:"4n",1x:{21:"4o 1m",2P:"?",1X:"1v\\n\\n",3E:"4r\'t 4t 1D O: ",4g:"4m 4B\'t 51 O 1z-1k 4F: ",37:\'<!4T 1z 4S "-//4V//3H 4W 1.0 4Z//4Y" "1Z://2y.3L.3K/4X/3I/3H/3I-4P.4J"><1z 4I="1Z://2y.3L.3K/4L/5L"><3J><4N 1Z-4M="5G-5M" 6K="2O/1z; 6J=6I-8" /><1t>6L 1v</1t></3J><3B 1L="25-6M:6Q,6P,6O,6N-6F;6y-2f:#6x;2f:#6w;25-22:6v;2O-3D:3C;"><T 1L="2O-3D:3C;3w-32:1.6z;"><T 1L="25-22:6A-6E;">1v</T><T 1L="25-22:.6C;3w-6B:6R;"><T>3v 3.0.76 (72 73 3x)</T><T><a 2h="1Z://3u.2w/1v" 1F="38" 1L="2f:#3y">1Z://3u.2w/1v</a></T><T>70 17 6U 71.</T><T>6T 6X-3x 6Y 6D.</T></T><T>6t 61 60 J 1k, 5Z <a 2h="6u://2y.62.2w/63-66/65?64=5X-5W&5P=5O" 1L="2f:#3y">5R</a> 5V <2R/>5U 5T 5S!</T></T></3B></1z>\'}},1Y:{2j:N,2A:{}},1U:{},3A:{6n:/\\/\\*[\\s\\S]*?\\*\\//2c,6m:/\\/\\/.*$/2c,6l:/#.*$/2c,6k:/"([^\\\\"\\n]|\\\\.)*"/g,6o:/\'([^\\\\\'\\n]|\\\\.)*\'/g,6p:1f M(\'"([^\\\\\\\\"]|\\\\\\\\.)*"\',"3z"),6s:1f M("\'([^\\\\\\\\\']|\\\\\\\\.)*\'","3z"),6q:/(&1y;|<)!--[\\s\\S]*?--(&1G;|>)/2c,3M:/\\w+:\\/\\/[\\w-.\\/?%&=:@;]*/g,6a:{18:/(&1y;|<)\\?=?/g,1b:/\\?(&1G;|>)/g},69:{18:/(&1y;|<)%=?/g,1b:/%(&1G;|>)/g},6d:{18:/(&1y;|<)\\s*1k.*?(&1G;|>)/2T,1b:/(&1y;|<)\\/\\s*1k\\s*(&1G;|>)/2T}},16:{1H:6(a){6 b(i,k){H e.16.2o(i,k,e.13.1x[k])}O(K c=\'<T 1g="16">\',d=e.16.2x,h=d.2X,g=0;g<h.L;g++)c+=(d[h[g]].1H||b)(a,h[g]);c+="</T>";H c},2o:6(a,b,c){H\'<2W><a 2h="#" 1g="6e 6h\'+b+" "+b+\'">\'+c+"</a></2W>"},2b:6(a){K b=a.1F,c=b.1l||"";b=B(p(b,".20",R).1c);K d=6(h){H(h=15(h+"6f(\\\\w+)").X(c))?h[1]:N}("6g");b&&d&&e.16.2x[d].2B(b);a.3N()},2x:{2X:["21","2P"],21:{1H:6(a){I(a.V("2l")!=R)H"";K b=a.V("1t");H e.16.2o(a,"21",b?b:e.13.1x.21)},2B:6(a){a=1E.6j(t(a.1c));a.1l=a.1l.Q("47","")}},2P:{2B:6(){K a="68=0";a+=", 18="+(31.30-33)/2+", 32="+(31.2Z-2Y)/2+", 30=33, 2Z=2Y";a=a.Q(/^,/,"");a=1P.6Z("","38",a);a.2C();K b=a.1E;b.6W(e.13.1x.37);b.6V();a.2C()}}}},35:6(a,b){K c;I(b)c=[b];Y{c=1E.36(e.13.34);O(K d=[],h=0;h<c.L;h++)d.U(c[h]);c=d}c=c;d=[];I(e.13.2M)c=c.1O(z());I(c.L===0)H d;O(h=0;h<c.L;h++){O(K g=c[h],i=a,k=c[h].1l,j=3W 0,l={},m=1f M("^\\\\[(?<2V>(.*?))\\\\]$"),s=1f M("(?<27>[\\\\w-]+)\\\\s*:\\\\s*(?<1T>[\\\\w-%#]+|\\\\[.*?\\\\]|\\".*?\\"|\'.*?\')\\\\s*;?","g");(j=s.X(k))!=N;){K o=j.1T.Q(/^[\'"]|[\'"]$/g,"");I(o!=N&&m.1A(o)){o=m.X(o);o=o.2V.L>0?o.2V.1e(/\\s*,\\s*/):[]}l[j.27]=o}g={1F:g,1n:C(i,l)};g.1n.1D!=N&&d.U(g)}H d},1M:6(a,b){K c=J.35(a,b),d=N,h=e.13;I(c.L!==0)O(K g=0;g<c.L;g++){b=c[g];K i=b.1F,k=b.1n,j=k.1D,l;I(j!=N){I(k["1z-1k"]=="R"||e.2v["1z-1k"]==R){d=1f e.4l(j);j="4O"}Y I(d=A(j))d=1f d;Y 6H;l=i.3X;I(h.2M){l=l;K m=x(l),s=11;I(m.1i("<![6G[")==0){m=m.4h(9);s=R}K o=m.L;I(m.1i("]]\\>")==o-3){m=m.4h(0,o-3);s=R}l=s?m:l}I((i.1t||"")!="")k.1t=i.1t;k.1D=j;d.2Q(k);b=d.2F(l);I((i.1c||"")!="")b.1c=i.1c;i.2G.74(b,i)}}},2E:6(a){w(1P,"4k",6(){e.1M(a)})}};e.2E=e.2E;e.1M=e.1M;e.2L=6(a,b,c){J.1T=a;J.P=b;J.L=a.L;J.23=c;J.1V=N};e.2L.Z.1q=6(){H J.1T};e.4l=6(a){6 b(j,l){O(K m=0;m<j.L;m++)j[m].P+=l}K c=A(a),d,h=1f e.1U.5Y,g=J,i="2F 1H 2Q".1e(" ");I(c!=N){d=1f c;O(K k=0;k<i.L;k++)(6(){K j=i[k];g[j]=6(){H h[j].1p(h,14)}})();d.28==N?1P.1X(e.13.1x.1X+(e.13.1x.4g+a)):h.2J.U({1I:d.28.17,2D:6(j){O(K l=j.17,m=[],s=d.2J,o=j.P+j.18.L,F=d.28,q,G=0;G<s.L;G++){q=y(l,s[G]);b(q,o);m=m.1O(q)}I(F.18!=N&&j.18!=N){q=y(j.18,F.18);b(q,j.P);m=m.1O(q)}I(F.1b!=N&&j.1b!=N){q=y(j.1b,F.1b);b(q,j.P+j[0].5Q(j.1b));m=m.1O(q)}O(j=0;j<m.L;j++)m[j].1V=c.1V;H m}})}};e.4j=6(){};e.4j.Z={V:6(a,b){K c=J.1n[a];c=c==N?b:c;K d={"R":R,"11":11}[c];H d==N?c:d},3Y:6(a){H 1E.4i(a)},4c:6(a,b){K c=[];I(a!=N)O(K d=0;d<a.L;d++)I(1j a[d]=="2m")c=c.1O(y(b,a[d]));H J.4e(c.6b(D))},4e:6(a){O(K b=0;b<a.L;b++)I(a[b]!==N)O(K c=a[b],d=c.P+c.L,h=b+1;h<a.L&&a[b]!==N;h++){K g=a[h];I(g!==N)I(g.P>d)1N;Y I(g.P==c.P&&g.L>c.L)a[b]=N;Y I(g.P>=c.P&&g.P<d)a[h]=N}H a},4d:6(a){K b=[],c=2u(J.V("2i-1s"));v(a,6(d,h){b.U(h+c)});H b},3U:6(a){K b=J.V("1M",[]);I(1j b!="2m"&&b.U==N)b=[b];a:{a=a.1q();K c=3W 0;O(c=c=1Q.6c(c||0,0);c<b.L;c++)I(b[c]==a){b=c;1N a}b=-1}H b!=-1},2r:6(a,b,c){a=["1s","6i"+b,"P"+a,"6r"+(b%2==0?1:2).1q()];J.3U(b)&&a.U("67");b==0&&a.U("1N");H\'<T 1g="\'+a.1K(" ")+\'">\'+c+"</T>"},3Q:6(a,b){K c="",d=a.1e("\\n").L,h=2u(J.V("2i-1s")),g=J.V("2z-1s-2t");I(g==R)g=(h+d-1).1q().L;Y I(3R(g)==R)g=0;O(K i=0;i<d;i++){K k=b?b[i]:h+i,j;I(k==0)j=e.13.1W;Y{j=g;O(K l=k.1q();l.L<j;)l="0"+l;j=l}a=j;c+=J.2r(i,k,a)}H c},49:6(a,b){a=x(a);K c=a.1e("\\n");J.V("2z-1s-2t");K d=2u(J.V("2i-1s"));a="";O(K h=J.V("1D"),g=0;g<c.L;g++){K i=c[g],k=/^(&2s;|\\s)+/.X(i),j=N,l=b?b[g]:d+g;I(k!=N){j=k[0].1q();i=i.1o(j.L);j=j.Q(" ",e.13.1W)}i=x(i);I(i.L==0)i=e.13.1W;a+=J.2r(g,l,(j!=N?\'<17 1g="\'+h+\' 5N">\'+j+"</17>":"")+i)}H a},4f:6(a){H a?"<4a>"+a+"</4a>":""},4b:6(a,b){6 c(l){H(l=l?l.1V||g:g)?l+" ":""}O(K d=0,h="",g=J.V("1D",""),i=0;i<b.L;i++){K k=b[i],j;I(!(k===N||k.L===0)){j=c(k);h+=u(a.1o(d,k.P-d),j+"48")+u(k.1T,j+k.23);d=k.P+k.L+(k.75||0)}}h+=u(a.1o(d),c()+"48");H h},1H:6(a){K b="",c=["20"],d;I(J.V("2k")==R)J.1n.16=J.1n.1u=11;1l="20";J.V("2l")==R&&c.U("47");I((1u=J.V("1u"))==11)c.U("6S");c.U(J.V("1g-27"));c.U(J.V("1D"));a=a.Q(/^[ ]*[\\n]+|[\\n]*[ ]*$/g,"").Q(/\\r/g," ");b=J.V("43-22");I(J.V("42-45")==R)a=n(a,b);Y{O(K h="",g=0;g<b;g++)h+=" ";a=a.Q(/\\t/g,h)}a=a;a:{b=a=a;h=/<2R\\s*\\/?>|&1y;2R\\s*\\/?&1G;/2T;I(e.13.46==R)b=b.Q(h,"\\n");I(e.13.44==R)b=b.Q(h,"");b=b.1e("\\n");h=/^\\s*/;g=4Q;O(K i=0;i<b.L&&g>0;i++){K k=b[i];I(x(k).L!=0){k=h.X(k);I(k==N){a=a;1N a}g=1Q.4q(k[0].L,g)}}I(g>0)O(i=0;i<b.L;i++)b[i]=b[i].1o(g);a=b.1K("\\n")}I(1u)d=J.4d(a);b=J.4c(J.2J,a);b=J.4b(a,b);b=J.49(b,d);I(J.V("41-40"))b=E(b);1j 2H!="1d"&&2H.3S&&2H.3S.1C(/5s/)&&c.U("5t");H b=\'<T 1c="\'+t(J.1c)+\'" 1g="\'+c.1K(" ")+\'">\'+(J.V("16")?e.16.1H(J):"")+\'<3Z 5z="0" 5H="0" 5J="0">\'+J.4f(J.V("1t"))+"<3T><3P>"+(1u?\'<2d 1g="1u">\'+J.3Q(a)+"</2d>":"")+\'<2d 1g="17"><T 1g="3O">\'+b+"</T></2d></3P></3T></3Z></T>"},2F:6(a){I(a===N)a="";J.17=a;K b=J.3Y("T");b.3X=J.1H(a);J.V("16")&&w(p(b,".16"),"5c",e.16.2b);J.V("3V-17")&&w(p(b,".17"),"56",f);H b},2Q:6(a){J.1c=""+1Q.5d(1Q.5n()*5k).1q();e.1Y.2A[t(J.1c)]=J;J.1n=C(e.2v,a||{});I(J.V("2k")==R)J.1n.16=J.1n.1u=11},5j:6(a){a=a.Q(/^\\s+|\\s+$/g,"").Q(/\\s+/g,"|");H"\\\\b(?:"+a+")\\\\b"},5f:6(a){J.28={18:{1I:a.18,23:"1k"},1b:{1I:a.1b,23:"1k"},17:1f M("(?<18>"+a.18.1m+")(?<17>.*?)(?<1b>"+a.1b.1m+")","5o")}}};H e}();1j 2e!="1d"&&(2e.1v=1v);',62,441,'||||||function|||||||||||||||||||||||||||||||||||||return|if|this|var|length|XRegExp|null|for|index|replace|true||div|push|getParam|call|exec|else|prototype||false|lastIndex|config|arguments|RegExp|toolbar|code|left|captureNames|slice|right|id|undefined|split|new|class|addToken|indexOf|typeof|script|className|source|params|substr|apply|toString|String|line|title|gutter|SyntaxHighlighter|_xregexp|strings|lt|html|test|OUTSIDE_CLASS|match|brush|document|target|gt|getHtml|regex|global|join|style|highlight|break|concat|window|Math|isRegExp|throw|value|brushes|brushName|space|alert|vars|http|syntaxhighlighter|expandSource|size|css|case|font|Fa|name|htmlScript|dA|can|handler|gm|td|exports|color|in|href|first|discoveredBrushes|light|collapse|object|cache|getButtonHtml|trigger|pattern|getLineHtml|nbsp|numbers|parseInt|defaults|com|items|www|pad|highlighters|execute|focus|func|all|getDiv|parentNode|navigator|INSIDE_CLASS|regexList|hasFlag|Match|useScriptTags|hasNamedCapture|text|help|init|br|input|gi|Error|values|span|list|250|height|width|screen|top|500|tagName|findElements|getElementsByTagName|aboutDialog|_blank|appendChild|charAt|Array|copyAsGlobal|setFlag|highlighter_|string|attachEvent|nodeName|floor|backref|output|the|TypeError|sticky|Za|iterate|freezeTokens|scope|type|textarea|alexgorbatchev|version|margin|2010|005896|gs|regexLib|body|center|align|noBrush|require|childNodes|DTD|xhtml1|head|org|w3|url|preventDefault|container|tr|getLineNumbersHtml|isNaN|userAgent|tbody|isLineHighlighted|quick|void|innerHTML|create|table|links|auto|smart|tab|stripBrs|tabs|bloggerMode|collapsed|plain|getCodeLinesHtml|caption|getMatchesHtml|findMatches|figureOutLineNumbers|removeNestedMatches|getTitleHtml|brushNotHtmlScript|substring|createElement|Highlighter|load|HtmlScript|Brush|pre|expand|multiline|min|Can|ignoreCase|find|blur|extended|toLowerCase|aliases|addEventListener|innerText|textContent|wasn|select|createTextNode|removeChild|option|same|frame|xmlns|dtd|twice|1999|equiv|meta|htmlscript|transitional|1E3|expected|PUBLIC|DOCTYPE|on|W3C|XHTML|TR|EN|Transitional||configured|srcElement|Object|after|run|dblclick|matchChain|valueOf|constructor|default|switch|click|round|execAt|forHtmlScript|token|gimy|functions|getKeywords|1E6|escape|within|random|sgi|another|finally|supply|MSIE|ie|toUpperCase|catch|returnValue|definition|event|border|imsx|constructing|one|Infinity|from|when|Content|cellpadding|flags|cellspacing|try|xhtml|Type|spaces|2930402|hosted_button_id|lastIndexOf|donate|active|development|keep|to|xclick|_s|Xml|please|like|you|paypal|cgi|cmd|webscr|bin|highlighted|scrollbars|aspScriptTags|phpScriptTags|sort|max|scriptScriptTags|toolbar_item|_|command|command_|number|getElementById|doubleQuotedString|singleLinePerlComments|singleLineCComments|multiLineCComments|singleQuotedString|multiLineDoubleQuotedString|xmlComments|alt|multiLineSingleQuotedString|If|https|1em|000|fff|background|5em|xx|bottom|75em|Gorbatchev|large|serif|CDATA|continue|utf|charset|content|About|family|sans|Helvetica|Arial|Geneva|3em|nogutter|Copyright|syntax|close|write|2004|Alex|open|JavaScript|highlighter|July|02|replaceChild|offset|83'.split('|'),0,{}))
diff --git a/apps/files_textviewer/js/syntaxhighlighter/shLegacy.js b/apps/files_textviewer/js/syntaxhighlighter/shLegacy.js
new file mode 100644
index 0000000000000000000000000000000000000000..6d9fd4d19f678550adbc8dc4204cb09a42d06ab8
--- /dev/null
+++ b/apps/files_textviewer/js/syntaxhighlighter/shLegacy.js
@@ -0,0 +1,17 @@
+/**
+ * SyntaxHighlighter
+ * http://alexgorbatchev.com/SyntaxHighlighter
+ *
+ * SyntaxHighlighter is donationware. If you are using it, please donate.
+ * http://alexgorbatchev.com/SyntaxHighlighter/donate.html
+ *
+ * @version
+ * 3.0.83 (July 02 2010)
+ * 
+ * @copyright
+ * Copyright (C) 2004-2010 Alex Gorbatchev.
+ *
+ * @license
+ * Dual licensed under the MIT and GPL licenses.
+ */
+eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('3 u={8:{}};u.8={A:4(c,k,l,m,n,o){4 d(a,b){2 a!=1?a:b}4 f(a){2 a!=1?a.E():1}c=c.I(":");3 g=c[0],e={};t={"r":K};M=1;5=8.5;9(3 j R c)e[c[j]]="r";k=f(d(k,5.C));l=f(d(l,5.D));m=f(d(m,5.s));o=f(d(o,5.Q));n=f(d(n,5["x-y"]));2{P:g,C:d(t[e.O],k),D:d(t[e.N],l),s:d({"r":r}[e.s],m),"x-y":d(4(a,b){9(3 h=T S("^"+b+"\\\\[(?<q>\\\\w+)\\\\]$","U"),i=1,p=0;p<a.7;p++)6((i=h.J(a[p]))!=1)2 i.q;2 1}(c,"G"),n)}},F:4(c,k,l,m,n,o){4 d(){9(3 a=H,b=0;b<a.7;b++)6(a[b]!==1){6(z a[b]=="L"&&a[b]!="")2 a[b]+"";6(z a[b]=="X"&&a[b].q!="")2 a[b].q+""}2 1}4 f(a,b,h){h=12.13(h);9(3 i=0;i<h.7;i++)h[i].V("15")==b&&a.Y(h[i])}3 g=[];f(g,c,"Z");f(g,c,"W");6(g.7!==0)9(c=0;c<g.7;c++){3 e=g[c],j=d(e.B["14"],e.10,e.B.v,e.v);6(j!==1){j=u.8.A(j,k,l,m,n,o);8.11(j,e)}}}};',62,68,'|null|return|var|function|defaults|if|length|SyntaxHighlighter|for|||||||||||||||||value|true|collapse|reverse|dp|language||first|line|typeof|parseParams|attributes|gutter|toolbar|toString|HighlightAll|firstline|arguments|split|exec|false|string|result|nocontrols|nogutter|brush|ruler|in|XRegExp|new|gi|getAttribute|textarea|object|push|pre|className|highlight|document|getElementsByTagName|class|name'.split('|'),0,{}))
diff --git a/apps/files_textviewer/js/textviewer.js b/apps/files_textviewer/js/textviewer.js
new file mode 100644
index 0000000000000000000000000000000000000000..143c97c9d47e681c013394635b4536c18ea1dfca
--- /dev/null
+++ b/apps/files_textviewer/js/textviewer.js
@@ -0,0 +1,130 @@
+TextViewer=new Object();
+
+TextViewer.type='';
+TextViewer.types={
+	'ac3':'shBrushAS3',
+	'applescript':'shBrushAppleScript',
+	'bash':'shBrushBash',
+	'sh':'shBrushBash',
+	'csharp':'shBrushCSharp',
+	'coldfusion':'shBrushColdFusion',
+	'cpp':'shBrushCpp',
+	'css':'shBrushCss',
+	'delphi':'shBrushDelphi',
+	'diff':'shBrushDiff',
+	'erlang':'shBrushErlang',
+	'groovy':'shBrushGroovy',
+	'javascript':'shBrushJScript',
+	'js':'shBrushJScript',
+	'java':'shBrushJava',
+	'javafx':'shBrushJavaFX',
+	'perl':'shBrushPerl',
+	'php':'shBrushPhp',
+	'plain':'shBrushPlain',
+	'powershell':'shBrushPowerShell',
+	'python':'shBrushPython',
+	'py':'shBrushPython',
+	'ruby':'shBrushRuby',
+	'sass':'shBrushSass',
+	'scala':'shBrushScala',
+	'sql':'shBrushSql',
+	'vb':'shBrushVb',
+	'xml':'shBrushXml',
+}
+
+TextViewer.load=function(ready){
+	if(!TextViewer.load.done){
+		OC.addStyle('files_textviewer','syntaxhighlighter/shCoreDefault');
+		OC.addStyle('files_textviewer','syntaxhighlighter/shCore');
+		OC.addStyle('files_textviewer','syntaxhighlighter/shThemeDefault');
+		OC.addStyle('files_textviewer','style');
+		OC.addScript('files_textviewer','syntaxhighlighter/shCore',function(){
+			if(ready){
+				ready();
+			}
+		});
+	}else if(ready){
+		ready();
+	}
+}
+TextViewer.load.done=false;
+
+TextViewer.showText=function(dir,file){
+	var type;
+	var parts=file.split('.');
+	var ext=parts[parts.length-1];
+	if(TextViewer.types[ext]){
+		type=ext;
+	}else{
+		type='plain';
+	}
+	TextViewer.type=type;
+	var div=$('<div id="textframe" class="center"></div>');
+	div.click(TextViewer.hideText);
+	TextViewer.textFrame=$('<div></div>');
+	TextViewer.textFrame.click(function(event){
+		event.stopPropagation();
+	});
+	TextViewer.textFrame.pre=$('<pre></pre>');
+	div.append(TextViewer.textFrame);
+	TextViewer.textFrame.append(TextViewer.textFrame.pre);
+	$('body').append(div);
+	TextViewer.loadHighlighter(function(){
+		$.ajax({
+			url: OC.filePath('files','ajax','download.php')+'?files='+encodeURIComponent(file)+'&dir='+encodeURIComponent(dir),
+			complete: function(text){
+				TextViewer.textFrame.pre.text(text.responseText);
+				TextViewer.textFrame.pre.attr('class','brush: '+TextViewer.type+';');
+				SyntaxHighlighter.highlight(null,TextViewer.textFrame.pre[0]);
+			}
+		});
+	});
+}
+
+TextViewer.hideText=function(){
+	$('#textframe').remove();
+} 
+
+TextViewer.loadedTypes=new Array();
+TextViewer.loadHighlighter=function(ready){
+	TextViewer.load(function(){
+		TextViewer.type=(TextViewer.types[TextViewer.type])?TextViewer.type:'plain';
+		if(!TextViewer.loadedTypes[TextViewer.type]){
+			OC.addScript('files_textviewer','syntaxhighlighter/'+TextViewer.types[TextViewer.type],function(){
+				TextViewer.loadedTypes[TextViewer.type]=true;
+				SyntaxHighlighter.vars.discoveredBrushes=null; //force the highlighter to refresh it's cache
+				if(ready){
+					ready();
+				}
+			});
+		}else{
+			if(ready){
+				ready();
+			};
+		}
+	});
+}
+
+$(document).ready(function() {
+	if(typeof FileActions!=='undefined'){
+		FileActions.register('text','View','',function(filename){
+			TextViewer.showText($('#dir').val(),filename);
+		});
+		FileActions.setDefault('text','View');
+		FileActions.register('application/xml','View','',function(filename){
+			TextViewer.showText($('#dir').val(),filename);
+		});
+		FileActions.setDefault('application/xml','View');
+	}
+	OC.search.customResults.Text=function(row,item){
+		var text=item.link.substr(item.link.indexOf('file=')+5);
+		var a=row.find('a');
+		a.data('file',text);
+		a.attr('href','#');
+		a.click(function(){
+			var file=text.split('/').pop();
+			var dir=text.substr(0,text.length-file.length-1);
+			TextViewer.showText(dir,file);
+		});
+	}
+});
diff --git a/apps/media/ajax/api.php b/apps/media/ajax/api.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b4932728da987367eab1ad23205d2b8f211c5c8
--- /dev/null
+++ b/apps/media/ajax/api.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+header('Content-type: text/html; charset=UTF-8') ;
+
+//no apps
+$RUNTIME_NOAPPS=true;
+
+require_once('../../../lib/base.php');
+require_once('../lib_collection.php');
+require_once('../lib_scanner.php');
+
+error_reporting(E_ALL); //no script error reporting because of getID3
+
+$arguments=$_POST;
+
+if(!isset($_POST['action']) and isset($_GET['action'])){
+	$arguments=$_GET;
+}
+
+foreach($arguments as &$argument){
+	$argument=stripslashes($argument);
+}
+global $CONFIG_DATADIRECTORY;
+@ob_clean();
+if(!isset($arguments['artist'])){
+	$arguments['artist']=0;
+}
+if(!isset($arguments['album'])){
+	$arguments['album']=0;
+}
+if(!isset($arguments['search'])){
+	$arguments['search']='';
+}
+OC_MEDIA_COLLECTION::$uid=OC_User::getUser();
+if($arguments['action']){
+	switch($arguments['action']){
+		case 'delete':
+			$path=$arguments['path'];
+			OC_MEDIA_COLLECTION::deleteSongByPath($path);
+			$paths=explode(PATH_SEPARATOR,OC_Preferences::getValue(OC_User::getUser(),'media','paths',''));
+			if(array_search($path,$paths)!==false){
+				unset($paths[array_search($path,$paths)]);
+				OC_Preferences::setValue(OC_User::getUser(),'media','paths',implode(PATH_SEPARATOR,$paths));
+			}
+		case 'get_collection':
+			$data=array();
+			$data['artists']=OC_MEDIA_COLLECTION::getArtists();
+			$data['albums']=OC_MEDIA_COLLECTION::getAlbums();
+			$data['songs']=OC_MEDIA_COLLECTION::getSongs();
+			echo json_encode($data);
+			break;
+		case 'scan':
+			OC_DB::beginTransaction();
+			set_time_limit(0); //recursive scan can take a while
+			$path=$arguments['path'];
+			echo OC_MEDIA_SCANNER::scanFolder($path);
+			OC_DB::commit();
+			flush();
+			break;
+		case 'scanFile':
+			echo (OC_MEDIA_SCANNER::scanFile($arguments['path']))?'true':'false';
+			break;
+		case 'get_artists':
+			echo json_encode(OC_MEDIA_COLLECTION::getArtists($arguments['search']));
+			break;
+		case 'get_albums':
+			echo json_encode(OC_MEDIA_COLLECTION::getAlbums($arguments['artist'],$arguments['search']));
+			break;
+		case 'get_songs':
+			echo json_encode(OC_MEDIA_COLLECTION::getSongs($arguments['artist'],$arguments['album'],$arguments['search']));
+			break;
+		case 'get_path_info':
+			if(OC_Filesystem::file_exists($arguments['path'])){
+				$songId=OC_MEDIA_COLLECTION::getSongByPath($arguments['path']);
+				if($songId==0){
+					unset($_SESSION['collection']);
+					$songId= OC_MEDIA_SCANNER::scanFile($arguments['path']);
+				}
+				if($songId>0){
+					$song=OC_MEDIA_COLLECTION::getSong($songId);
+					$song['artist']=OC_MEDIA_COLLECTION::getArtistName($song['song_artist']);
+					$song['album']=OC_MEDIA_COLLECTION::getAlbumName($song['song_album']);
+					echo json_encode($song);
+				}
+			}
+			break;
+		case 'play':
+			ob_end_clean();
+			
+			$ftype=OC_Filesystem::getMimeType( $arguments['path'] );
+			
+			$songId=OC_MEDIA_COLLECTION::getSongByPath($arguments['path']);
+			OC_MEDIA_COLLECTION::registerPlay($songId);
+			
+			header('Content-Type:'.$ftype);
+			 // calc an offset of 24 hours
+			$offset = 3600 * 24;
+			// calc the string in GMT not localtime and add the offset
+			$expire = "Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";
+			//output the HTTP header
+			header($expire);
+			header('Cache-Control: max-age=3600, must-revalidate');
+			header('Pragma: public');
+			header('Accept-Ranges: bytes');
+			header('Content-Length: '.OC_Filesystem::filesize($arguments['path']));
+			$gmt_mtime = gmdate('D, d M Y H:i:s', OC_Filesystem::filemtime($arguments['path']) ) . ' GMT';
+			header("Last-Modified: " . $gmt_mtime );
+			
+			OC_Filesystem::readfile($arguments['path']);
+			exit;
+		case 'find_music':
+			echo json_encode(findMusic());
+			exit;
+	}
+}
+
+function findMusic($path='/'){
+	$music=array();
+	$dh=OC_Filesystem::opendir($path);
+	if($dh){
+		while($filename=readdir($dh)){
+			if($filename[0]!='.'){
+				$file=$path.'/'.$filename;
+				if(OC_Filesystem::is_dir($file)){
+					$music=array_merge($music,findMusic($file));
+				}else{
+					if(OC_MEDIA_SCANNER::isMusic($filename)){
+						$music[]=$file;
+					}
+				}
+			}
+		}
+	}
+	return $music;
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/ajax/autoupdate.php b/apps/media/ajax/autoupdate.php
new file mode 100644
index 0000000000000000000000000000000000000000..ded1fd02bc39a7abd1d6c3b8c1720ef13cd0ef8d
--- /dev/null
+++ b/apps/media/ajax/autoupdate.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+header('Content-type: text/html; charset=UTF-8') ;
+
+//no apps or filesystem
+$RUNTIME_NOAPPS=true;
+$RUNTIME_NOSETUPFS=true;
+
+require_once('../../../lib/base.php');
+
+error_log($_GET['autoupdate']);
+$autoUpdate=(isset($_GET['autoupdate']) and $_GET['autoupdate']=='true');
+error_log((integer)$autoUpdate);
+
+OC_Preferences::setValue(OC_User::getUser(),'media','autoupdate',(integer)$autoUpdate);
+
+echo json_encode( array( "status" => "success", "data" => $autoUpdate));
+?>
\ No newline at end of file
diff --git a/apps/media/appinfo/app.php b/apps/media/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..0d36217bd4dc9312e182da2b6004edb5e7608f3f
--- /dev/null
+++ b/apps/media/appinfo/app.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * ownCloud - media plugin
+ *
+ * @author Robin Appelman
+ * @copyright 2010 Robin Appelman icewind1991@gmail.com
+ *
+ * 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 Lesser General Public
+ * License along with this library.  If not, see <http://www.gnu.org/
+ *
+ */
+
+$l=new OC_L10N('media');
+
+require_once('apps/media/lib_media.php');
+
+OC_Util::addScript('media','loader');
+
+OC_App::register( array( 'order' => 3, 'id' => 'media', 'name' => 'Media' ));
+
+OC_App::addNavigationEntry( array( 'id' => 'media_index', 'order' => 2, 'href' => OC_Helper::linkTo( 'media', 'index.php' ), 'icon' => OC_Helper::imagePath( 'core', 'filetypes/audio.svg' ), 'name' => $l->t('Music') ));
+?>
diff --git a/apps/media/appinfo/database.xml b/apps/media/appinfo/database.xml
new file mode 100644
index 0000000000000000000000000000000000000000..223682fcfcda2da174cc0b0e477c7cef140bc0f3
--- /dev/null
+++ b/apps/media/appinfo/database.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>latin1</charset>
+
+ <table>
+
+  <name>*dbprefix*media_albums</name>
+
+  <declaration>
+
+   <field>
+    <name>album_id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>album_name</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>200</length>
+   </field>
+
+   <field>
+    <name>album_artist</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>album_art</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>200</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*media_artists</name>
+
+  <declaration>
+
+   <field>
+    <name>artist_id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>artist_name</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>200</length>
+   </field>
+
+   <index>
+    <name>artist_name</name>
+    <unique>true</unique>
+    <field>
+     <name>artist_name</name>
+     <sorting>ascending</sorting>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*media_sessions</name>
+
+  <declaration>
+
+   <field>
+    <name>session_id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>token</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+   <field>
+    <name>user_id</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+	<length>64</length>
+   </field>
+
+   <field>
+    <name>start</name>
+    <type>timestamp</type>
+    <notnull>true</notnull>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*media_songs</name>
+
+  <declaration>
+
+   <field>
+    <name>song_id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>song_name</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>200</length>
+   </field>
+
+   <field>
+    <name>song_artist</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>song_album</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>song_path</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>200</length>
+   </field>
+
+   <field>
+    <name>song_user</name>
+    <type>text</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+   <field>
+    <name>song_length</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>song_track</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>song_size</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+	   <name>song_playcount</name>
+	   <type>integer</type>
+	   <default>
+	   </default>
+	   <notnull>true</notnull>
+	   <length>4</length>
+   </field>
+
+   <field>
+	   <name>song_lastplayed</name>
+	   <type>integer</type>
+	   <default>
+	   </default>
+	   <notnull>true</notnull>
+	   <length>4</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*media_users</name>
+
+  <declaration>
+
+   <field>
+    <name>user_id</name>
+    <type>text</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <length>64</length>
+   </field>
+
+   <field>
+    <name>user_password_sha256</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+
+</database>
diff --git a/apps/media/appinfo/info.xml b/apps/media/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..044abade53f5177f37b81aaaea53190fdb492115
--- /dev/null
+++ b/apps/media/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>media</id>
+	<name>Media</name>
+	<description>Media player and server for ownCloud</description>
+	<version>0.2</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>2</require>
+</info>
\ No newline at end of file
diff --git a/apps/media/css/music.css b/apps/media/css/music.css
new file mode 100644
index 0000000000000000000000000000000000000000..59d10f74db52894bb296b2da44dab0713a103724
--- /dev/null
+++ b/apps/media/css/music.css
@@ -0,0 +1,40 @@
+#controls ul.jp-controls { padding:0; }
+#controls ul.jp-controls li { display:inline; }
+#controls ul.jp-controls li a { position:absolute; padding:.8em 1em; }
+a.jp-play, a.jp-pause { left:2.5em; }
+a.jp-pause { display:none; }
+a.jp-next { left:5em; }
+
+div.jp-progress { position:absolute; overflow:hidden; top:.5em; left:8em; width:15em; height:1.2em; padding:0; }
+div.jp-seek-bar { background:#eee; width:0; height:100%; cursor:pointer; }
+div.jp-play-bar { background:#ccc; width:0; height:100%; }
+div.jp-seeking-bg { background:url("../img/pbar-ani.gif"); }
+div.jp-current-time,div.jp-duration { position:absolute; font-size:.64em; font-style:oblique; top:1em; left:13.5em; width:22em; }
+div.jp-duration { text-align:right; }
+
+a.jp-mute,a.jp-unmute { left:24em; }
+div.jp-volume-bar { position:absolute; overflow:hidden; background:#eee; width:4em; height:0.4em; cursor:pointer; top:1.3em; left:27em; }
+div.jp-volume-bar-value { background:#ccc; width:0; height:0.4em; }
+
+#collection { padding-top:1em; position:relative; width:70em; float:left; }
+#collection li.album,#collection li.song { margin-left:3em; }
+#leftcontent img.remove { display:none; float:right; cursor:pointer; }
+#leftcontent li:hover img.remove { display:inline; }
+#collection li button { float:right; }
+#collection li,#playlist li { list-style-type:none; }
+.template { display:none; }
+
+#collection li { padding-right:10px; }
+#searchresults input.play, #searchresults input.add { float:left; height:1em; width:1em; }
+#collection tr.collapsed td.album, #collection tr.collapsed td.title { color:#ddd; }
+a.expander {  }
+tr.active { background-color:#eee; }
+tr.artist, tr.artist td {
+    border-top: 1px solid lightgrey;
+}
+tr.album td.artist {
+    padding-left: 20px;
+}
+tr.song td.artist {
+    padding-left: 40px;
+}
diff --git a/apps/media/css/player.css b/apps/media/css/player.css
new file mode 100644
index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc
--- /dev/null
+++ b/apps/media/css/player.css
@@ -0,0 +1 @@
+
diff --git a/apps/media/getID3/changelog.txt b/apps/media/getID3/changelog.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e1a3d6fcf5a9174a4b48dc16578ec8c0e4fb14d5
--- /dev/null
+++ b/apps/media/getID3/changelog.txt
@@ -0,0 +1,2435 @@
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// changelog.txt - part of getID3()                            //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+        »  denotes a major feature addition/change
+        ¤  denotes a change in the returned structure
+        !  denotes a cry for help from developers
+* Bugfix:  denotes a fixed bug
+
+Version History
+===============
+
+1.7.9: [2009-03-08] James Heinrich
+    » Added DSS (Digital Speech Standard) support
+      new file: module.audio.dss.php
+      (thanks luke*wilkinsØdtsam*com)
+    » Added MPC (Musepack) SV8 support
+      (thanks WaldoMonster)
+    ¤ some MPC [header] keys renamed to be the same between SV7/SV8
+    ¤ start aligning demos CSS styling with v2.x styles
+      new file: demos/getid3.css
+    ¤ JPEG now returns parsed IPTC tags in [iptc]
+    ¤ getid3_lib::GetDataImageSize now requires $imageinfo parameter
+    ¤ better support for Matroska files with AC3/DTS/MP3/OGG audio
+      (support still lacking for AAC)
+    ¤ standardize ID3v2 TCMP key to 'part_of_a_set' between reading
+      and writing  (thanks aaron_stormØyahoo*com)
+    ¤ added ID3v2 keys 'TCMP','TCP' to for writing iTunes-style tags
+      (thanks aaron_stormØyahoo*com)
+    ¤ back-ported PICTURE tag handling in FLAC tags
+      (thanks WaldoMonster)
+    ¤ added alternate method to get [video][frame_rate] from QuickTime
+    * added partial support for "TCMP"/"TCP" ID3v2 frames (iTunes
+      non-standard part-of-a-compilation tag)
+      (thanks aaron_stormØyahoo*com)
+    * slightly improved scanning through FLV files speed
+      (thanks franki)
+    * faster Matroska scanning by stopping at cluster chunks once
+      needed header chunks are found (much faster for large files)
+    * added workaround for broken tagging programs that miss terminating
+      null byte for numeric ID3v2.4 genres
+      (thanks yam655Øgmail*com)
+    * Bugfix: MultiByteCharString2HTML() did not escape common HTML
+      special characters like & and ?
+    * Bugfix: cleaned up some malformed HTML errors in demo.browse.php
+    * Bugfix: under Windows files >2GB might not be processed due to
+      "dir" command not finding file with double directory slashes
+    * Bugfix: "MODule (assorted sub-formats)" was falsely matching
+      some random files (e.g. JPEGs)  (thanks qwertywin)
+    * Bugfix: suppress PHP_notice on failed SWF-compressed
+      decompression failure  (thanks mkron)
+
+
+1.7.8b3: [2008-07-13] James Heinrich
+    » Experimental partial support for files > 2GB (gets filesize
+      from shell call to "dir" or "ls", parse files with PHP only
+      up to 2GB limit). See readme.txt for details on what formats
+      work properly and other limitations
+    » Initial support for Matroska. Has only been tested with a
+      limited number of sample files, please report any bugs
+    » Experimental support for PHP-RAR reading. Known buggy, disabled
+      by default, enable with care
+    ¤ getid3_lib::CastAsInt() now returns ints up to 2^31 (not 2^30)
+    ¤ Quicktime: [video] now returns [frame_rate] and [fourcc] for MP4
+      video files
+    * MP3: headerless VBR files now only have up to 10 blocks of 5000
+      frames each scanned by default and bitrate extrapolated from that
+      distribution for speed  (thanks glau*stuffØridiculousprods*com)
+    * Quicktime: support "co64" atom
+    * SWF: lower memory use when compressed SWF files processed
+      (thanks doughammondØblueyonder*co*uk)
+    * Bugfix: FLV height and width was calculated incorrectly
+      (thanks moysevichØgmail*com)
+    * Bugfix: FLV GETID3_FLV_TAG_META parsed incorrectly
+      (thanks moysevichØgmail*com)
+    * Bugfix: Quicktime: 'tkhd' matrix_v and matrix_d were switched
+      (thanks rjjmoroØhotmail*com)
+    * Bugfix: Quicktime: frame_rate was often incorrect for MP4 video
+    * Bugfix: getid3_lib::CastAsInt returned -2147483648 when passed
+      2147483648 (0x80000000)
+
+
+1.7.8b2: [2007-10-15] James Heinrich, Allan Hansen
+    * Video bitrate now calculated even if not explicitly stated in
+      file metadata, but if overall and audio bitrates are known
+    * Bugfix: 'comments_html' missing last letter in id3v2 tags.
+    * Bugfix: module objects (e.g. getid3_riff) that are instantiated
+      in other modules are explicitly disposed once no longer needed.
+    * Bugfix: some AVI files were not returning audio information
+      because "strh" chunk was not being read in
+    * Bugfix: asf [audio][<streamnumber>][dataformat] should be set
+      to "wma" but wasn't
+    * Bugfix: [mpeg][audio][bitrate_mode] should always be one of
+      ("cbr", "vbr", "abr") but wasn't for some values in
+      LAMEvbrMethodLookup()
+    * Bugfix: MP3 audio in AVI files could show "cbr" instead of
+      correct audio bitrate_mode, and audio bitrate could be slightly
+      incorrect if multiple files were scanned in a loop (scanning
+      single files produced correct values).
+    * Bugfix: remove [audio/video][bitrate] key if falsely set to zero
+    * Bugfix: PlaytimeString returned non-matching value for negative
+      playtimes (which shouldn't happen either, but now they're at
+      least shown correctly, if they happen due to other bugs)
+    * Bugfix: Several ASF header values are invalid if the broadcast
+      flag is set, getID3() now calculates these values in other
+      ways if the broadcast flag is set  (thanks fletchØpobox*com)
+    * Bugfix: lyrics3-flags-lyrics field was always false, and there
+      never was a lyrics3-flags-timestamp field present even though
+      the lyrics3-raw-IND field consisted of "10" (lyrics present,
+      timestamp not present).  (thanks i*f*schulzØweb*de)
+    * Bugfix: TAR.GZ files produce PHP errors when
+      option_gzip_parse_contents == true in module.archive.gzip.php
+      (thanks alan*harderØsun*com)
+
+
+1.7.8b1: [2007-01-08] Allan Hansen
+    » Major update to readme.txt
+    » PHP 4.2.0 required
+    » Tagwriter requires metaflac 1.1.1+ in order to write FLAC tags.
+    » Removed broken and non-fixable tagwriting module for real format.
+    ! Developers please help fix the above module:
+      http://www.getid3.org/phpBB2/viewtopic.php?t=677
+    » Avoided security issues with demo.browse.php, demo.write.php and
+      demo.mysql.php. These demos are now disabled by default and has
+      to be enabled in the source.
+    * Bugfix: id3v2 genre broken since 1.7.7.
+    » Added DTS module (module.audio.dts.php)
+    ¤ ASF/WMV files now return largest video stream dimensions in
+      [video][resolution_x] and [video][resolution_y]
+    * Bugfix: Minor issues with midi module (avoid PHP_NOTICE).
+    * Bugfix: Minor issues with lyrics3 (avoid PHP_NOTICE).
+    * Bugfix: PHP_NOTICE issues in MultiByteCharString2HTML()
+    * Bugfix: PHP_NOTICE issue  in BigEndian2Float()
+    * Bugfix: fread() zero bytes issue in real module.
+    * Bugfix: ASF module returned mime type video/x-ms-wma instead of
+      video/x-ms-wmv for certain FourCCs.
+    * Bugfix: PHP_NOTICE issues with broken ID3v2 tag/garbage.
+    * Bugfix: PNG module broken in regards to gIFg and gIFx chunks.
+    » Removed detection of short filenames 8dot3 under windows, as
+      it only worked for English versions of windows and has other
+      problems.
+    * Bugfix: Some CBR MP3 files detected as VBR with plenty of warnings.
+    * Bugfix: PHP_NOTICE issues in MP3 module.
+    * Bugfix: Quicktime returned incorrect frame rate.
+    * Bugfix: DivByZero on zero length FLV files.
+    * Bugfix: PHP_NOTICE one some FLV files.
+    * Bugfix: ID3v2 UTF-8/16 encoded frames terminated by \x00
+    * Bugfix: ID3v2 LINK frames iconv error.
+    * Bugfix: ID3v2 padding length calculated incorrectly.
+    * Bugfix: ID3v2.3 extended headers non-conformance
+    » SVG file detection.
+    » Added SVG user module (user_modules/module.graphic.svg.php).
+      Thanks to Roan Horning.
+    » PAR2 file detection (no parsing)
+    * Bugfix: Wave files being detected as MP3.
+    * Bugfix: ASF padding offset bug.
+    * Bugfix: Shorten module not working for wav files with fmt
+      chunks <> 16 bytes.
+    ¤ RIFF: Zero sized chunk invokes warning instead of error.
+    ¤ FLAC: Removed some ['raw'] keys.
+    ¤ MPC: Mime type returned: audio/x-musepack
+
+1.7.7: [2006-06-25] Allan Hansen
+    * Bugfix: AAC static bitrate cache wrong result when parsing
+      several files.
+    * Bugfix: Do not return NULL video bitrate for ASF v3.
+    * Bugfix: getid3_lib::GetImageSize() broken => JPG module broken.
+    * Bugfix: Encoder options should now be returned with correct
+      "--alt-preset n" /  "--alt-preset cbr n" when scanning more files.
+    * Bugfix: Shorten module not escapeshellarg() filenames (UNIX only).
+    * Bugfix: Filenames not escapeshellarg() for md5_data and
+      sha1_data (UNIX only).
+    * Bugfix: UNIX: head and tail called with -cNNN instead of "-c NNN".
+    » Added detection support for PDF and MS Office documents
+      (*.doc, *.xls, *.pps, etc)  (thanks zeromassmediaØgmail*com)
+    ¤ Bugfix: ID3v2 "TDRC" frame now used as "year" in comments if TYER
+      unavailable (TYER is deprecated in ID3v2.4)
+      (thanks matthiasØpanczyk*org)
+    ¤ Removed GETID3_OS_DIRSLASH, replaced with DIRECTORY_SEPARATOR
+    * Bugfix: added LAME preset guessing for presets 410,420,440,490
+      (thanks adminØlogbud*com)
+    * Bugfix: Added escapeshellarg() call in getid3_lib::hash_data
+      (thanks towbØgmx*net)
+    » TAR module no longer reads entire file into memory
+    » FLV module no longer reads entire file into memory
+    * Bugfix: added LAME preset guessing for presets 410,420,440,490
+      (thanks adminØlogbud*com)
+    * Bugfix: Added escapeshellarg() call in getid3_lib::hash_data
+      (thanks towbØgmx*net)
+    * Bugfix: Error message when padding in FLAC files were used up.
+    * Bugfix: Shorten module not working under windows.
+    ¤ Bugfix: gmmktime() instead of mktime().
+    ¤ Using gmmktime() instead of mktime() in ISO, ZIP, PNG and RIFF
+      modules to avoid E_STRICT notices with PHP5.1+.
+    * Bugfix: ['comments_html'] and ['comments'] contains different
+      value when having multiple tags (one of them ID3v1) and the
+      long field names.
+
+1.7.6: [2006-03-12] James Heinrich
+    * Rewrote getid3_lib::GetDataImageSize() to use GetImageSize()
+      instead of using code by filØrezox*com
+    * Bugfix: incorrect dimensions from disabled Quicktime tracks
+      (thanks m-1Øgmx*net)
+    * Bugfix: ['codec'] key warning in module.audio-video.asf.php
+      (thanks niel*archerØblueyonder*co*uk)
+    * Bugfix: undefined array in write.php
+      (thanks drewishØkatherinehouse*com)
+    * Bugfix: DeleteAPEtag() incorrectly failing when no tag present
+      (thanks drewishØkatherinehouse*com)
+    * Bugfix: ID3v2 writing frames with URL fields failing when URL
+      is not in ISO-8859-1  (thanks drewishØkatherinehouse*com)
+    * Bugfix: PHP notices on bad ID3v2 frames
+      (thanks cw264701Øohiou*edu)
+    * Bugfix: audio & video bitrates sometimes wrong in ASF files
+      (thanks kris_kauperØexcite*com)
+
+1.7.5: [2005-12-29] James Heinrich
+    » Added FLV (FLash Video) support -- new file:
+      module.audio-video.flv.php
+      (thanks Seth Kaufman <seth@whirl-i-gig.com> for code)
+    » Real tags can now be written (previous Real tag writing
+      code was not supposed to be in public releases, as it
+      was not complete)
+    » GETID3_HELPERAPPSDIR now autodetected under Windows
+    ¤ ASF lyrics now returned under [comments][lyrics]
+    * Bugfix: removed "--lowpass xxxxx" info from guessed
+      LAME presets when source frequency <= 32kHz
+    * Bugfix: ID3v2 extended header errors
+    * Bugfix: missing ob_end_clean() in write.id3v2.php
+      (thanks rasherØgmail*com)
+
+1.7.4: [2005-05-04] James Heinrich
+    ¤ Added ['quicktime']['hinting'] key (boolean)
+      (thanks jonØwebignition*net)
+    * Bugfix: major UTF-8 to UTF-16/ISO-8859-1 conversion
+      bug (empty string returned) when using iconv_fallback
+      (thanks chrisØfmgp*com)
+    * Bugfix: Missing 'lossless' key in RIFF-WAV
+      (thanks bobbfwedØcomcast*net)
+
+1.7.3: [2005-04-22] James Heinrich
+    » Added TAR support -- new file: module.archive.tar.php
+      (thanks Mike Mozolin <teddybearØmail*ru> for code!)
+    » Added GZIP support -- new file: module.archive.gzip.php
+      (thanks Mike Mozolin <teddybearØmail*ru> for code!)
+    * Bugfix: demo.browse.php now displays embedded images
+      internally instead of passing local filename as IMG
+      SRC (should allow demo.browse.php to correctly show
+      embedded images over a network)
+      (thanks patpowermanØhotmail*com)
+    * Bugfix: minor UTF-8 display issues in demo.browse.php
+    * Bugfix: demo.browse.php now works even if the evil
+      setting magic_quotes_gpc is turned on
+      (thanks patpowermanØhotmail*com)
+    * Bugfix: incorrect MIDI playtime for some files
+      (thanks joelØoneporpoise*com)
+    * Bugfix: 'url_source' typo in module.tag.id3v2.php
+      (thanks richardlynchØusers*sourceforge*net)
+    * Bugfix: Quicktime 'mvhd' matrix values were wrong
+      (thanks webØbobbymac*net)
+    ¤ ID3v2 now returns xx/yy for ['track'] (if
+      available), with xx in ['tracknum'] and yy in
+      ['totaltracks']. Previously ['tracknum'] was not
+      available and ['track'] had only xx.
+    Bugfixes and improvements to /demo/demo.mysql.php:
+      - remix/version parsed from tags and stored in
+        database, can be used when renaming files
+      - track number can be used for renaming files
+
+
+1.7.2: [2004-10-18] Allan Hansen
+    » Added support for WavPack v4.0+
+      (thanks ahØartemis*dk)
+    » Removed code for parsing EXE files
+      (thanks ahØartemis*dk)
+      Removed file: module.misc.exe.php
+    * Bugfix: Large ID3v2 tags inside ASF not parsed
+      properly under PHP5.
+    * Bugfix: Certain Wavpack3 files failed under PHP5 due
+      to new undocumented tmpfile() limit (same problem as
+      above).
+    * Bugfix: New iTunes crashes PHP - temp fix - no tags
+      on those files.
+    * Bugfix: ['nsv']['NSVs']['framerate_index'] might be
+      wrong  (thanks ahØartemis*dk)
+    * Bugfix: transparent color was wrong from truecolor
+      PNG  (thanks ahØartemis*dk)
+    * Bugfix: Changed MPC SV7 header size from 30 to 28,
+      this will change hash values for MPC files
+      (thanks ahØartemis*dk)
+    * Bugfix: Changed MPC SV4-6 header size from 28 to 8,
+      this will change hash values for MPC files
+      (thanks ahØartemis*dk)
+    ¤ Trim/unset wavpack encoder_options to match 2.0.0b2
+      output.
+    ¤ Commented-out unknown/unused values in NSV and ISO
+      modules  (thanks ahØartemis*dk)
+
+
+1.7.1b1: [July-26-2004] James Heinrich
+    » Added support for Apple Lossless Audio Codec
+    » Added support for RealAudio Lossless
+    » Added support for TTA v3
+    » Added support for TIFF
+      New file: /getid3/module.graphic.tiff.php
+    » Modified iconv_fallback to work with UTF-8, UTF-16, UTF-16LE,
+      UTF-16BE and ISO-8859-1 even if iconv() and/or XML support is
+      not available. This means that iconv() is no longer required
+      for most users of getID3()
+      (thanks Jeremia, khleeØbitpass*com)
+    » Added support for Monkey's Audio v3.98+ (thanks ahØartemis*dk)
+    » Included new demo showing most-basic getID3() usage
+      New file: /demos/demo.basic.php
+    * Bugfix: LAME3.94+ presets cached incorrectly if multiple files
+      are scanned in one batch and first file is LAME3.93 or earlier
+      (thanks enoyandØyahoo*com)
+    * Bugfix: Added warning if compressed ID3v2 frame decompression
+      fails. (thanks Mike Billings)
+    * Bugfix: Assorted small fixes to ensure compatability with PHP5
+    * Bugfix: ID3v1 genre "Blues" could not be written
+      (thanks Jeremia)
+    * Bugfix: ['bitrate_mode'] typo in module.audio-video.real.php
+      (thanks asukakenjiØusers*sourceforge*net)
+    * Bugfix: ['zip']['files'] is now populated with filenames even
+      if End Of Central Directory couldn't be parsed
+    * Bugfix: ['audio']['lossless'] was incorrect for FLAC
+      (thanks WaldoMonster)
+    * Bugfix: MD5 File was incorrect in directory browse mode for
+      /demo/getid3.browse.php
+    * Bugfix: PHP v5 compatability changes (float array keys, fread()
+      calls with zero data length)
+      (thanks getid3Øjsc*pp*ru)
+    * Bugfix: was dying if on compressed ID3v2 frames if
+      gzuncompress() function was unavailable
+    * Bugfix: ['vqf']['COMM'] was always empty
+    * Bugfix: MIDI playtime was missing for single-track MIDI files
+    * Bugfix: removed &#0; characters from ['comments_html']
+      (thanks p*quaedackersØplanet*nl)
+    * Bugfix: improved MIDI playtime accuracy
+      (thanks joelØoneporpoise*com)
+    * Bugfix: BMP subtypes 4 and 5 were not being identified
+    * Bugfix: frame_rate in AVI was incorrectly truncated to integer
+    * Bugfix: FLAC cuesheet track index was incorrect
+      (thanks tetsuo*yokozukaØoperamail*com)
+    ¤ ['quicktime']['display_scale'] now contains the playback scale
+      multiplier for QuickTime movies - a movie set to playback at
+      double-size will have "2" here. Other values are "1" and "0.5"
+    ¤ Added LAME preset guessing for --preset medium with v3.90.3
+      (thanks phwipØfish*co*uk)
+    ¤ Added $encoding_id3v1 to allow for ID3v1 encodings other than
+      the standard ISO-8859-1
+    ¤ Default AVI video bitrate_mode is now 'vbr'
+      (thanks eltoderØpisem*net)
+    Force getID3() to abort if Shorten files have ID3 or APE tags
+      (thanks ahØartemis*dk)
+    Editable textbox for parent directory in demo.browse.php
+      (thanks eltoderØpisem*net)
+
+
+1.7.0-hotfix [2004-03-17] Allan Hansen
+    (hotfix version released by Allan Hansen)
+    * Bugfix: PHP 4.1.x compatiblity - fgets($fp) => fgets($fp, 1024)
+    * Bugfix: Added default charset to TextEncodingNameLookup() in
+      module.tag.id3v2.php
+    Ø Removed option_no_iconv
+      iconv() support is only a requirement for WMA/WMW/ASF, and for
+      destination encodings other than ISO-8859-1 and UTF-8, iconv is
+      not needed otherwise. New 'iconv_req' in GetFileFormatArray()
+      only set for WMA/WMV/ASF. analyze() now refuses to analyse
+      WMA/ASF file if iconv is not present.
+    iconv_fallback() only dies on internal errors not missing iconv()
+
+
+1.7.0: [January-19-2004] James Heinrich
+    » Added support for RIFF/CDXA files (MPEG video in RIFF container
+      format (thanks chrisØdigitekdesign*com)
+    » Added support for TTA v2  (thanks ahØartemis*dk)
+    ¤ ID3v2 unsynchronisation scheme disabled by default because most
+      tag-reading programs cannot read unsynchronised tags. Can be
+      overridden by setting id3v2_use_unsynchronisation to true.
+      (thanks mikeØdelusion*org)
+    ¤ extention.*.php renamed to extension.*.php
+      (thanks tp62Øcornell*edu)
+    ¤ /demo/demo.check.php renamed to /demo/demo.browse.php
+    ¤ Added id3v2_paddedlength configuration parameter to WriteTags()
+      and renamed tag_language to id3v2_tag_language
+    ¤ MPEG audio layers are now represented as 1, 2 or 3 instead of
+      'I', 'II', or 'III'
+    ¤ Added [audio][wformattag] and [video][fourcc] for WAV and AVI
+    ¤ Added [audio][streams] which contains one entry for each audio
+      stream present in the file (usually only one). The data is a
+      copy of what is usually found in [audio]. If there are multiple
+      audio streams then [audio] will contain a sum of the bitrates
+      of all audio streams, and the data format of the first stream
+      (if streams are of different data types)
+    ¤ Added BruteForce mode to mp3 scanning. Disabled by default as
+      it is extremely slow and only files that are broken enough to
+      not really play will gain any benefit from this.
+    ¤ Suppress '--resample xxxxx' appended to encoder options for mp3
+      with low-quality presets for default sampling frequencies
+    ¤ Enhanced LAME preset guessing for pre-3.93 with a better lookup
+      table, --resample/--lowpass guessing (thanks phwipØfish*co*uk)
+    ¤ RIFF files with non-MP3 contents no longer have
+      [audio][encoder_options] set
+    ¤ Added [audio][encoder_options] to audio formats where possible
+      (including LiteWave, LPAC, OptimFROG, TTA)
+    ¤ Moved [quantization] and [max_prediction_order] from
+      [lpac][flags] to just [lpac]
+    ¤ WavPack flags are now parsed into [wavpack][flags]
+    * Bugfix: APEtags with ReplayGain information stored with comma-
+      seperated decimal values (ie "0,95" instead of "0.95") were
+      giving wrong peak and gain values
+    * Bugfix: Filesize > 2GB not always detected correctly
+    * Bugfix: Some ID3v2 frames had data key unset incorrectly
+      (thanks chrisØdigitekdesign*com)
+    * Bugfix: Warnings on empty-strings-only comments
+    * Bugfix: ID3v2 tag writing may have had incorrect padding length
+      if padded length less than current ID3v2 tag length and
+      merge_existing_data is false  (thanks mikeØdelusion*org)
+    * Bugfix: hash_data() for SHA1 was broken under Windows
+    * Bugfix: BigEndian2Float()/LittleEndian2Float() were broken
+    * Bugfix: LAME header calculated track peaks were incorrect for
+      LAME3.94a15 and earlier
+    * Bugfix: AVIs with VBR MP3 audio data reported incorrect bitrate
+      and bitrate_mode
+    * Bugfix: AVIs sometimes had incorrect or missing video and total
+      bitrates
+    * Bugifx: AVIs sometimes had incorrect ['avdataend'] and
+      therefore also incorrect data hashes (md5_data, sha1_data)
+    * Bugfix: ID3v1 genreid no longer returned for Unknown genre
+    * Bugfix: ID3v1 SCMPX genres were broken
+    Modified LAME header parsing to correctly process peak track
+      value for LAME3.94a16+ (thanks Gabriel)
+    md5_file() and sha1_file() now work under Windows in PHP < 4.2.0
+      and 4.3.0 respectively with helper apps
+    Default md5_data() tempfile location is now system temp directory
+      instead of same directory as file (thanks towbØtiscali*de)
+    Improved list of RIFF ['INFO'] comment key translations
+    More helpful error message when GETID3_HELPERAPPSDIR has spaces
+    /demo/demo.browse.php now autogets both MD5 and SHA1 hashes for
+      files < 50MB
+    Replaced PHP_OS comparisons with GETID3_OS_ISWINDOWS define
+      (thanks necroticØusers*sourceforge*net)
+
+
+1.7.0b5: [December-29-2003] James Heinrich
+    » Windows only: Various binary files are now required for some
+      file formats, especially for tag writing, as well as md5sum
+      (and other) calculations. These binaries are now stored in the
+      directory defined as GETID3_HELPERAPPSDIR in getid3.php
+      (default is /helperapps/ parallel to /getid3/).
+      Note: This directory must not have any spaces in the pathname.
+      All neccesary files are available as a seperate download.
+      See /helperapps/readme.txt for more information
+      New file: /helperapps/readme.txt
+    » Unified tag-writing interface for all tag formats
+      New file: /getid3/write.php
+                /getid3/write.apetag.php
+                /getid3/write.id3v1.php
+                /getid3/write.id3v2.php
+                /getid3/write.lyrics3.php
+                /getid3/write.metaflac.php
+                /getid3/write.vorbiscomment.php
+    » Added support for Shorten - requires shorten binary (head.exe
+      is also required under Windows).
+      New file: /getid3/module.audio.shorten.php
+    » Added support for RKAU
+      New file: /getid3/module.audio.rkau.php
+    » Added (minimal) support for SZIP
+      New file: /getid3/module.archive.szip.php
+    » Added MySQL caching extention  (thanks ahØartemis*dk)
+      New file: /getid3/extention.cache.mysql.php
+    » Added DBM caching extention  (thanks ahØartemis*dk)
+      New file: /getid3/extention.cache.dbm.php
+    » Added sha1_data hash option  (thanks ahØartemis*dk)
+    » Added option to allow getID3() to skip ID3v2 without parsing it
+      for faster scanning when ID3v2 data is not required. If you
+      want to enable this feature delete /getid3/module.tag.id3v2.php
+      (thanks ahØartemis*dk)
+    ¤ 8-bit WAV data now calculates MD5 checksums as normal, not
+      converting to signed data as before, so stored md5_data_source
+      in FLAC files will no longer match md5_data for the equivalent
+      decoded 8-bit WAV. A warning will be generated for 8-bit FLAC
+      files
+    ¤ Added option_no_iconv option to allow getID3() to work
+      partially without iconv() support enabled in PHP
+      (thanks ahØartemis*dk)
+    ¤ All '*_ascii' keys removed for ASF/WMA/WMV files
+    ¤ All 'ascii*' keys removed for ID3v2 tags
+    ¤ Ogg filetypes now return MIME of "application/ogg" instead of
+      the previous "application/x-ogg"
+      (thanks blakewattersØusers*sourceforge*net)
+    ¤ Force contents of ['id3v2']['comments'] to UTF-8 format from
+      whatever encoding each frame may have (text encoding can vary
+      from frame to frame in ID3v2)
+    ¤ MP3Gain information from APE tags suppressed from ['tags'] and
+      parsed into ['replay_gain']
+    ¤ ReplayGain information (all formats) changed from "Radio" and
+      "Audiophile" to "Track" and "Album" respectively
+    ¤ ['volume'] and ['max_noclip_gain'] are now available in both
+      ['replay_gain']['track'] and ['replay_gain']['album'] for all
+      formats that calculate ReplayGain.
+    ¤ ['video']['total_frames'] is available for AVIs
+    ¤ All parsed ID3v2 frame data is now in ['id3v2'][XXXX][#]
+      (previously some frame types would have numeric array keys if
+      multiple instances of that frame type were allowed and other
+      frame types would not)
+    ¤ ASF/WMA "WM/Picture" images are now parsed in the same manner
+      as ID3v2 with the image (ex JPEG) data returned in [data]
+      rather than [value]
+    * Bugfix: Optional tag processing options were being ignored (ie
+      ID3v1 still processed even if option_tag_id3v1 == false)
+      (thanks ahØartemis*dk)
+    * Bugfix: fixed MultiByteCharString2HTML() for UTF-8
+    * Bugfix: Quicktime files not always reporting video frame_rate
+    * Bugfix: False ID3v1 synch patterns in APE or Lyrics3 tags are
+      now detected and incorrect ID3v1 data not returned
+      (thanks sebastian_maresØusers*sourceforge*net for the idea)
+    * Bugfix: WMA9 Lossless now reported as lossless
+    * Bugfix: two typos in ID3v1 genre list
+    * Bugfix: MPEG-2/2.5 ABR/VBR MP3 files had doubled playtime
+    * Bugfix: MPEG-2/2.5 LayerII (ie MP2: 24/22.05/16kHz) files were
+      not detected due to incorrect frame length calculation
+    * Bugfix: MPEG LayerI files were not detected due to incorrect
+      frame length calculation (must be multiple of slot length)
+    Added alternative md5_data via system call - twice as fast. Needs
+      "getID3()-WindowsSupport" to work under Windows.
+      (thanks ahØartemis*dk)
+    ID3v2.4 compressed frames are now supported
+    php_uname() calls changed to use PHP_OS constant
+    Added SCMPX extensions to ID3v1 genres (0xF0-0xFE)
+    Obfuscated contributor email address in changelog and sourcecode
+    Added memory-saving EmbeddedLookup() function for lookup tables
+      in RIFF and ID3v2 modules (thanks ahØartemis*dk)
+    Major memory patches to many modules by using
+      $var = &$INFO_ARRAY_AT_SOME_INDEX
+      in place of large multi-dimensional array declarations.
+      Memory saved:  RIFF: ~200kB;  ID3v2: ~475kB;  ASF: ~50kB  etc.
+      (thanks ahØartemis*dk)
+
+
+1.7.0b4: [November-19-2003] James Heinrich
+   » Support added for MPC files with old SV4-SV6 structure
+   » RealVideo now properly supported with resolution, framerate, etc
+     (thanks jcsston)
+   » RealAudio files with old-style file format (v2-v4) are now
+     fully supported
+   » Support added for DolbyDigital WAV files (thanks ahØartemis*dk)
+   ¤ ['RIFF'] is now ['riff'] to conform to make all root key names
+     lowercase
+   ¤ ['OFR'] is now ['ofr'] to conform to make all root key names
+     lowercase
+   ¤ ['tags_html'] is now available as a copy of ['tags'] but
+     with all text replaced with an HTML version of all characters
+     above chr(127), translated according to whatever the encoding
+     of the source tag is, in the HTML form &#1234;
+   ¤ CopyTagsToComments() is now available in getid3_lib
+   ¤ QuicktimeVR files now return a ['video']['dataformat'] of
+     'quicktimevr' instead of 'quicktime' (thanks gtsØtsu*biz)
+   ¤ Quicktime video files with DivX, Xvid, 3ivx or MPEG4 video
+     streams now return those names as ['video']['dataformat']
+   ¤ MPEG video files are now identified with ['video']['codec'] set
+     to either 'MPEG-1' or 'MPEG-2' (rather than just 'MPEG'). If you
+     see a file wrongly identified, please report it!
+     (thanks fccHandler)
+   ¤ All bitrate values in ['mpeg']['audio'] is now reported in bps
+     rather than kbps (ie 128000 instead of 128) for consistancy
+   ¤ AVIs with MP2 audio now report ['audio']['dataformat'] as 'mp2'
+     rather than 'wav'  (thanks metalbrainØnetian*com)
+   ¤ Added ['md5_data_source'] for OptimFROG
+   ¤ AC3 in RIFF-WAV now identified with ['audio']['dataformat']
+     returning 'ac3'
+   ¤ WavPack ['extra_bc'] now returned as integer
+   ¤ WavPack ['extras'] now returned as 3-element array of integers
+   ¤ MP3 ['audio']['encoder options'] now returns 'VBR' or 'CBR' only
+     (no bitrate) if no LAME preset is used, or 'VBR q??' where ?? is
+     a number 0-100 for Fraunhofer-encoded VBR MP3s
+   * Bugfix: VBR MP3s could have incorrect bitrate reported
+   * Bugfix: Quicktime files with MP4 audio were not returning
+     ['video']['dataformat'] (thanks robØmassive-interactive*nl)
+   * Bugfix: strpad vs str_pad typo in module.riff.php
+     (thanks nicojunØusers*sourceforge*net)
+   * Bugfix: ReplayGain information was often wrong for MPC files
+   * Bugfix: MD5 and other post-TAIL chunks were not being processed
+     in module.audio.optimfrog.php
+   * Bugfix: Undefined variable in table_var_dump() in demo/check.php
+   * Bugfix: QuickTime files now only return information in [audio]
+     or [video] if those exist in the file
+   * Bugfix: WavPack no longer tries to read entire compressed data
+     chunk
+   * Bugfix: Properly handle VBR MP3s with "Info" (rather than
+     "Xing") header frame. foobar2000 adds this to MP3 files when
+     "Fix MP3 Header" function is used (thanks ahØartemis*dk)
+   * Bugfix: Fraunhofer VBRI headers for MP3s were assuming 2-byte
+     entries for TOC rather than using stride, and were ignoring the
+     scaling value. (thanks sebastianØmaresweb*net)
+   Several QuickTime atoms have been added to an exclusion list
+     because they have been observed, but I have no idea what they
+     are supposed to do so I can't add real support for them, but
+     they should not generate warnings (robØmassive-interactive*nl)
+   Old MPC encoder (before v1.06) was return as v0.00, now returned
+     as 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05'
+     (thanks ahØartemis*dk)
+   Added check for magic_quotes_runtime and code to disable it if
+     neccesary (thanks stefan*kischkelØt-online*de)
+   Added 3ivx fourCCs to module.audio-video.quicktime.php
+   MP3 and AC3 streams are now parsed when contained inside RIFF-WAV
+     or RIFF-AVI container formats
+   Better detection of named presets in LAME 3.93/3.94
+
+
+1.7.0b3: [October-17-2003] James Heinrich
+   » AC-3 (aka Dolby Digital) is now supported.
+     New file: /getid3/module.audio.ac3.php
+   * Bugfix: ID3v2-writing function Unsynchronise() was broken, which
+     made ID3v2 tag containing binary data (typically pictures) get
+     corrupted. (thanks t*coombesØbendigo*vic*gov*au,
+     i*kuehlbornØndh*net, mikeØdelusion*org, mikeØftl*com)
+   * Bugfix: Zip comments now returned as array instead of string,
+     as they're supposed to be.
+   * Bugfix: Quicktime/MP4 files may have reported extremely low
+     bitrates (thanks spunkØdasspunk*com)
+   Improved double-ID3v1 check to prevent false detection when string
+     "TAG" is present in APE or Lyrics3
+   Fixed /demo/simple.php
+   Fixed /demo/joinmp3.php
+   Fixed /demo/mimeonly.php
+   Fixed /demo/write.php
+
+
+1.7.0b2: [October-15-2003] James Heinrich
+   » TTA Lossless Audio Compressor format now supported.
+     (http://tta.iszf.irk.ru)
+     New file: /getid3/module.graphic.tta.php
+   » PhotoCD (PCD) format now supported. Image data for the three
+     lowest resolutions (192x128, 384x256, 768x512) can be optionally
+     extracted.
+     New file: /getid3/module.graphic.pcd.php
+   ¤ RIFF-MP3 files now should return the same ['md5_data'] as the
+     identical MP3 file outside the RIFF container
+   ¤ Name of LAME preset used (if available, needs LAME v3.90+)
+     returned in ['mpeg']['audio']['LAME']['preset_used'] and also as
+     part of ['audio']['encoder_options']
+   ¤ VQF module now sets ['audio']['encoder_options'] to i.e. CBR96
+   ¤ MP3 module now sets ['audio']['encoder_options'] on CBR files
+     and all LAME-encoded files
+   ¤ MPC module now sets ['audio']['encoder_options']
+   ¤ Monkey module now sets ['audio']['encoder_options']
+   ¤ AAC module now sets ['audio']['encoder_options'] to profile name
+   ¤ ASF module now sets ['audio']['encoder_options']
+   ¤ Ogg module adds ['audio']['encoder_options'] -b 128 on
+     Ogg Vorbis 1.0+ ABR files
+   ¤ Ogg module adds ['audio']['encoder_options'] -q N   on
+     Ogg Vorbis 1.0+ VBR files 44k/48k sample rate/stereo files only.
+   ¤ Ogg module ['audio']['encoder_options'] "Nominal birate: 80k" to
+     other Ogg Vorbis files.
+   ¤ ID3v2 track number now returned as string (with leading zeros,
+     if present in data) rather than integer (thanks Plamen)
+   ¤ ASF module returns ['asf']['comments']['encoding_time_unix'] if
+     available (from WM/EncodingTime)
+   ¤ Fixed /demo/mysql.php and added some new features:
+     - encoder options
+     - ID3v2 "Encoded By"
+     - non-empty comments
+     - total entries in database summary (totals & averages)
+     - database version update
+   * Bugfix: 'UNICODE' iconv() charset changed to 'UTF-16LE' or
+     'UTF-16BE' as appropriate
+   * Bugfix: iconv_fallback() function created in case iconv() fails
+   * Bugfix: fixed MD5 calls in demo/check.php
+   * Bugfix: reenabled detection of APE + Lyrics3 tags in same file
+   * Bugfix: ASF module now returns ID3v1 genre as string instead of
+     number - patch from Eugene Toder.
+   * Bugfix: ASF module now reads non-standard field names,
+     i.e. "date" as well as WM/Year - patch from Eugene Toder.
+   * Bugfix: ASF module now returns genre as-is if it is not a
+     standard ID3v1 genre (thanks wonderboy)
+   * Bugfix: Eliminated false-synch problem in MP3 module
+   * Bugfix: Fixed missing root ['bitrate'] for most formats
+   * Bugfix: ['audio']['compression_ration'] missing for MPC
+     (thanks WaldoMonster)
+   * Bugfix: NSV module died in 1.7.0b1
+   * Bugfix: ASF module died in 1.7.0b1 when WM/Picture preset
+   * Bugfix: ASF tracknumber incorrect when specified by WM/Track
+     rather than WM/TrackNumber (thanks jgriffiniiiØhotmail*com)
+   * Bugfix: MPEG audio+video playtime should now be pretty accurate
+     (ie within 0.1% variation at most)
+     (thanks mgrimmØhealthtvchannel*org)
+   * Bugfix: ID3v2 not being copied to ['tags'] in some cases
+   * Bugfix: LAME CBR files with Info tag were being incorrectly
+     flagged as VBR (thanks Jojo)
+   * Bugfix: LAME tag not being detected for LAME 3.90 (original)
+   Changed regex pattern match for MP3 to include 3rd byte for more
+     reliable/accurate pattern matching
+   Added duplicate-ID3v1 tag checking (two ID3v1 tags, one after the
+     other) that has been known to occur with iTunes
+     (thanks towbØtiscali*de)
+   Added instructions for enabling iconv() support under Windows
+   Removed some unneccesary debugging code
+   Suppressed duplicate PHP warnings for missing include files
+   Included some missing dependencies in various files
+   /demo/audioinfo.class.php now copies ['audio']['encoder_options']
+
+
+1.7.0b1: [2003-09-28] Allan Hansen
+   This beta version was not made by James Heinrich. It was made by
+   Allan Hansen <ahØartemis*dk> - please send bug reports on this
+   beta directly to me.
+
+   James Heinrich will release 1.7.0 final, but it may take some time
+   to work out the bugs from the major rewrite.
+
+   This version could be called getID3lite. It makes a lot of checks
+   optional and makes it easy to remove support for undesired formats
+
+   It also is more library-like. Older versions of getID3() declared
+   an incredible amount of global scope functions and defined several
+   constants. 1.7.0beta1 still declares constants, but they are all
+   prepended by GETID3_. It declares no global scope functions - they
+   are all wrapped into classes.
+
+   » Made getID3() depend on iconv library: compile PHP --with-iconv
+   » Created new directory structure
+       Moved all demos to demos/
+       Moved all getID3() files to getid3/
+       Renamed most files to module.something
+       Changed header in all module.something to explain what they do
+       Simply remove all modules you don't need
+       Wrapped all modules into classes
+   * Bugfix: Implemented misc patches from Eugene Toder
+   * Bugfix: Implemented misc patches from "six"
+   ¤ Added root key 'encoding'
+   ¤ Added prefix GETID3_ to all defined constants.
+   ¤ Wrapped getid3.php into getid3 class
+   ¤ Wrapped getid3.functions.php into getid3_lib class
+       Removed unused functions
+       Moved several functions away from getid3.functions.php and
+         into the files where they are actually used.
+       Renamed getid3.functions.php to getid3.lib.php
+       Moved getid3.rgad.php functions into getid3_lib
+       Moved getid3.getimagesize.php funcitons ingo getid3_lib
+   ¤ Moved getid3.ogginfo.php into ogg module
+   ¤ Combined GetTagOnly() and GetAllFileInfo() in method analyze
+   ¤ Removed redundant and unuseful root keys
+       'file_modified_time' == filemtime($filename)
+       'md5_file' == md5_file($filename)
+       'exist' == file_exists($filename)
+   ¤ Changed root key ['tags'] from array of string to array of array
+     of comments.
+   Simplified code for detecting base path.
+   Removed ob_ from InitializeFilepointerArray(). That was really a
+     ugly HACK to get output from fopen. If user want the reason,
+     he should open the file himself!
+   Checking for APE tags before lyrics3 - makes Lyrics3 not depend
+     on APE tag. It seems to work on my test file.
+   Changed ['error'] and ['warning'] in multiple files to append to
+     array instead of appending to string. That simplified code in
+     getid3.php too.
+   Simplified clean-up procedure: simply remove all empty root keys
+   Setting tags in individual modules instead of main getid3.php
+   Made Bonk and ASF modules non-dependent on id3 modules - id3
+     optional.
+   Rewrote HandleAllTags() - simplified and convert comments to
+     desired encoding.
+   Replaced all calls to RoughTranslateUnicodeToASCII() in ASF module
+     with a TrimConvert() method. This uses iconv() for conversion.
+     It also converts from UNICODE instead of UTF-16BE, as the spec
+     says it should.
+   Replaced all calls to RoughTranslateUnicodeToASCII() in id3v2
+     module with iconv(). id3v2 module also reads
+     $ThisFileInfo['encoding'] and converts all comments to this
+     format. All other formats just add their comments in their
+     native charset, but every comment field in id3v2 can have a
+     different encoding, so this is needed.
+   Did same thing as above with ISO module. However - it does not
+     work. I need to find out how to specify big-endian unicode !=
+     UNICODING encoding name given to iconv().
+   Built-in assume mp3 format in getid3.php
+   Temporarily nuked root key ['comments'] and CopyCommentsToRoot()
+   Updated demo/audioinfo.class.php
+   Updated demo/check.php - some thing don't work!
+   Other demos are out of order!
+
+
+1.6.5: [October-06-2003] James Heinrich
+   » Added support for LiteWave (thanks supportØclearjump*com)
+   Ø Split out speedup info from ['OFR']['OFR']['compression'] into
+     ['OFR']['OFR']['speedup']
+   Ø If EXIF functions for JPEG not available, now warning not error
+   Ø ID3v2 track number now returned as string (with leading zeros,
+     if present in data) rather than integer (thanks Plamen)
+   * Bugfix: now correctly parses cbSize element of WAVEFORMATEX
+     structure (thanks supportØclearjump*com)
+   * Bugfix: ASF module now reads non-standard field names,
+     i.e. "date" as well as WM/Year - patch from Eugene Toder.
+   * Bugfix: ASF module now returns genre as-is if it is not a
+     standard ID3v1 genre (thanks wonderboy)
+   * Bugfix: ['audio']['compression_ration'] missing for MPC
+     (thanks WaldoMonster)
+   Prevent infinite loop in MP3 histogram if framelength == 0
+   Added wFormatTag values 0x00FF and 0x2001 - 0x2005
+     (thanks steveØheadbands*com)
+   Added "twos" and "sowt" FourCCs for Mac AIFC
+
+
+1.6.4: [June-30-2003] James Heinrich
+   » Added support for free-format MP3s
+     (thanks Sebastian Mares for the idea)
+   » Compressed (Flash 6+) SWF files are now handled properly
+     (thanks alan*cheungØalumni*ust*hk)
+   » Added DeleteLyrics3() to getid3.lyrics3.php
+   » Added FixID3v1Padding() to getid3.putid3.php
+   » Added new simple MP3-splicing sample file
+     (thanks tommybobØmailandnews*com for the idea)
+     New file: getid3.demo.joinmp3.php
+   » Moved all contents of getid3.putid3.php into either
+     getid3.id3v1.php or getid3.id3v2.php or getid3.functions.php as
+     appropriate
+     Removed file: getid3.putid3.php
+   ¤ ['error'] and ['warning'] keys now return as arrays, not strings
+   ¤ New root key for all files: ['file_modified_time'] (UNIX time)
+   ¤ getid3.demo.scandir.php renamed to getid3.demo.mysql.php
+   ¤ New demo file returns the MIME type only for a single file
+     (thanks adminØe-tones*co*uk for the idea)
+     New file: getid3.demo.mimeonly.php
+   ¤ Added check for valid ID3v1 padding (strings should be padded
+     with null characters but some taggers incorrectly use spaces).
+     A warning will be generated if padding is invalid. New boolean
+     key ['id3v1']['padding_valid'] indicates padding validity.
+   ¤ CleanUpGetAllMP3info() removes more useless root keys for
+     unknown-format files
+   ¤ Extended LAME information in ['mpeg']['audio']['LAME'] is now
+     only returned for LAME v3.90+
+   ¤ LAME-encoded MP3s now return
+     ['mpeg']['audio']['LAME']['long_version'] as well as
+     ['mpeg']['audio']['LAME']['short_version'] - these are identical
+     in LAME v3.90+ but older versions will report longer more
+     detailed version information if available
+   ¤ New Lyrics3 values: ['lyrics3']['raw']['offset_start'] and
+     ['lyrics3']['raw']['offset_end']
+   ¤ New optional parameter on getAPEtagFilepointer() to scan from a
+     defined offset rather than end-of-file to allow scanning of APE
+     tags before Lyrics3 tags
+   ¤ ['tag_offset_start'] and ['tag_offset_end'] are now present in
+     ['ape'], ['lyrics3'], ['id3v1'] and ['id3v2']
+   ¤ Numerous changes to the returned structure and content for La
+     files, including parsing the seektable (if applicable) and
+     parsing RIFF data occuring after the end of the compressed audio
+     data (notably RIFF comments)
+     (thanks mikeØbevin*de)
+   ¤ getSWFHeaderFilepointer() now has optional 3rd parameter
+     $ReturnAllTagData (default == false) which if set to true will
+     return data on all tags in ['swf']['tags']
+   ¤ ['swf']['bgcolor'] now returns the 6-character string
+     representing the background color in HTML hex color format
+     (thanks ubergeekØubergeek*tv)
+   ¤ ['swf']['header']['frame_delay'] is no longer returned
+   ¤ getQuicktimeHeaderFilepointer() now has two additional optional
+     parameters: $ReturnAtomData (default == true) and
+     $ParseAllPossibleAtoms (default == false). Setting
+     $ReturnAtomData to false will reduce the size of the returned
+     data array by unsetting ['quicktime']['moov'] before returning.
+     Leaving $ParseAllPossibleAtoms as false now suppresses parsing
+     of several atom types that contain very large tables of data
+     that are not typically useful. Atom type suppressed are:
+     stts, stss, stsc, stsz, and stco
+     (thanks ubergeekØubergeek*tv)
+   ¤ ['fileformat'] no longer set to 'id3' if ID3v1 or ID3v2 tag
+     detected but no other data format recognized
+   * Bugfix: La files now return the correct values for
+     ['avdataoffset'] and ['avdataend'] and therefore the correct
+     values for ['md5_data'] - note that ['md5_data'] values will not
+     match values from previous versions of getID3() - the previous
+     versions were incorrect
+     (thanks mikeØbevin*de)
+   * Bugfix: A temporary file was being created in the web server's
+     root directory (not DocumentRoot) each time ['md5_data'] was
+     calculated, and not removed due to lack of permissions. Temp
+     file is now created (as it was supposed to be) in the directory
+     of the file being examined, or the system temp directory, and
+     properly removed when done.
+   * Bugfix: Several incorrect values were being returned inside
+     ['mpeg']['audio']['LAME'] (thanks bouvigneØmp3-tech*org)
+   * Bugfix: SWF frame rates values were usually incorrect.
+     (thanks alan.cheungØalumni*ust*hk and ubergeekØubergeek*tv)
+   * Bugfix: ID3v2.2 files always flagged 4 bytes of invalid padding
+     (thanks marcaØmac*com)
+   * Bugfix: Lyrics3 without ID3v1 was not working properly
+   * Bugfix: Lyrics3, APE & ID3v1 can all now exist in the same file.
+     A warning is issued if APE comes after Lyrics3 (because Lyrics3-
+     aware taggers probably are not APE-aware and therefore won't be
+     able to find the Lyrics3 tag)  (thanks mp3gainØhotmail*com)
+   * Bugfix: WriteAPEtag() now writes the APE tag before any Lyrics3
+     tags (if present) and removes any incorrect ones that are after
+     existing Lyrics3 tags  (thanks mp3gainØhotmail*com)
+   * Bugfix: RIFF-WAVE file with incorrect NumberOfSamples values in
+     the 'fact' chunk no longer cause incorrect playtime calculation
+     (thanks stprasadØindusnetworks*com)
+   * Bugfix: getid3.demo.simple.php had undefined variables if the
+     file needed to be deep-scanned with assumeFormat
+   * Bugfix: fixed previously-incorrect ['avdataend'] values for APE
+     and Lyrics3 tags in some cases, which in some cases means that
+     ['md5_data'] is different than previously (now correct)
+   Much-improved detection of AAC-ADTS, which also means MP3
+     format detection should now be nearly twice as fast
+   Truncated AVIs and WAVs are now reported
+   Number of new features and bugfixes in getid3.demo.mysql.php
+   Quicktime 'meta' atoms now parsed, so Quicktime MP4 files can now
+     return artist, title, album, etc  (thanks spunkØdasspunk*com)
+   Consolidated all comments processing functions (processing the
+     ['comments'] and ['tags'] keys) into HandleAllTags() which now
+     also checks to ensure that APE tags are really better than ID3v2
+     before using them in ['comments']
+   Known issue with Meracl ID3 Tag Writer v1.3.4 truncating last byte
+     of MP3 file when appending new ID3v1 tag now specifically noted
+     (rather than generic Probably Truncated File message)
+   getid3.demo.mysql.php now stores last-modified time for each file
+   getid3.demo.mysql.php is now case-sensitive for filenames
+   getid3.demo.mysql.php can generate M3U playlists of any of the
+     groups of files it can select (duplicate filenames, tag types,
+     etc.)
+   getid3.demo.mysql.php can now find mismatched tag contents and
+     filenames
+   getid3.demo.check.php now shows total number of errors & warnings
+   GetFileFormatArray() now matches actual patterns for MP3 files
+     based on the first two bytes of the file, rather than just the
+     first one
+   Simplified DeleteAPEtag() and made it work properly with Lyrics3
+
+
+1.6.3: [May-17-2003] James Heinrich
+   » Added support for Bonk  (thanks ahØartemis*dk)
+     New file: getid3.bonk.php
+   » Added support for AVR  (thanks ahØartemis*dk)
+     New file: getid3.avr.php
+   ¤ Contents of getid3.id3.php moved to getid3.id3v1.php
+     Removed file: getid3.id3.php
+   ¤ Contents of getid3.frames.php moved to getid3.id3v2.php
+     Removed file: getid3.frames.php
+   ¤ Returned data structure documentation improved and updated and
+     now stored in getid3.structure.txt rather than getid3.readme.txt
+     New file: getid3.structure.txt
+   ¤ Now including the GNU General Public License in the distribution
+     as getid3.license.txt
+     New file: getid3.license.txt
+   ¤ Added new, optional, parameter to WriteAPEtag() (and also
+     GenerateAPEtag()) which must be set to TRUE if the values you
+     are passing are already UTF8-encoded, otherwise all data is
+     encoded to UTF8 by default. For all ASCII/ANSI data this value
+     should be left at the defaul value of FALSE.
+   ¤ Added third, optional, parameter to getID3v2Filepointer() -
+     $StartingOffset (default == 0) which can parse an ID3v2 tag
+     in a file at a position other than the start-of-file.
+   ¤ ['video']['pixel_aspect_ratio'] now returned when known
+   ¤ AVI files with WMA audio now return ['audio']['dataformat']
+     of 'wma' rather than 'wav'
+   ¤ ASF-WMA files now return the artist value from WM/AlbumArtist
+     in ['comments']['artist']  (thanks msibbaldØsaebauld*com)
+   ¤ ASF-WMA files now return the 'author' value from
+     ['asf']['content_description'] in ['comments']['artist']
+     instead of ['comments']['author']
+   ¤ ASF-WMA files now return the 'description' value from
+     ['asf']['content_description'] in ['comments']['comment']
+     instead of ['comments']['description']
+   * Bugfix: APE tag writing with multiple values for a tag (more
+     than one ARTIST for example) was not being correctly written
+     (thanks ahØartemis*dk)
+   * Bugfix: CreateDeepArray() was returning an empty-string key as
+     the top-level returned value - ['iso']['files'] now directly
+     contains the file listing without an empty array in between.
+   * Bugfix: ID3v2 genreid was not being returned in some cases.
+   * Bugfix: APEv1 tags would generate error messages
+   * Bugfix: APE tags would sometimes show phantom second entry for
+     each item (title, artist, etc) with no data
+   * Bugfix: APE tag writing was not UTF8-encoding the data -
+     non-ASCII characters (above chr(127)) were being incorrectly
+     stored  (thanks ahØartemis*dk)
+   * Bugfix: getid3.demo.scandir.php had undefined function error
+   * Bugfix: getid3.demo.scandir.php would not display list of files
+     with no tags
+   Added link to getid3.demo.check.php from list of specific-tags
+     files in getid3.demo.scandir.php
+
+
+1.6.2: [May-04-2003] James Heinrich
+   » New official mirror site for getID3() - http://www.getid3.org
+   » Added basic support for SWF (Flash)  (thanks n8n8Øyahoo*com)
+     New file: getid3.swf.php
+   » Added experimental support for parsing the audio portion of
+     MPEG-video files. I don't have any actual documentation for
+     this, so this part is experimental and not guaranteed accurate,
+     but it seems to be working OK as far as I have been able to test
+     it. Bug reports (or even better - documentation!) are welcome at
+     info@getid3.org
+   » Added new simple directory-scanning sample file
+     New file: getid3.demo.simple.php
+   » getid3.demo.write.php now writes APE tags as well.
+   ¤ Renamed getid3.write.php to getid3.demo.write.php
+   ¤ Renamed audioinfo.class.php to getid3.demo.audioinfo.class.php
+   ¤ getid3.php now automatically includes the getid3.functions.php
+     function library file, no need to include it seperately.
+   ¤ getLyrics3Filepointer() has been changed to be consistant with
+     all the other similar function structures - the parameters have
+     changed. The old function has been renamed to getLyrics3Data()
+   ¤ Added DeleteAPEtag() function to getid3.ape.php
+   ¤ HandleID3v1Tag() now only handles ID3v1. Lyrics3 processing is
+     now done by HandleLyrics3Tag()
+   ¤ If BitrateHistogram is enabled in getOnlyMPEGaudioInfo() it now
+     also returns ['mpeg']['audio']['version_distribution'] showing
+     the number of frames of each MPEG version (1, 2 or 2.5) - all
+     frames *should* be of the same MPEG version
+   ¤ getID3v1Filepointer() always returns TRUE now, even if it didn't
+     find a valid ID3v1 tag
+   ¤ getOnlyMPEGaudioInfo() now looks for MPEG sync in the first 128k
+     bytes rather than the first 64k bytes
+   ¤ Added dummy function GetAllMP3info() to generate warning not to
+     use that deprecated function.
+   ¤ ['video']['codec'] is now 'MPEG' for all MPEG video files (this
+     will change to 'MPEG-1' or 'MPEG-2' as soon as I figure out how
+     to determine that)  (thanks jigalØspill*nl)
+   ¤ ['mpeg']['audio']['LAME']['mp3_gain'] renamed to
+     ['mpeg']['audio']['LAME']['mp3_gain_db'] (gain in dB)
+   ¤ Added ['mpeg']['audio']['LAME']['mp3_gain_factor'] (gain as a
+     multiplication factor)
+   ¤ Added support for Preset and Surround Info bytes from LAME VBR
+     tag (http://gabriel.mp3-tech.org/mp3infotag.html)
+   * Bugfix: APE tag writing would put the string 'Array' for all
+     values rather than the actual data  (thanks ahØartemis*dk)
+   * Bugfix: Warning now generated for VBR MPEG-video files because
+     getID3() cannot determine average bitrate. If you know of
+     documentation that would tell me how to do this, please email
+     info@getid3.org
+   * Bugfix: Replay Gain values from Vorbis comments are now
+     returned in ['replay_gain'] (and not in ['comments'])
+     (thanks ahØartemis*dk)
+   * Bugfix: Replay Gain values from APE comments are now correctly
+     returned in ['replay_gain']  (thanks ahØartemis*dk)
+   * Bugfix: getid3.demo.check.php is now case-insensitive when
+     assuming a format for a corrupted file if standard detection
+     does not identify the file type.
+   * Bugfix: RIFF comments were overwriting/suppressing ID3 comments
+     for RIFF-MP3 files  (thanks wmØwofuer*com)
+   * Bugfix: RIFF-MP3 files with 'RMP3' chunks instead of 'WAVE' were
+     not being correctly identified.
+   * Bugfix: ID3v2 padding shorter than the length of an ID3v2 frame
+     header was not correctly detected
+   * Bugfix: getid3.demo.check.php now does in-depth scanning for MP2
+     and MP1 files the same as for MP3 files based on file extension
+     if a MPEG-audio structure isn't found immediately at the start
+     of the file
+   * Bugfix: removed condition where RIFF-WAV was being scanned for
+     MPEG-audio signature when it shouldn't be present (non-MP3 WAV)
+   * Bugfix: ASF files were not always showing correct audio datatype
+   * Bugfix: array_merge_clobber() and array_merge_noclobber() were
+     not being conditionally defined in getid3.functions.php
+     (thanks rich.martinØreden-anders*com)
+   * Bugfix: stream_numbers was not being correctly returned in
+     bitrate_mutual_exclusion_object chunks of ASF files
+   * Bugfix: Added support for 24kHz and 12kHz audio in ASF files
+   * Bugfix: Removed possible undefined offset error in MP3s where
+     cannot find synch before end of file
+   * Bugfix: Removed potential out-of-memory crash situation when
+     parsing Real files with chunks larger than the available memory
+     (thanks jigalØspill*nl)
+   * Bugfix: ID3v1 was incorrectly taking precedence over ID3v2 in
+     the ['comments'] array (thanks lionelflØwanadoo*fr)
+   * Bugfix: No longer calculates overall bitrate and playtime for
+     VBR MPEG video files based on the audio bitrate.
+   * Bugfix: AssumeFormat was not working properly
+   Added summary footer line to getid3.demo.check.php
+   Added '.mpeg' to the list of assume-format-from-filenames list in
+     getid3.demo.check.php
+   MPEG-video files now more reliably detected
+   A number of additional features have been added to
+     getid3.demo.scandir.php
+   Added many RIFF-AVI audio types and fourcc video types to the
+     lookup functions in getid3.riff.php
+   Now identifes files with Lyrics3 v1 tags that are of incorrect
+     length (v1 Lyrics3 is supposed to be 5100 bytes long, but
+     [unknown program] writes variable-length tags (which is illegal
+     for Lyrics3 v1)). getID3() now correctly parses these tags and
+     issues a warning.
+   Split GetFileFormat() to GetFileFormat() and GetFileFormatArray()
+   HTML colors in getid3.demo.check.php are now defined as constant
+     variables at the top of the file (if you want to change them)
+   Added support for OptimFROG v4.50x (non-alpha) (new header fields)
+     (thanks floringhidoØyahoo*com)
+   Added support for Lossless Audio v0.4 (thanks mikeØbevin*de)
+
+
+1.6.1: [March-03-2003] James Heinrich
+   » Added support for writing APE v2.
+     WriteAPEtag() in getid3.ape.php
+     NOTE: APE v1 writing support will *not* be added to future
+     versions of getID3()
+     (thanks ahØartemis*dk and adamØphysco*com for the idea)
+   » Added support for AIFF (Audio Interchange File Format) including
+     AIFF, AIFC and 8SVX  (thanks ahØartemis*dk for the idea)
+     Removed file: getid3.aiff.php
+   » Added support for OptimFROG (v4.50a and v4.2x)
+     (thanks ahØartemis*dk for the idea)
+     New file: getid3.optimfrog.php
+   » Added support for WavPack  (thanks ahØartemis*dk for the idea)
+   » Added support for LPAC  (thanks ahØartemis*dk for the idea)
+   » Added support for NeXT/Sun .au format
+     New file: getid3.au.php
+   » Added support for Creative SoundBlaster VOC format
+     New file: getid3.voc.php
+   » Added support for the BWF (Broadcast Wave File) RIFF chunks
+     "bext" and "MEXT"  (thanks Ryan and njhØsurgeradio*co*uk)
+   » Added support for the CART (Broadcast Wave File) RIFF chunks
+     (thanks Ryan)
+   » Added getid3.demo.scandir.php - a sample recursive scanning demo
+     that scans every file in a given directory, and all sub-
+     directories, and stores the resulting data in MySQL database,
+     and then displays a list of duplicate files based on md5_data
+   ¤ ['md5_data_source'] now contains the MD5 value for the original
+     uncompressed data for formats that store that information
+     (currently only FLAC v0.5+). ['md5_data'] (if chosen to be
+     calculated) will contain the calculated MD5 value for the
+     compressed file. To check if 2 files are identical in every way,
+     including all comments: compare ['md5_file']. To check if two
+     files were compressed from the same source file: compare
+     ['md5_data_source']. To check if the compressed audio/video data
+     of two files is identical, even if comments or even the
+     container file format is different (MP3 in RIFF container,
+     FLAC in Ogg container, etc): compare ['md5_data'].
+   ¤ ['md5_data'] for 8-bit WAV files is now calculated based on a
+     converted version of the data from unsigned to signed (MSB
+     inverted) to match the MD5 value calculated by FLAC
+   ¤ New optional parameter added to GetAllFileInfo() -
+     $MD5dataIfMD5SourceKnown (default: false). If false the md5_data
+     value will NOT be calculated for files (such as FLAC) that have
+     ['md5_data_source'] set, even if $MD5data == true.
+     (thanks ahØartemis*dk)
+   ¤ getid3.check.php renamed to getid3.demo.check.php
+   ¤ Added GetTagOnly() function to getid3.php - similar to
+     GetAllFileInfo() except only takes a filename as a parameter and
+     only returns ID3v2, APE, Lyrics3 and ID3v1 tag information - no
+     attempt is made to parse the data contents of the file at all.
+     (thanks Phil for the idea)
+   ¤ Added ['audio']['lossless'] and ['video']['lossless'] for all
+     formats (when known). Both are boolean values - true means the
+     data is lossless-compressed, false means the data is lossy-
+     compressed.
+   ¤ Added ['audio']['compression_ratio'] and/or
+     ['video']['compression_ratio'] for all formats. Returns a number
+     (usually) less than 1, where 1 represents no compression and 0.5
+     represents a compressed file half the size of the original file
+   ¤ Added ['video']['bits_per_sample'] to all video formats (when
+     known)
+   ¤ Added ['video']['frame_rate'] to all video formats (when known)
+   ¤ ['fileformat'] set to 'mp1' or 'mp2' instead of 'mp3' when
+     ['audio']['dataformat'] is one of those  (thanks ahØartemis*dk)
+   ¤ Added 4th parameter to md5_data(), $invertsign, which will invert
+     the MSB of each byte before MD5'ing. This is needed for 8-bit
+     WAV files because FLAC calculates the stored MD5 value on
+     signed data rather than the original byte values. ['md5_data']
+     of an 8-bit WAV will now match the ['md5_data_source'] value
+     (thanks lichvarmØphoenix*inf*upol*cz)
+   ¤ ['ape']['items']['data'] and ['ape']['items']['data_ascii'] now
+     contains an array of values, if the tag contains UTF-8 text (as
+     opposed to binary data)
+   ¤ ['mpeg']['audio']['bitratemode'] renamed to
+     ['mpeg']['audio']['bitrate_mode']
+   * Bugfix: Removed potential bug that could replace all MP3 file
+     contents with only the new ID3v2 tag in getid3.putid3.php
+   * Bugfix: md5_data values calculated for RIFF (WAV, AVI) files
+     were incorrect  (thanks ahØartemis*dk)
+   * Bugfix: MP3 data in an MP4 wrapper fileformat could not identify
+     bitrate  (thanks ahØartemis*dk)
+   * Bugfix: ['audio'] and/or ['video'] keys would sometimes get
+     removed even if not empty
+   * Bugfix: Prevented creation of null entries in
+     ['RIFF']['WAVE']['INFO'] if a comment entry was not present
+   * Bugfix: Potential infinite-loop condition in getid3.ogg.php
+     (thanks afshin.behniaØsbcglobal*net)
+   * Bugfix: Ogg files with illegal ID3v1 (and/or APE or Lyrics3)
+     tags were not finding the last Ogg page
+     (thanks afshin.behniaØsbcglobal*net)
+   * Bugfix: replay-gain values not properly set from LAME tag
+   * Bugfix: RIFF-MP3 had incorrect md5_data
+   * Bugfix: the LAME DLL CBR problem of not re-writing the LAME
+     frame at the beginning of the data is now detected for MP3s
+     with ID3v2 tags as well
+   * Bugfix: APE tags with multiple values (ie multiple entries in
+     the "artist" tag) are now shown properly in ['ape']['items']
+   * Bugfix: fixed condition where APE tag with no ID3v1 tag could be
+     mistaken for APE tag with ID3v1 (and incorrectly parsed)
+   * Bugfix: added warning if ID3v2 frame has zero-length data
+     (thanks cmassetØclubinternet*fr)
+   * Bugfix: getid3.frames.php looking for non-existant key in USER
+     frames
+   Improved detection of RIFF-MP3 data. [unknown program] encodes
+     RIFF-WAV data with a chunk name of 'RMP3' instead of the
+     standard 'RIFF'
+   Encoder now returned in both ['comments'] and ['audio']['encoder']
+     for RIFF-WAV files with an INFO.ISFT chunk
+   Generate a warning for FLAC files encoded with v0.3 or v0.4
+     because audio_signature is not calculated during encoding
+     (thanks ahØartemis*dk)
+   Modified getid3.check.php to display md5_data_source as well as
+     md5_file and md5_data if display-MD5 mode is selected
+   Modified getid3.check.php to assume-format based on file extension
+     in browse mode if fileformat is found to be 'id3' (formerly only
+     if the fileformat was null)
+   Changed scaling of BitrateColor() from representing 1-256kbps to
+     representing 1-768kbps for better display of high-bitrate files,
+     specifically lossless-compressed CD-audio (FLAC, LA, etc)
+
+
+1.6.0: [January-30-2003] James Heinrich
+   » Added support for OggFLAC (FLAC data stored in an Ogg container)
+     (thanks ahØartemis*dk for the idea)
+   » Added support for Speex (the data stored in an Ogg container)
+   » Comments are now available in the root 2-dimensional array
+     ['comments'] - each entry in this array will contain one or more
+     strings. For example, if there are two artists then
+     ['comments']['artist'][0] will contain the first one and
+     ['comments']['artist'][1] the other. All keys are forced
+     lowercase. Comments will be stored in the ['comments'] array in
+     this order of precedence:
+     1) Native format tags (ASF, VQF, NSV, RIFF, Quicktime, Vorbis)
+     2) APE tags
+     3) ID3v2
+     4) Lyrics3
+     5) ID3v1
+     Lower-priority tags will not overwrite or append existing values
+     of higher-priority tags (for example, 'artist' in ID3v1 will be
+     ignored if already specified in APE), but missing values will be
+     filled in (for example, if 'album' is specified in ID3v2 but not
+     in APE, it will be included in the ['comments'] array).
+     Note: Root keys (['title'], ['artist'], etc) are NOT available
+     in this or future versions of getID3().
+     (thanks ahØartemis*dk)
+   » MD5 hashes are now available for all formats for both the entire
+     file (['md5_file']) and the portion of the file containing only
+     the audio/video data, stripped of all prepended/appended tags
+     like ID3v2, ID3v1, APE, etc - ['md5_data']
+     (thanks ahØartemis*dk for alternate md5_file() function that
+     runs on UNIX system running PHP < 4.2.0)
+     NOTE: Ogg files require the use of vorbiscomment to obtain the
+     md5_data value. vorbiscomment must be downloaded from
+     http://www.vorbis.com/download.psp and placed in the getID3()
+     directory. All Ogg formats (Vorbis, OggFLAC, Speex) are affected
+     by this problem, but only OggVorbis files can be processed with
+     vorbiscomment. OggFLAC and Speex files will be processed by
+     getID3(), but this may result in an incorrect value for md5_data
+     in the event that VorbisComments are larger than 1 page (4-8kB).
+     NOTE: md5_data for Ogg will not work if PHP is running in Safe
+     Mode
+   » There is now a wrapper class available, written by Allan Hansen,
+     which should simplify extracting most common basic information
+     (such as format, bitrate, comments).
+     New file: audioinfo.class.php
+   » OggWrite() in getid3.ogginfo.php has been replaced with a new
+     version that uses vorbiscomment to write the comments, because
+     of a reported bug that can corrupt OggVorbis files such they
+     cannot be played.
+     NOTE: Ogg comment writing now requires the use of vorbiscomment
+     which must be downloaded from http://www.vorbis.com/download.psp
+     and placed in the getID3() directory.
+     NOTE: Ogg comment writing will not work if PHP is running in
+     Safe Mode
+   ¤ New root key ['tags'] is now always returned for all formats.
+     It is an array that may contain any of:
+     * Native format tags: 'vqf', 'riff', 'vorbiscomment', 'asf',
+       'nsv', 'real', 'midi', 'zip', 'quicktime'
+     * Appended data tags:  'ape', 'lyrics3', 'id3v2', 'id3v1'
+   ¤ New root key ['audio'] is an array containing any or all of:
+       codec, channels, channelmode, bitrate, bits_per_sample,
+       dataformat, bitrate_mode, sample_rate, encoder
+       Note: This replaces several root keys, including:
+         bitrate_audio, bits_per_sample, frequency, channels
+   ¤ New root key ['video'] is an array containing any or all of:
+       bitrate_mode, bitrate, codec, resolution_x,  resolution_y,
+       resolution_y, frame_rate, encoder
+       Note: This replaces several root keys, including:
+         bitrate_video, resolution_x, resolution_y, frame_rate
+   ¤ ['id3']['id3v1'] has moved to ['id3v1']
+   ¤ ['id3']['id3v2'] has moved to ['id3v2']
+   ¤ ['audiodataoffset'] and ['audiodataend'] have been renamed to
+     ['avdataoffset'] and ['avdataend'] respectively
+   ¤ GetAllMP3info() has been changed to GetAllFileInfo() with a
+     different parameter list ($allowedFormats is no longer a
+     parameter).  Check your code where you're calling
+     GetAllMP3Info() - you will need to change both the function
+     name and the parameter list if you pass more than 2 parameters
+   ¤ All formats now return ['audio']['dataformat'] and/or
+     ['video']['dataformat'] where appropriate - this goes along with
+     ['fileformat'] - ['fileformat'] will return the actual structure
+     of the file, whereas ['dataformat'] will return the format of
+     the data inside that structure. For example, an Ogg file can
+     contain Vobis data (normal), or it can contain FLAC data in the
+     Ogg container format. In that case, ['fileformat'] would be
+     'ogg', but ['dataformat'] would be 'flac'.
+     Note: this means that WAV and AVI files now return a
+     ['fileformat'] of 'riff' rather than 'wav' or 'avi'.
+   ¤ ['filesize'] is no longer returned for files larger than 2GB
+     because PHP does not support large file access. Attempting to
+     parse a file larger than 2GB will result in a message stored in
+     ['error'] and ['filesize'] not set.
+   ¤ APEtag, ID3v1, and ID3v2 are now supported on ALL multimedia
+     files - even if illegal by format. Ogg will return warning if
+     ID3/APE tags are present.  (thanks ahØartemis*dk)
+   ¤ All files: non-critical errors are now returned in the root key
+     ['warning'] rather than ['error'] (only critical errors that
+     prevent getID3() from correctly parsing the file are returned in
+     ['error']  (thanks ahØartemis*dk)
+   ¤ Renamed all references to $MP3fileInfo to $ThisFileInfo
+   ¤ Joliet now supported for ISO-9660.
+     ['iso']['supplementary_volume_descriptor'] is now returned, if
+     available, and ['iso']['files'] will contain ASCII equivalents
+     of the Unicode directory structure & filenames stored.
+   ¤ Moved Monkey's Audio code from getid3.ape.php to seperate file.
+     New file: getid3.monkey.php
+   ¤ Added new keys for ISO-9660: ['name_ascii'] for directories,
+     ['file_identifier_ascii'] for files
+   ¤ Added root key ['track'] for CD-audio files
+   ¤ Ogg/Vorbis-comment files now have comments returned inside
+     ['ogg']['comments_common'] as an array of strings, rather than
+     simple strings in ['ogg']
+   ¤ Quicktime files now have comments returned inside
+     ['quicktime']['comments'] as an array of strings, rather than
+     simple strings in ['quicktime']
+   ¤ ['mime_type'] is a new root key returned for all supported
+     formats (thanks ahØartemis*dk)
+   ¤ ['fileformat'] now returns 'mp1' instead of 'mp3' for MPEG-1
+     layer-I audio files (thanks ahØartemis*dk)
+   ¤ ['mpeg']['audio']['bitratemode'] now returns lowercase
+   ¤ MPEG-4 audio files which consist of MP3 data wrapped in a
+     Quicktime fileformat will now return the usual data in
+     ['mpeg']['audio']
+   ¤ Type-1 DV AVIs are now supported
+   ¤ DV AVIs will return 1 or 2 in ['RIFF']['video'][x]['dv_type']
+   ¤ Changed ['fileformat'] from 'mpg' to 'mpeg' for MPEG video files
+   ¤ ASF comments are now stored in ['asf']['comments'] instead of
+     ['asf']
+   ¤ RealMedia chunk data is now returned inside ['real']['chunks']
+     instead of ['real']
+   ¤ ['replay_gain'] now properly populated from APE tags
+   ¤ Added support for ASF_Old_ASF_Index_Object in ASF files
+     (thanks ahØartemis*dk)
+   ¤ AAC-ADTS files now return ['aac']['bitrate_distribution']
+   ¤ ParseVorbisComments() has been replaced with
+     ParseVorbisCommentsFilepointer() (with different parameters)
+   ¤ All references to any key ['frequency'] are now ['sample_rate']
+   ¤ Moved ID3v2 comments from ['id3v2'] into common root
+     ['comments'] structure, and now returns more values than before
+   * Bugfix: ['iso']['files'] and ['zip']['files'] could potentially
+     contain duplicate entries (in a numeric-indexed array) for files
+     if the directory structure specifies files multiple times.
+     Entries are now guaranteed unique, with the last entry for the
+     file overwriting any former ones.
+   * Bugfix: RIFF parsing had numerous issues, including:
+     - large AVIs would take a very very long time to parse
+     - chunks with odd (not even) sizes would cause the parser fail
+     - video and/or audio codecs not always identified
+     The ParseRIFF() function has been completely rewritten and fixes
+     all known issues with RIFF parsing. Users are, however,
+     encouraged to double-check output of any parsed (AVI/WAV/CDDA)
+     files.
+   * Bugfix: Modified getid3.riff.php to return correct total
+     bitrates for AVIs with multiple audio streams
+   * Bugfix: GetFileFormat() was not creating array structure
+     correctly  (thanks ahØartemis*dk)
+   * Bugfix: LAME tag for MP3s can only specify up to 255kbps, so any
+     files with actual CBR bitrate of >=256 were reported incorrectly
+   * Bugfix: Lyrics3 synched lyrics were not being correctly returned
+   * Bugfix: CreateDeepArray() was broken for non-nested cases, which
+     meant ZIP and ISO ['files'] structures were broken
+   * Bugfix: Incorrect pattern matching for ZIP files meant no zip
+     files were being detected as such
+   * Bugfix: AAC-ADIF was returning an incorrect number of channels
+     (too few) in some cases  (thanks ahØartemis*dk)
+   * Bugfix: Vorbis comments were returning an incorrect value for
+     ['dataoffset'] in some cases
+   * Bugfix: MPEG video ['marker_bit'] and ['vbv_buffer_size'] were
+     incorrect
+   * Bugfix: ['playtime_string'] could potentially have a value of
+     x minutes and 60 seconds (ie 3:60 instead of 4:00)
+   Added support for FLAC cuesheets (FLAC 1.1.0+)
+     (thanks ahØartemis*dk)
+   Improved parsing speed in MP3, MP2 and AAC  (thanks ahØartemis*dk)
+   Extra error-checking added to try and identify corrupt files for
+     most audio formats  (thanks ahØartemis*dk)
+   More accurate playtime calculation for RealMedia
+     (thanks ahØartemis*dk)
+   Changed all relevant files to use ['audiodataoffset'] and
+     ['audiodataend'] rather than ['filesize'] where appropriate
+     (thanks ahØartemis*dk)
+   Added text encoding type 255 as a duplicate of UTF-16BE but with
+     Big-Endian rather than Little-Endian byte order
+   Added many RIFF-AVI audio types and fourcc video types to the
+     lookup functions in getid3.riff.php
+   Added numerous new known GUIDs to getid3.asf.php
+   Added PoweredBygetID3() function to easily get a "powered by"
+     string with the current getID3() version.
+   Added "Morgan Multimedia Motion JPEG2000" (MJ2C), "DivX v5" (DX50)
+     and "XviD" (XVID) codecs to list of known codecs in
+     getid3.riff.php
+   Changed GETID3_INCLUDEPATH path seperators to forced /
+     (from \ for Windows)
+   Modified getid3.check.php to only change \ directory seperators to
+     / on Windows operating systems
+   Modified getid3.check.php to handle larger-than-2GB files (which
+     now do not return a filesize)
+   Modified getid3.check.php to handle ['dataformat_audio'] and
+     ['dataformat_video']
+   Modified getid3.check.php to show a list of present tags in one
+     column rather than one column for each of ID3v1, ID3v2, etc
+   Modified getid3.check.php to show MD5 values. Initially disabled
+     but can be enabled for a directory with a click. md5_file is
+     always calculated when displaying detailed info about a single
+     file; md5_data is calculated if the file is < 50MB
+   Modified getid3.check.php to show errors and warnings. Details are
+     visible with a mouseover or a click.
+   Changed getid3.check.php to use SafeStripSlashes instead of a
+     manual conditional directory name replacement for special
+     characters
+   Added sample recursive scanning sample code to getid3.readme.txt
+     (thanks lipisinØmail*ru for the idea)
+
+
+1.5.7: [January-10-2003] James Heinrich
+   » Added support for ISO 9660 (CD-ROM image) format. Most-useful
+     data is directory structure returned under ['iso']['files']
+     Note: Only ISO-9660 supported, not (yet) Joliet extension
+     (thanks nebula_djØsofthome*net for the idea)
+     New file: getid3.iso.php
+   ¤ ZIP files are now parsed by getID3() itself without relying on
+     built-in PHP functions and/or ZZipLib support.
+     (thanks Vince for the idea)
+   ¤ ZIP files now return a simple directory listing with filename
+     and filesize info only under ['zip']['files'].
+     Note: empty subdirectories will note appear in here, only files
+     and non-empty subdirectories. Information for all entries,
+     including empty subdirectories, is available under
+     ['zip']['central_directory'] (or under ['zip']['entries'] if the
+     Central Directory cannot be located (usually due to a trucated
+     file).
+   ¤ RIFF-WAV files with MP3 data (or MP3s with RIFF headers, if you
+     want to think of it that way) now have the MPEG audio portion
+     scanned and the usual data returned in ['mpeg']['audio'] if the
+     RIFF audio codec has wFormatTag of "85" (identified by getID3()
+     as "MPEG Layer 3")
+     (thanks ahØartemis*dk for the idea)
+   ¤ EXIF data (if present) is returned for JPEG files under
+     ['jpg']['exif']  (thanks nebula_djØsofthome*net)
+   ¤ ['filepath'] now returned for all files with the directory part
+     of the full filename.
+   ¤ ['filenamepath'] is now returned for all files (equivalent to
+     ['filepath'].'/'.['filename'])
+   * Bugfix: ['id3']['id3v2'][<framename>]['dataoffset'] was wrong
+   * Bugfix: MP3s tagged with iTunes have an invalid comment field
+     frame name ('COM ' - should be 'COMM') but the data is valid
+     otherwise; the frame is now renamed to 'COMM' and parsed
+     normally (with the error noted in ['error'])
+     (thanks kheller2Ømac*com for the sample file)
+   * Bugfix: Some ASF/WMA audio files were not being identified as
+     any format  (thanks ahØartemis*dk)
+   * Bugfix: Warning now generated and ASCII format assumed for
+     invalid text encoding values in ID3v2
+   * Bugfix: Changed ZIP detection pattern from 'PK' to 'PK\x04\x03'
+   * Bugfix: Ogg/FLAC files with large Vorbis comments were dying in
+     an infinite loop with lots of error messages due to missing $fd
+     parameter on ParseVorbisComments()  (thanks ahØartemis*dk)
+   * Bugfix: ['data'] and ['image_mime'] were being returned for all
+     Ogg comments even if they were not images for versions of PHP
+     that have image_type_to_mime_type() built in (ie PHP 4.3.0+)
+
+
+1.5.6: [December-31-2002] James Heinrich
+   » Added support for NSV (Nullsoft Streaming Video)
+     (www.nullsoft.com/nsv/)
+     (thanks demonØsoundplanet*com for the idea)
+     New file: getid3.nsv.php
+   » Added support for CD-audio track files (track01.cda etc)
+   ¤ Added standard ['frame_rate'] root value when known (AVI, NSV,
+     MPEG-video)
+   ¤ ASF files now report ['fileformat'] of:
+     'wmv' when Windows Media Video codec v7/v8/v9 is used;
+     'wma' when any 'Windows Media Audio' named audio codec is used
+           and no video stream is present;
+     'asf' in all other cases (audio-only, video-only, or both)
+   ¤ Removed support for ZIP functions (will be rewritten to not
+     require ZZIPlib support in future versions)
+   ¤ Added function SafeStripSlashes() as a drop-in replacement for
+     stripslashes(), but that only strips slashes if magic_quotes_gpc
+     is set
+   ¤ Removed support for remote file scanning (HTTP / FTP)
+   ¤ Added ['aac']['frames'] (number of AAC frames in file)
+   ¤ Added ['mpeg']['audio']['frame_count'] when a bitrate histogram
+     is created
+   ¤ Average bitrate for VBR MP3/MP2 is calculated from actual counts
+     of frames of various bitrates (rather than relying on the header
+     values or filesize) when a bitrate histogram is created
+   ¤ RecursiveFrameScanning() split out into seperate function
+     (getid3.mp3.php)
+   ¤ Removed old function getMP3header() from getid3.mp3.php
+   ¤ Changed default MPEG_VALID_CHECK_FRAMES (number of mp3 frames
+     scanned to ensure a valid audio sequence has been located) from
+     10 to 25. This means scanning will be slightly slower, but more
+     reliable/accurate
+   * Bugfix: ID3v2.2 - valid frame names not correctly detected
+     (thanks maeckerØweb*de for the sample file)
+   * Bugfix: ID3v2.2 - valid padding not correctly detected
+     (thanks maeckerØweb*de for the sample file)
+   * Bugfix: MIDI files with flat key signatures were not being
+     correctly reported (thanks alexleeisØshaw*ca for sample file)
+   * Bugfix: now returns message in ['error'] if file does not exist
+   * Bugfix: ['RIFF']['video'][x]['codec'] wasn't always being
+     correctly populated
+   * Bugfix: ['bitrate'] was incorrect for multi-stream RealMedia
+   * Bugfix: ['playtime_seconds'] was sometimes null or incorrect
+     for multi-stream RealMedia
+   * Bugfix: ChannelTypeID was incorrect in RVA2 ID3v2.4 frames
+   * Bugfix: Fixed potential divide-by-zero error for corrupt FLAC
+     files  (thanks ahØartemis*dk)
+   * Bugfix: AAC-ADTS was not returning ['bitrate_mode'] unless
+     $ReturnExtendedInfo was TRUE  (thanks ahØartemis*dk)
+   * Bugfix: LAME-encoded CBR MP3s now properly identified as CBR
+     with correct bitrate  (thanks ahØartemis*dk)
+   * Bugfix: VBR MP2 (or headerless MP3) is now identified as VBR
+     rather than CBR. Note: to obtain VBR bitrate for headerless
+     files, the entire file is scanned and a histogram distribution
+     of bitrates is created, and the average bitrate calculated from
+     that.  (thanks ahØartemis*dk for sample file)
+   Added support for DSIZ chunks in VQF, and checks to make sure size
+     of audio data matches DSIZ value, if present
+     (thanks ahØartemis*dk for sample file)
+   Rewrote GetAllMP3info() - removed some unneccesary code, changed
+     format-detection routine from ParseAsThisFormat() to
+     GetFileFormat() to allow for more flexible format parsing
+     (needed for ISO CD-ROM images, helpful for Quicktime and others)
+   Changed references in all files from string-cast indexes: ["$i"]
+     to non-cast indexes: [$i] where appropriate
+   Put a sans-serif 9pt style on all text in getid3.check.php
+   getAACADTSheaderFilepointer() now return TRUE if synch is lost
+     after the first frame has been successfully parsed (previously
+     it would return FALSE if synch was lost at any time, meaning the
+     file is most likely MP3, which was incorrect)
+     (thanks ahØartemis*dk for sample file)
+   Speed improvement code changes to getid3.mp3.php (up to 24% faster
+     in some cases)  (thanks ahØartemis*dk for the code)
+   Changed all include_once() to require_once()
+
+
+1.5.5: [November-25-2002] James Heinrich
+   » Added support for La (Lossless Audio - www.lossless-audio.com)
+     (thanks ahØartemis*dk for the idea)
+     New file: getid3.la.php
+   ¤ Moved lookup functions from getid3.lookup.php to the files where
+     they are used.
+     New file: getid3.id3.php
+     New file: getid3.rgad.php
+     Removed file: getid3.lookup.php
+   ¤ getID3v1Filepointer() returns FALSE if ID3v1 tag not found
+   ¤ Added new paramter "ReturnExtendedInfo" to the function
+     getAACADTSheaderFilepointer() in getid3.aac.php which now
+     defaults to FALSE - if TRUE then the data for every frame is
+     returned (containing aac_frame_length, adts_buffer_fullness and
+     num_raw_data_blocks, which aren't usually very useful). Speed
+     improvement with FALSE is about 35%.
+   ¤ Now returns fopen() errors in ['error'], for example if a remote
+     file is not accessible.
+   ¤ Changed default number of MP3 audio frames to scan to determine
+     if a valid stream has been found from 5 to 10, now also defined
+     as a constant at the top of getid3.mp3.php  This will result in
+     slightly slower MP3 parsing, but greater reliability in
+     detecting false/invalid/corrupted VBR headers.
+   ¤ fopen() errors now displayed in getid3.putid3.php
+     (thanks miguel.dieckmannØhamburg*de)
+   ¤ Added 4th parameter to decodeMPEGaudioHeader() $ScanAsCBR which
+     will force an MP3 audio frame sequence to be force-scanned in
+     CBR mode. You should never need to call this directly, it's only
+     used internally to scan for MP3 files that have an illegal VBR
+     header with CBR data. (thanks fletchØpobox*com)
+   * Bugfix: ASF_Marker_Object in getid3.asf.php was always returning
+     an error in non-existant "reserved_1" and failing
+   * Bugfix: VBR bitrate calculations in getid3.mp3.php only occur if
+     ['mpeg']['audio']['VBR_frames'] is defined.
+     (thanks fletchØpobox*com)
+   * Bugfix: getid3.putid3.php no longer deletes original MP3 if
+     ID3v2 tag writing fails (thanks miguel*dieckmannØhamburg*de)
+   * Bugfix: incorrect order of if-statement error messages in
+     getid3.putid3.php (thanks miguel*dieckmannØhamburg*de)
+   getid3.asf.php now notes the error and continues parsing rather
+     than failing when it encounters an error parsing a chunk
+   Now actually scan 1000 frames for AAC ADTS as reported in the
+     v1.5.4 changelog, rather than 100. (thanks ahØartemis*dk)
+   Improved scanning speed in getAACADTSheaderFilepointer() by ~30%
+     (thanks ahØartemis*dk for the fix)
+   Added FileSizeNiceDisplay() function to getid3.functions.php for
+     formatting filesize output in kB, MB, GB, etc.
+
+
+1.5.4: [October-07-2002] James Heinrich
+   » Added support for Quicktime.
+     New file: getid3.quicktime.php
+   » Added support for AAC files, both ADTS and ADIF header formats.
+     ADIF format is a pain because it's very similar to standard MP3
+     header format, and it's hard to distinguish between the two. I
+     have tried to make the detection accurate, but I have a limited
+     number of AAC test files to play with so if you have an AAC file
+     that gets detected as MP3/MP2 (or vice-versa), please send me
+     the details via email at getid3Øsilisoftware*com
+     ADTS format is very slow to parse because to get the bitrate of
+     VBR files the whole file has to be stepped through frame by
+     frame (getID3() scans up to the first 1000 frames and assumes
+     that to be close enough).
+     Note: I would suggest commenting out support for AAC (see top of
+     GetAllMP3info() function in getid3.php) unless you need it.
+     (thanks jfaulØgmx*de for the idea and sample Delphi source code)
+     New file: getid3.aac.php
+   » Added bitrate distribution analysis option for MP3 VBR files. A
+     new boolean parameter for getOnlyMPEGaudioInfo() enabled this
+     feature which steps through the MP3 file frame by frame and
+     counts how many frames of each bitrate exist. This information
+     is returned in ['mpeg']['audio']['bitrate_distribution']
+     Caution: this feature is very inefficient for large files and
+     takes a very long time and does lots of disk I/O. Use with care.
+   ¤ Changed layout of allowedFormats in GetAllMP3info() function in
+     getid3.php to allow easy removal of support for any of the
+     supported format. As stated above, I recommend commenting out
+     AAC unless needed.
+   ¤ Added ['flac']['compressed_audio_bytes'],
+     ['flac']['uncompressed_audio_bytes'], and
+     ['flac']['compression_ratio']
+   ¤ Replaced FXPT2DOT30toFloat() function with FixedPoint2_30()
+   * Bugfix: getid3.mpc.php was slightly miscalculating the number of
+     samples, therefore also bitrate and playtime
+     (thanks ahØartemis*dk for the fix)
+   * Bugfix: MonkeyCompressionLevelNameLookup() didn't know about
+     'insane' compression (thanks ahØartemis*dk for the fix)
+   * Bugfix: MonkeySamplesPerFrame() was incorrect for MAC v3.95+
+     (thanks ahØartemis*dk for the fix)
+   * Bugfix: getid3.check.php wasn't processing the assumeFormat
+     directive when (register_globals == off)
+   * Bugfix: detecting of synch pattern for MP3 files with invalid
+     data at the beginning wasn't always correct, also meant possible
+     incorrect bitrate/duration/etc info for such corrupt files.
+   getid3.functions.php now includes a replacement utf8_decode()
+     function for those PHP installations that are not configured
+     with the --with-xml option. (thanks stephaneØtekartists*com)
+
+
+1.5.3: [September-30-2002] James Heinrich
+   » Added support for VQF. (thanks mtØmansonthomas*com for the idea)
+     New file: getid3.vqf.php
+   » Added support for FLAC. Comments, if present, are returned under
+     ['ogg'] because they follow the Ogg Vorbis structure standard.
+     New file: getid3.flac.php
+   ¤ OS/2-format bitmaps are now correctly interpreted. The format of
+     the bitmap is now returned in ['bmp']['type_os'] and
+     ['bmp']['type_version']. OS/2 bitmaps can be v1 or v2, Windows
+     can be v1, v4 or v5
+
+
+1.5.2: [September-25-2002] James Heinrich
+   » Support for RealMedia (audio & video) added
+     Note: only tested on G2 and v5 audio and video files - if anyone
+     has older and/or newer sample files, please test it and/or send
+     me the sample files.
+     (thanks stephaneØtekartists*com for idea)
+     New file: getid3.real.php
+   » Support for BMP added. Palette and pixel data can optionally be
+     extracted as well - this is slow and generally unneccesary, but
+     the option is there if you need it. Also includes PlotBMP()
+     which will take the extracted pixel data and output it as a true
+     color PNG. This function requires GD v2.0+
+     Note: Untested on 16-bit and 32-bit BMPs because I couldn't find
+     any sample files - if you know of a program that can create such
+     files, please email getid3Øsilisoftware*com
+     Note: Support for RGB (uncompressed), RLE8 and RLE4 is included
+     and tested. BITFIELDS support is also included for 16- & 32-bit
+     formats, but it's untested, so if anybody has any test files
+     please send them to getid3Øsilisoftware*com
+     Note: Support currently only for Windows-format BMPs, and trying
+     to parse an OS/2-format bitmap leads to unpredictable/invalid
+     results.
+     New file: getid3.bmp.php
+   » PNG now fully parsed, including all information chunks
+     New file: getid3.png.php
+   ¤ Support for GIF/JPG/PNG moved to seperate files and expanded,
+     including standard ['resolution_x'] and ['resolution_y'] as well
+     as more thorough parsing of header information
+     New file: getid3.gif.php
+     New file: getid3.jpg.php
+   table_var_dump() simplified and now outputs &#123;-style character
+     entities for characters outside the normal alphanumeric range
+   CleanOggCommentName() changed to a regular expression
+     (thanks chris-getid3Øbolt*cx for rewriting the function)
+
+
+1.5.1: [September-20-2002] James Heinrich
+   » Added support for MPEGplus/Musepack SV7. ['fileformat'] is 'SV7'
+     for version 7 files (versions 4, 5 ,6 and 8 are not supported
+     yet, but will be of ['fileformat'] SV4, SV5, SV6 and SV8) when
+     they are supported (thanks Christian Fritz for the idea)
+     New file: getid3.mpc.php
+   ¤ ['bitrate_audio'], ['bitrate_video'], ['bitrate_mode'],
+     ['channels'], ['resolution_x'], and ['resolution_y'] keys added
+     for all appropriate formats
+   ¤ Ogg files with a COVERART comment now save and display the
+     attached image the same way as is done with ID3v2 APICs
+   ¤ ['ogg']['comments'][n]['data'] and
+     ['ogg']['comments'][n]['dataoffset'] is now returned for all
+     comments. ['ogg']['comments'][n]['data'] is only useful if
+     the field is supposed to contain binary data. It is a
+     base64_decode()'d version of ['value'].
+     ['ogg']['comments'][n]['dataoffset'] is the byte offset in the
+     file at which the 'COMMENTNAME=value string' starts, not the
+     start of just 'value'
+   ¤ ['ogg']['comments'][n]['image_mime'] is now returned if
+     ['ogg']['comments'][n]['data'] contains valid image data.
+   ¤ More than 3 Ogg pages may now be read in, if the comment data
+     is longer than 1 page (usually about 4kB)
+   ¤ ['fileformat'] is now 'mp2' rather than 'mp3' if it's MPEG-1,
+     Layer-II audio
+   ¤ ASF bitrates now calculated even if stream_bitrate_properties
+     object not present
+   ¤ ['asf']['stream_properties_object'] is now a numeric-key array
+     with one entry for each stream - the key being the stream number
+   ¤ ['replay_gain'] is returned for all audio formats that support
+     it (MP3-LAME, ID3v2, Ogg) (thanks Christian Fritz for the idea)
+   ¤ ['mpeg']['audio']['LAME']['RGAD']['radio_replay_gain'] is now
+     ['mpeg']['audio']['LAME']['RGAD']['radio'] (same for audiophile)
+   ¤ ASF/WMA files now use WM/Track to get track number from if
+     WM/TrackNumber is not available (thanks stephaneØtekartists*com)
+   ¤ ASF/WMV files now returns ['year'] and ['asf']['year']
+   ¤ ASV/WMV files now use ['content_description']['description'] for
+     the ['comment'] field (thanks stephaneØtekartists*com)
+   ¤ ['track'] is now always returned as an integer
+   * Bugfix: Ogg comments that are larger than one data page (usually
+     about 4kB) are now correctly parsed (thanks Christian Fritz)
+   * Bugfix: Ogg comment data is now UTF8-decoded
+   * Bugfix: Ogg comment writing now UTF8-encodes the data
+   * Bugfix: playtime for ASF files was off by <preroll> (usually
+     between 3 and 12 seconds)
+   * Bugfix: ['asf']['stream_properties_objects']['flags'] data was
+     possibly incorrect
+   * Bugfix: ASF Padding Object was overwriting
+     Stream Bitrate Properties Object data (now returned correctly in
+     ['asf']['padding_object']
+   * Bugfix: ASF Marker Object Reserved_2 field was incorrect
+   * Bugfix: ASF Bitrate Mutual Exclusion Object had incorrect stream
+     numbers
+   Warning displayed if incorrectly-formatted Ogg comment is present
+     (known to be an issue with CDex v1.40, but fixed by v1.50b7)
+     (thanks Christian Fritz)
+   Ogg comment writing now checks for valid comment names
+   Added bitrate column in getid3.check.php, and added some formatting
+     (font, colour)
+   Performance tweaks using bitwise math instead of binary string
+     operations
+
+
+1.5.0: [September-18-2002] James Heinrich
+   » Ogg comment writing support added. getid3.write.php has been
+     updated to allow for writing comment tags to both MP3 and Ogg.
+     Big thanks to Chris Bolt <chris-getid3Øbolt*cx> for writing the
+     OggWrite() function and offering it for inclusion in getID3()
+     New file: getid3.ogginfo.php
+   » Support for Monkey's Audio and APE tag added.
+     (thanks Christian Fritz for the idea)
+     New file: getid3.ape.php
+     ['fileformat'] now returns 'mac' for Monkey's Audio files, or
+     'ape' for files with an APE tag (Monkey's Audio or other format)
+   » getid3.thumbnail.php has been removed from the distribution and
+     the table_var_dump() function now outputs APICs as seperate
+     files in the same directory as the analyzed file. This should
+     make the image-displaying more reliable as well as reduce
+     complexity. The naming convention for the images is
+     filename.ext.[byte offset of APIC data].[jpg|gif|png]
+     If anybody still has any problems with corrupted images please
+     let me know at getid3Øsilisoftware*com
+   » Support for extended Xing/LAME tag
+     (see http://users.belgacom.net/gc247244/extra/tag.html)
+     Data is returned in ['mpeg']['audio']['LAME']
+   ¤ ['ogg']['tracknumber'] has been renamed to ['ogg']['track'] and
+     ['track'] is now returned in the root of the array
+   ¤ ['ogg']['pageheader'][n]['flag'] has been renamed to
+     ['ogg']['pageheader'][n]['flags'] and the unprocessed flag byte
+     is available in ['ogg']['pageheader'][n]['flags_raw']
+   ¤ ['frequency'] is now returned for WAVE files in the root of the
+     array (thanks danielØelectroteque*org)
+   ¤ ASF files now return codec, bitrate, resolution, etc information
+     under ['asf']['video_media'] or ['asf']['audio_media']
+   * Bugfix: RVA2 and EQU2 writing in getid3.putid3.php were
+     incorrectly writing Volume Adjustment field
+   * Bugfix: EQU2 in getid3.frames.php was reading Volume Adjustment
+     as unsigned integer instead of signed integer
+   * Bugfix: handling of remote files over HTTP & FTP was broken
+     (thanks Vince)
+   * Bugfix: incorrect handling of some ASF packets
+   ASF/Windows Media format now more fully parsed, including Index
+     Objects
+   Added several new fourCC video codecs
+
+
+1.4.3: [September-15-2002] James Heinrich
+   » Now parses ASF / WMV / WMA files
+   ¤ New file: getid3.asf.php
+   * Bugfix: RoughTranslateUnicodeToASCII() would return nothing
+     if didn't find a terminator it was expecting
+   Added FILETIMEtoUNIXtime() function (for converting 64-bit
+     Microsoft FILETIME timestamps, used in ASF files and elsewhere,
+     to UNIX Epoch timestamps)
+   Added GUIDtoBytestring() and BytestringToGUID() functions
+
+
+1.4.2: [September-12-2002] James Heinrich
+   » getID3() now requires PHP v4.1.0 or higher because it now is
+     designed to work with register_globals = off and the new auto-
+     globals ($_GET, $_SERVER, etc).
+   * Bugfix: VBR MP3 files with Fraunhofer-style VBR header were not
+     being correctly detected in most cases
+     (thanks dkushnerØoddcast*com and mikeØftl*com for sample files)
+   * Bugfix: IsValidTextEncoding() was broken
+   * Bugfix: Add stripslashes($EditorFilename) to getid3.write.php
+     (writing was broken for files with ' or " in the filename)
+     (thanks mikeØftl*com and kthejoker)
+   * Bugfix: If there is garbage data between a valid VBR header
+     frame and a sequence of valid MPEG-audio frames the VBR data is
+     no longer discarded. (thanks to mikeØftl*com for sample
+     Fraunhofer-style VBR file produced with MusicMatch v7.2)
+   ¤ Changed variable system to work with (register_globals = off)
+   ¤ Moved relevant code into seperate PlaytimeString() function
+   ¤ Added nl2br() to table_var_dump() for cleaner output
+   ¤ Now returns the following keys from Fraunhofer-VBR files:
+     ['VBR_seek_offsets'], ['VBR_seek_offsets_stride'],
+     ['VBR_offsets_relative'] and ['VBR_offsets_absolute']
+   ¤ Added ID3v1matchesID3v2() function and implemented in
+     getid3.check.php (thanks to "Guest" in the forums for the idea)
+   Changed amount of data read in getid3.getimagesize.php from 10kB
+     to entire file. (thanks mikeØftl*com)
+   Wrapped function_exists() checks around function definitions in
+     getid3.functions.php
+   Fixed a lot of E_WARNING and E_NOTICE situations, especially in
+     ID3-writing code (getid3.putid3.php, etc)
+   Added checks to make sure all needed data is available for writing
+     ID3v2 tags
+
+
+1.4.1b5: [May-30-2002] James Heinrich
+   * Bugfix: Unsynchronise() was broken, now fixed
+     (thanks mikeØftl*com)
+   * Bugfix: GenerateID3v2Tag() now correctly uses non-synchsafe
+     integers for frame size descriptors in ID3v2.3 and ID3v2.2
+     (thanks mikeØftl*com)
+   ¤ Added ['artist'], ['title'], etc keys to root of returned
+     array to provide a common place to access any returned info
+     from any file type. Currently gets info from ID3v1, ID3v2,
+     Ogg, and RIFF/WAVE. Possible returned keys are:
+     title, artist, album, year, genre, comment, track
+   ¤ Modified LookupGenre() function to search for either genre based
+     on numeric ID, or now reverse lookup as well
+   ¤ Added ['artist'], ['title'], etc keys to ['RIFF'] information
+     if info tags are present
+   Added functionality to attach a picture to the ID3v2 tag in
+     getid3.write.php
+   Sorted genres into alphabetical order (special 3 at end of list)
+     in getid3.write.php
+   Changed the comment-edit field in getid3.write.php to a multi-line
+     <textarea> from a single-line <input>
+   getid3.write.php now only writes ID3v2 frames that have data
+   Added default TXXX field to getid3.write.php to put a tagger info
+     field when writing ID3v2 tags. Description is "ID3v2-tagged by"
+     and data is "getID3() v[version] (www.silisoftware.com)"
+   Changed getid3.check.php to use the new common info keys
+   Improved file-format detection in getid3.check.php - if the auto-
+     detect based on the first few bytes of the file doesn't find a
+     known format (for example if the header is corrupt), a more
+     thorough scan is done based on the file extension
+   Added 'Edit ID3' link from getid3.check.php to getid3.write.php for
+     MP3 files  (thanks maxØgutalin*com for the idea)
+   Added 'Delete file' link from getid3.check.php to getid3.write.php
+     allowing you to permanently delete a file (be careful with this!!)
+     (thanks maxØgutalin*com for the idea)
+   Added some mouse-over titles for links in getid3.check.php
+
+
+1.4.1b4: [May-15-2002] James Heinrich
+   * Bugfix: getid3.check.php wasn't parsing MP3s with invalid headers
+     or padding at the beginning of the file - added 'assumeFormat'
+     parameter and 'Parse this file as:' options to force parsing in a
+     particular format  (thanks Alcohol for the sample file)
+   * Bugfix: unset(['fileformat']) and ['error'] added in cases where
+     file cannot be parsed in the assumed or forced format
+
+
+1.4.1b3: [May-01-2002] James Heinrich
+   ¤ For Ogg files, now calculates the real average bitrate (returned
+     in ['ogg']['bitrate_average']) and so the playtime of the file is
+     calculated on actual average bitrate, not nominal bitrate, so it
+     should be accurate now  (thanks to stephaneØtekartists*com for
+     telling me it was wrong)
+   * Bugfix: ID3v2FrameIsAllowed() wasn't behaving properly if the
+     writing functions were called for more than one file, because of
+     the static array not being cleared between uses. This is an
+     updated fix because the one in 1.4.1b2 didn't work :o)
+     (thanks soulcatcherØevilsoft*org and yoyo)
+   Added rawurlencode() to the filename parameter in table_var_dump()
+     for images (wouldn't work with path/file names containing special
+     characters (#, &, ", +)  (thanks Christian Fritz)
+   getid3.check.php no longer attempts to scan all MIDI tracks in
+     directory-browse mode, since this can take a long time. Detailed
+     single-file view is still fully scanned (new third parameter for
+     getMIDIHeaderFilepointer() controls this)
+   Small improvements to MoreNaturalSort()
+
+
+1.4.1b2: [April-18-2002] James Heinrich
+   ¤ GetAllMP3Info()'s 2nd parameter has changed from boolean to string
+     (now specifying the parse-this-file-as-this format, like 'mp3',
+     but also can be FALSE to mean don't assume any format, auto-detect
+     only), and a third parameter (array containing allowed formats)
+     has been added. The assumedFormat parameter allows a file to be
+     forced to be parsed as a certain format rather than relying on the
+     auto-detection of getID3() (ex: an MP3 wrapped in a RIFF/WAV
+     header will be auto-detected as RIFF/WAV, but if forced to parse
+     as MP3 will extract the original MP3 information)
+     (thanks reel_tazØusers*sourceforge*net)
+   * Bugfix: ID3v2FrameIsAllowed() wasn't behaving properly if the
+     writing functions were called for more than one file, because of
+     the static array not being cleared between uses (thanks yoyo)
+   * Bugfix: Lyrics3 data wasn't being properly copied from the ['raw']
+     keys to the easy keys (['title'], etc.)  (thanks Christian Fritz)
+   * Bugfix: some testing code was accidentally left in
+     getid3.thumbnail.php  (thanks Christian Fritz)
+   * Bugfix: RIFF/WAVE files are now more likely to have all their
+     chunks parsed.
+   * Bugfix: RIFF/WAVE bitrate & playtime now better calculated
+   * Bugfix: MP3 scanning for synch doesn't go beyond 64k now, to stop
+     intensive scanning through large file that don't have a synch
+     (thanks soulcatcherØevilsoft*org for a weird sample file)
+   Improved performance when scanning for MP3 synch (about 600% faster
+     if the synch is never found)
+   ZIP files no longer return the contents of each compressed file, as
+     that would very easily be more data than PHP could handle.
+     (thanks davidbullockØtech-center*com)
+   getid3.check.php now displays entries in a more natural sort order:
+     case insensitive, ignores most punctuation, treats accented chars
+     the same as their unaccent equivalent  (thanks mikeØftl*com)
+   Added support for SmartSound-format RIFF files (which are regular
+     RIFF/WAVE files with the first 4 chars changed from RIFF to SDSS)
+   All instances of while(list() = each()) replaced with foreach()
+
+
+1.4.1b1: [April-11-2002] James Heinrich
+   » Parses MIDI files.
+     NOTE: very slow at parsing, much slower than any other file type
+     NOTE: playtime is generally mostly accurate, but not always 100%
+   » Parses ZIP files (if ZZIPlib available, and only in PHP 4.0.7RC1
+     and later (see http://www.php.net/manual/en/ref.zip.php)
+     NOTE: currently untested as I'm unable to find php_zip.dll for
+     PHP/Win32 - if someone has a copy of this file, please email me:
+     getid3Øsilisoftware*com
+   » Parses JPEG files (requires GD installed)
+   » Parses PNG files  (requires GD v1.6+ installed)
+   » Parses GIF files  (requires GD < v1.6 installed)
+   » For MP3s, once a valid synch is detected, the next 5 frames are
+     also scanned for valid synch signatures, to prevent false
+     identification of synch. For corrupt MP3 files this will be a bit
+     slower, but hopefully produce more reliable results.
+     (Thanks to mpdjØbtinternet*com for bringing this to my attention,
+     and xbhoffØpacbell*net for explaining what was happening)
+     (Thanks also to macik for helping me with MP3 frame lengths:
+     http://66.96.216.160/cgi-bin/YaBB.pl
+     ?board=c&action=display&num=1018474068)
+   » The actual image data is now displayed (for JPEG, PNG and GIF
+     images only) rather than a binary text dump in getid3.check.php
+     (specifically table_var_dump()) for APIC frames. Made possible
+     by the inclusion of (a modified version of) GetURLImageSize() by
+     Filipe Laborde-Basto (www.rezox.com). You can right-click, save-as
+     to extract the image to a file.
+     NOTE: The actual image data is still returned in ['data']
+   ¤ ['image_mime'], ['image_width'], ['image_height'], ['image_bytes']
+     are now returned for APICs
+   ¤ split parsing functions out into seperate files: lyrics3, id3v1,
+     id3v2, mp3, ogg, riff, mpeg, midi, zip
+   ¤ ['ogg']['bitrate_ave'] -> ['ogg']['bitrate_nominal'] (thanks to
+     stephaneØtekartists*com for pointing out that "nominal" bitrate
+     may actually differ significantly from the "average" bitrate)
+     The real average bitrate seems to be only extractable by parsing
+     the entire file and calculating the average bitrate. This is not
+     yet an option, but hopefully in a future version of getID3()
+   ¤ ['filename'] now returned for all files
+   ¤ ['ogg']['date'] and ['ogg']['description'] now returned when
+     available  (thanks stephaneØtekartists*com)
+   ¤ ['mpeg']['audio']['crc'] now contains the CRC (if present)
+   ¤ ['bitrate'] is now returned as a double instead of an int
+   ¤ ['dataoffset'] is now returned for all ID3v2 frames
+   * Bugfix: MP3 CRC presence ['mpeg']['audio']['protection'] was being
+     reported as opposite of what it actually should be
+   * Bugfix: MPEG videos weren't being detected (they were being
+     parsed as MP3), and even if they were, there was a typo in
+     getMPEGHeaderFilepointer()  (thanks Christian Fritz)
+   * Bugfix: getid3.functions.php wasn't being included in
+     getid3.write.php  (thanks mikeØftl*com)
+   * Bugfix: Browse:___ directory name in getid3.check.php wasn't
+     correct with directory names with ' and other strange characters
+     (thanks Christian Fritz)
+   ID3v2FrameProcessing() now checks to see if the next frame is valid
+     after it encounters an invalid FrameID, and if the next frameID
+     appears valid, it will just skip the current (invalid) frame and
+     continue processing (it would previously abort at the first sign
+     of incorrect structure)   (thanks stephaneØtekartists*com)
+   getid3.check.php now scans filetypes based on content, not filename
+     extension, and shows the filetype in the displayed output. Files
+     are only scanned as MP3 if ID3v2 or MPEG-audio signatures are at
+     the immediate beginning of the file (MP3 used to be the default
+     format), so a corrupt file may not show up as MP3 format in the
+     browse screen, but in detail it will scan in-depth
+   getid3.check.php now has columns to show the presence of ID3v1,
+     ID3v2 and Lyrics3 content
+   Helium2 (www.helium2.com) has been known to write ID3v2.4 tags with
+     non-synchsafe-integer framesizes, getID3() now checks for this and
+     will override and parse the tag as ID3v2.3 if the tag would parse
+     fine as ID3v2.3 when it's really specified as ID3v2.4  (thanks
+     Christian Fritz for the test files)
+
+
+1.4.0b9: [April-05-2002] James Heinrich
+   » Ogg files now return bitrate and playtime (playtime calculated
+     from nominal bitrate and filesize, so it's only approximately
+     accurate).  (thanks stephaneØtekartists*com for the idea)
+   * Bugfix: ID3v1 tags were not properly being parsed - track, genre
+     and comment fields were incorrect.  (thanks Christian Fritz)
+   * Bugfix: getid3.check.php would not browse directories with single
+     quotes (') or double quotes (") in the directory name.
+     (thanks Christian Fritz)
+   * Bugfix: Improved detection of MPEG-video files (a sample MP3 file
+     had a false MPEG video signature at the beginning), and the MPEG-
+     video parsing function now only looks for the MPEG-video header
+     in the first 100k bytes of the file, to prevent needlessly
+     scanning very large files. Also will not infinitely loop if it
+     does not find what it's looking for.  (thanks Christian Fritz)
+   ['error'] now returned if MP3 synch doesn't occur at beginning of
+     file if ID3v2 not used (ie there's some kind of padding there that
+     should not be)
+   Reduced use of fread() in getMPEGHeaderFilepointer() (now faster)
+   Added "file parsed in x.xxx seconds" to getid3.check.php
+   Added "browse: <directory>" link to getid3.check.php
+   Changed default ID3v2 majorversion from 2.4 to 2.3 in
+     getid3.write.php because Winamp (and probably many other
+     ID3v2-aware tools) can only read up to ID3v2.3
+     (thanks mikeØftl*com)
+
+
+1.4.0b8: [April-04-2002] James Heinrich
+   » Lyrics3 support added  (thanks Christian Fritz for the idea)
+   ¤ check.php renamed to getid3.check.php
+   ¤ write.php renamed to getid3.write.php
+   ¤ ['id3']['id3v2']['error'] (if present) now reported in ['error']
+   ¤ ['mpeg']['audio']['error'] (if present) now reported in ['error']
+   * Bugfix: RoughTranslateUnicodeToASCII() was completely mangling
+     UTF-16/UTF-16BE encoded text
+   * Bugfix: The warning about MP3ext wasn't always showing up
+     (thanks davidbullockØtech-center*com)
+   getID3v1Filepointer() cleaned up & shortened
+   Moved the include_once() statements around so that a minimum of code
+     is included
+
+
+1.4.0b7: [April-03-2002] James Heinrich
+   » RIFFs (specifically AVIs) are now more completely parsed,
+     almost everything in the returned ['RIFF'] array has been moved
+     around and/or restructured. A lot of new data is in there too -
+     codecs, frame size, etc.
+   ¤ Better recursive parsing of RIFFs (sub-arrays are now in the right
+     place)
+   * Bugfix: the isset() idea introduced in beta 5 was incorrectly
+     implemented, such that ['asciidata'] and ['asciidescription'] were
+     never returned - this had the side effect that ID3v2 comments were
+     not copied to ['id3']['id3v2']['comment']  (thanks mikeØftl*com)
+   * Bugfix: MPEG audio synch wasn't being detected, and therefore MPEG
+     audio data not parsed, if no ID3v2 header present in an MP3
+   ID3v1 track number only returned if greater than zero
+   Removed !== FALSE (introduced in 1.4.0b6) from while(fread()) loops,
+     some users were reporting problems with that syntax.
+   Changed substr($string, 0, 1) to $string{0} syntax in most files
+   Reformatted changelog.txt to 72-column width
+
+
+1.4.0b6: [April-01-2002] James Heinrich
+   * Bugfix: 1.4.0b5 introduced a bug where any RIFF file other than
+     PCM WAVE (this includes any compressed WAV, as well as all AVIs)
+     would crash getID3()
+   Reduced use of fread() in getOggHeaderFilepointer() for increased
+     speed
+   Added constant FREAD_BUFFER_SIZE for many fread() operations
+   Added !== FALSE check to while(fread()) loops
+     (thanks davidbullockØtech-center*com)
+   Added more entries to RIFFwFormatTagLookup()
+     (still looking for a good complete list)
+   Converted use of hexdec() in getid3.lookup.php to 0x1234 notation
+
+
+1.4.0b5: [March-28-2002] James Heinrich
+   ¤ Renamed decodeheader() to decodeMPEGaudioHeader()
+   * Bugfix: Fixed infinite loop problem for RIFF/WAV files with
+     unknown chunks
+   * Bugfix: WXXX frames were incorrectly writing from ['URL'] instead
+     of ['url']
+   * Bugfix: RoughTranslateUnicodeToASCII() wasn't properly decoding
+     UTF-16/UTF-16BE
+   Changed all quoted strings from " to ' to hopefully improve speed
+     (although benchmarks have not yet shown any significant
+     improvement in speed)  (thanks davidbullockØtech-center*com)
+   Improved code in check.php for dealing with symbolic links
+     (thanks davidbullockØtech-center*com)
+   Changed '<?' tags to '<?php'  (thanks davidbullockØtech-center*com)
+   Added processing time indicator in check.php
+     (ie 'directory scanned in 2.45 seconds')
+   Replaced all instances of feof() to prevent infinite loop conditions
+   Moved lookup portions of decodeMPEGaudioHeader() to
+     getid3.lookup.php
+   Replaced $arrayname[$index] with $arrayname["$index"] to avoid PHP
+     E_NOTICEs  (thanks davidbullockØtech-center*com)
+   Wrapped isset() around many if statements, to avoid PHP E_NOTICEs,
+     hence improve speed (up to 30x speed improvement reported in some
+     cases :)
+
+
+1.4.0b4: [March-26-2002] James Heinrich
+   ¤ RIFF/WAV file format now parsed, returned under ['riff']
+   ¤ Support for Relative Gain Adjustment in RIFF/WAV files
+   ¤ ['channels'] (1 or 2) now returned for MP3 and WAV files
+   ¤ ['bitrate'] now returned (in bits-per-second) at root level for
+     MP3 and WAV files
+   Added support for RGAD (Relative Gain ADjustment) ID3v2 frames, both
+     reading & writing
+     (see http://privatewww.essex.ac.uk/~djmrob/replaygain/ for details
+     on RGAD)  (thanks Christian Fritz for the idea)
+   Removed some test data-dumping from the ID3v2 writing functions
+   Language code 'XXX' now returns descriptive string 'unknown' instead
+     of NULL
+   Seperated out comments from top of getid3.php into getid3.readme.txt
+     and changelog.txt
+   Split out non-lookup functions from getid3.lookup.php to
+     getid3.functions.php
+
+
+1.4.0b3: [March-25-2002] James Heinrich
+   ¤ ['asciidata'] for WXXX frames now returns correct information, but
+     under ['asciidescription']  (thanks Christian Fritz)
+   ¤ Added ['framenamelong'] to all returned frame data arrays with
+     text description of that frame (ie 'RVA2' would return 'Relative
+     volume adjustment (2)')  (thanks Christian Fritz)
+   ¤ ['datalength'] is now ['indexeddatalength'] in ASPI frames (was
+     confliciting with the all-frames ['datalength'] as introduced in
+     v1.4.0b1
+   ¤ ['datalength'] now returned as integer (rather than double) where
+     possible
+
+
+1.4.0b2: [March-21-2002] James Heinrich
+   ¤ ['mpeg']['audio']['bitrate'] now returned as int rather than
+     double for VBR files
+   * Bugfix: MPEG audio information wasn't being parsed on files that
+     had neither ID3v1 or ID3v2
+   * Bugfix: COMM/WXXX frames weren't returning 'asciidata' in
+     ID3v2.2, which also meant the ['id3']['id3v2']['comment'] field
+     wasn't being returned  (thanks stephaneØtekartists*com)
+   * Bugfix: file might not be found if filename actually contains
+     escaped chars or %xx-formatted characters
+     (thanks reel_tazØusers*sourceforge*net)
+   Added support for running with Register Globals turned off
+     (thanks reel_tazØusers*sourceforge*net)
+   Added urlencode() where needed in check.php
+     (thanks reel_tazØusers*sourceforge*net)
+   Fixed IE buffering/display problem in progress counter in check.php
+
+
+1.4.0b1: [March-11-2002] James Heinrich
+   » ID3v2 writing support via WriteID3v2() in putid3.php
+     RemoveID3v2() and RemoveID3v1() functions now available in
+     putid3.php  All ID3v1 and ID3v2 writing functions have been moved
+     to putid3.php and example file write.php has been added to the
+     distribution
+   ¤ MPEG audio frame information (bitrate, frequency, etc) now
+     returned inside ['mpeg']['audio'] instead of just ['mpeg']
+   ¤ MPEG video information now parsed, returned in ['mpeg']['video']
+     Note: audio portion of video system files is not yet being parsed
+   ¤ All flag bits are now returned as boolean rather than int or
+     string
+   ¤ RVA2 data now returned as an array (multiple RVA2 tags are
+     allowed)
+   ¤ RVA2/EQU2 description returned under ['description'] rather than
+     ['identification']
+   ¤ RVAD/EQUA adjustments now returned as signed integers, rather than
+     absolute values which required you to check flag bytes
+   ¤ RVRB/REV data no longer returns under ['reverb'] array
+   ¤ WXXX/W???/LINK frames now return ['url'] instead of ['URL']
+   ¤ USER now properly returns both ['language'] and ['languagename']
+   ¤ OWNE now returns ['purchasedateunix'] as a UNIX timestamp
+     (only if ['purchasedate'] is a valid date)
+   ¤ ['id3']['id3v2']['padding'] now returned with information on padding
+   ¤ ['headerlength'] now includes the initial 6 or 10 bytes of the
+     ID3v2 header
+   ¤ ['artist'], ['title'], ['album'], ['tracknumber'], ['genre'] now
+     returned for easier access for Ogg files
+   ¤ added ['datalength'] to all ID3v2 frames: length of frame data,
+     not including frame header
+   ¤ ['fileformat'] now returns 'id3' if there are ID3v1 or ID3v2 tags
+     but no audio data
+   ¤ ['fileformat'] now returns 'mpg' if it's an MPEG system (video +
+     audio) file
+   * Bugfix: RVAD was being parsed incorrectly
+   * Bugfix: ['currency'] and ['purchasedate'] now correctly returned
+     in OWNE
+   * Bugfix: Frequncies in 'EQU2' frames were incorrectly double
+   * Bugfix: ['bytedeviation'] and ['msdeviation'] now properly
+     returned as integer rather than binary string for 'MLLT' frames
+   * Bugfix: ['filename'] now properly returned for 'GEOB' frames
+   * Bugfix: ['imagetype'] now properly returned for 'PIC' frames in
+     ID3v2.2
+   * Bugfix: Genre not being written if not set in WriteID3v1()
+     (thanks reel_tazØusers*sourceforge*net)
+   * Bugfix: Changed write mode to 'r+b' from 'a+' because ID3v1 tags
+     were being appended instead of overwritten if they already existed
+     (thanks reel_tazØusers*sourceforge*net)
+   * Bugfix: open would fail on filenames containing quotes
+     (thanks javierØcrackdealer*com)
+   * Bugfix: various values were incorrectly returned (unneeded ord())
+     in these frames: COMR, USER, ENCR, GRID, PRIV, SIGN
+   * Bugfix: ASPI ['bitsperpoint'] was not correctly returned
+   * Bugfix: RoughTranslateUnicodeToASCII() was not returning the last
+     char for UTF-16
+   * Bugfix: ['audiobytes'] now correctly 0 if no synch found
+   * Bugfix: GenreLookup was incorrectly returning 'Remix' instead of
+     'Blues' for GenreID 0
+   Added sample directory browser to check.php
+   Seperated out MPEGaudio-parsing functionality into
+     getOnlyMPEGaudioInfo() which may be called directly if you don't
+     need any ID3 parsing  (thanks djpretzelØcox*rr*com for idea)
+   Reduced use of fread() for increased performance in
+     getID3v1Filepointer()
+   Added clearstatcache() before checking filesize - size after writing
+     tag now correct
+   Added hack for mp3Rage (www.chaoticsoftware.com) that puts
+     ID3v2.3-formatted MIME type instead of 3-char ID3v2.2-format image
+     type  (thanks xbhoffØpacbell*net for test file)
+
+
+1.3.2: [February-15-2002] James Heinrich
+   ¤ UFID/UFI, USLT/ULT, COMM/COM, APIC/PIC, GEOB/GEO, CRM, RVA2, EQU2,
+     POPM/POP, AENC/CRA, ENCR and GRID frame data now returned under
+     numeric array index rather than by ownerID
+   ¤ RVA2 frame data is now returned keyed by $channeltypeid instead of
+     $frame_idstring
+   ¤ WXXX/WXX frame description now returned under ['description']
+     instead of ['data']
+   Trailing null bytes now trimmed from frame (W??? & T???) text data
+     (it shouldn't be there to begin with, but a sample file encoded by
+     [unknown program] had data padded to 50 chars with null bytes,
+     which caused ParseID3v2GenreString() to freeze).
+
+
+1.3.1: [February-13-2002] James Heinrich
+   * Bugfix: ['playtime_seconds'] and ['playtime_string'] were not
+     being returned
+   * Bugfix: ['fileformat'] was incorrectly being returned as a
+     2-element array
+   * Bugfix: USLT wasn't being correctly parsed
+   Improved RoughTranslateUnicodeToASCII()
+     (thanks reel_tazØusers*sourceforge*net for Unicode test file)
+
+
+1.3.0: [February-13-2002] James Heinrich
+   » ID3v1 writing support via WriteID3v1()
+   ¤ MPEG audio frame information (bitrate, frequency, etc) now
+     returned inside ['mpeg']
+   ¤ ['mpeg']['raw'] returns the integer values of the bits for MPEG
+     audio information as returned in ['mpeg'] by decodeheader()
+     (thanks reel_tazØusers*sourceforge*net)
+   ¤ 'protection', 'padding', 'private', 'copyright' and 'original' now
+     return as boolean
+   ¤ 'bitrate' and 'frequency' now return as int (except in special
+     case of 'free')
+   Language name as well as code retured where appropriate
+     (ie 'English' and 'eng')
+   Text frames with invalid TextEncoding value are now passed through
+     anyway
+   ID3v1 data (title, artist, album, year, comment) is now trimmed
+     (no more nulls)
+   RoughTranslateUnicodeToASCII() now uses utf8_decode() for UTF-8
+
+
+1.2.5: [January-30-2002] James Heinrich
+   * Bugfix: Playtime calculations for VBR files were off slightly
+     (rounding error)
+   * Bugfix: Extended header length was incorrectly calculated
+   * Bugfix: Genre strings such as '03' weren't being handled correctly
+   More complete support for ID3v2.3 FrameIDs
+   Split out getid3.frames.php (FrameID-specific parsing function)
+   Split out getid3.lookup.php (assorted lookup-table functions)
+   Searches for what directory getid3.*.php support files are in (must
+     be same as getid3.php, but doesn't have to be same as main file -
+     for example your main file could be /index.php, but including
+     /lib/getid3/getid3.php)
+   Simplified, tweaked, changed and/or eliminated several functions.
+
+
+1.2.4: [January-26-2002] James Heinrich
+   » Basic support for reading Ogg-Vorbis comment tags
+
+
+1.2.3: [January-24-2002] James Heinrich
+   » ID3v2.2.x 3-char FrameIDs are now fully parsed
+     Note: While I've included support for 22 FrameIDs as defined in
+     the specs, I don't have test files for all of them. If anyone
+     knows of programs that generate any of the untested tags, please
+     email getid3Øsilisoftware*com ! Here's what's tested and not:
+       Tested: T??, COM
+     Untested: UFI, TXX, W??, WXX, IPL, MCI, ETC, MLL, STC, ULT, SLT,
+               RVA, EQU, REV, PIC, GEO, CNT, POP, BUF, CRM, CRA, LNK
+   table_var_dump() now displays boolean variables as TRUE or FALSE
+   table_var_dump() now uses htmlspecialchars() to avoid broken-table
+     problems
+
+
+1.2.2: [January-18-2002] James Heinrich
+   ¤ Parses ID3v2 genres into ['id3']['id3v2']['genreid'] and
+     ['id3']['id3v2']['genrelist'] where appropriate
+     (thanks stephaneØtekartists*com for the idea)
+   Added ID3v2 genre abbreviations 'RX' (remix) and 'CR' (cover)
+
+
+1.2.1: [January-17-2002] James Heinrich
+   * Bugfix: 'mp3' was being returned in ['format'], but 'zip' was
+     being returned in ['fileformat'], both are now returned in
+     ['fileformat']
+   ¤ Splits ['id3']['id3v2']['track'] in the format '5/12' into
+     ['track'] = '5' and ['totaltracks'] = '12'
+   ¤ Enabled ['id3']['id3v2']['title'] etc for ID3v2.2.x
+     (3-char frame names)  (thanks stephaneØtekartists*com)
+   ¤ Changed v1.?? version number format to v1.?.?
+   Scans through the file until it finds the MPEG synch (start of audio
+     frame) - some files encoded by LAME 3.91 had undocumented padding
+     after the ID3v2 header; getMP3headerFilepointer() now scans until
+     it finds synch (or EOF)  (thanks adamØtrekjapan*com)
+   Improved Unicode conversion in RoughTranslateUnicodeToASCII()
+
+
+1.20:  [January-15-2002] James Heinrich
+   » Support for variable-bitrate (VBR) files, both Xing and Fraunhofer
+     headers
+   » All 4-character FrameIDs are now fully parsed according to the
+     specs at http://www.id3.org/id3v2.4.0-frames.txt
+     ¤ This means that most no longer return ['flags'] and ['data']
+     Note: While I've included support for 30 FrameIDs as defined in
+     the specs, I don't have test files for all of them. If anyone
+     knows of programs that generate any of the untested tags, please
+     email getid3Øsilisoftware*com ! Here's what's tested and not:
+       Tested: UFID, T???, WXXX, USLT, SYLT, COMM, APIC, GEOB
+     Untested: TXXX, W???, MCDI, ETCO, MLLT, SYTC, RVA2, EQU2, RVRB,
+               PCNT, POPM, RBUF, AENC, USER, OWNE, COMR, ENCR, GRID,
+               PRIV, SIGN, SEEK, ASPI
+   ¤ Added 'title', 'artist', etc names to ID3v2 data (easier to access
+     than the 4-character FrameIDs of the ID3v2 standard)
+     (thanks jaksonØgmx.net)
+   * Bugfix: added fclose() at end of GetAllMP3Info()
+     (thanks stephaneØtekartists*com)
+   * Bugfix: ID3v1 wasn't being parsed if ID3v2 wasn't present
+     (thanks jaksonØgmx.net)
+   * Bugfix: several flags were being parsed incorrectly (the structure
+     had changed from ID3v2.3 to ID3v2.4) - v2.3 flags were being
+     incorrectly parsed
+   Much more compact implementation of decodeheader()
+     (thanks jaksonØgmx.net for the idea)
+   ID3v1 genres 126 through 147  (thanks jaksonØgmx.net)
+   New table_var_dump() function in check.php
+     (based partially on idea by jaksonØgmx.net)
+   Seperated ID3v1 retrieval into seperate function
+
+
+1.11:  [December-23-2001] James Heinrich
+   All functions merged into file getid3.php
+   Updated documentation to reflect new returned information
+
+
+1.10:  [December-20-2001] James Heinrich
+   * Bugfix: ID3v1 Track# was incorrectly being parsed whether it
+     existed or not
+   Changed calling procedure to recommend using
+     GetAllMP3info($filename) from getmp3header.php
+   Now includes check.php - example file
+   ¤ Checks to see if file is in ZIP or MP3 format
+     (returned in ['format'])
+     [Ed. Note: ['fileformat'] as of v1.2.1]
+
+
+1.06:  [November-05-2001] James Heinrich
+   * Bugfix: ID3v2.2.x frames weren't being parsed since they use
+     6-byte rather than 10-byte headers as v2.3+ does
+     (thanks spunkØmac*com for pointing that out)
+
+
+1.05:  [September-06-2001] James Heinrich
+   * Bugfix: ID3v2 was being parsed even if it didn't exist
+
+
+1.04:  [July-16-2001] James Heinrich
+   * Bugfix: typo in Extended Header section (strpad() should be
+     str_pad()) (thanks jurroonØyahoo*com)
+
+
+1.03:  [May-07-2001] James Heinrich
+   * Bugfix: Added missing ['id3']['id3v1']['genreid'] and
+     ['id3']['id3v1']['genre']
+
+
+1.02:  [May-05-2001] James Heinrich
+   ¤ Added ['getID3version']
+
+
+1.01:  [May-04-2001] James Heinrich
+   » Added support for frame-level de-unsynchronisation (as per
+     ID3v2.4.0 specs) in addition to ID3v2.3.x tag-level
+     de-unsynchronisation
+
+
+1.00:  [May-04-2001] James Heinrich
+   » Initial public release
+
diff --git a/apps/media/getID3/demos/demo.audioinfo.class.php b/apps/media/getID3/demos/demo.audioinfo.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..d38ec19807fbfd4d51ab19b5fec47fb0c0877d82
--- /dev/null
+++ b/apps/media/getID3/demos/demo.audioinfo.class.php
@@ -0,0 +1,319 @@
+<?php
+
+// +----------------------------------------------------------------------+
+// | PHP version 4.1.0                                                    |
+// +----------------------------------------------------------------------+
+// | Placed in public domain by Allan Hansen, 2002. Share and enjoy!      |
+// +----------------------------------------------------------------------+
+// | /demo/demo.audioinfo.class.php                                       |
+// |                                                                      |
+// | Example wrapper class to extract information from audio files        |
+// | through getID3().                                                    |
+// |                                                                      |
+// | getID3() returns a lot of information. Much of this information is   |
+// | not needed for the end-application. It is also possible that some    |
+// | users want to extract specific info. Modifying getID3() files is a   |
+// | bad idea, as modifications needs to be done to future versions of    |
+// | getID3().                                                            |
+// |                                                                      |
+// | Modify this wrapper class instead. This example extracts certain     |
+// | fields only and adds a new root value - encoder_options if possible. |
+// | It also checks for mp3 files with wave headers.                      |
+// +----------------------------------------------------------------------+
+// | Example code:                                                        |
+// |   $au = new AudioInfo();                                             |
+// |   print_r($au->Info('file.flac');                                    |
+// +----------------------------------------------------------------------+
+// | Authors: Allan Hansen <ahØartemis*dk>                                |
+// +----------------------------------------------------------------------+
+//
+
+
+
+/**
+* getID3() settings
+*/
+
+require_once('../getid3/getid3.php');
+
+
+
+
+/**
+* Class for extracting information from audio files with getID3().
+*/
+
+class AudioInfo {
+
+	/**
+	* Private variables
+	*/
+	var $result = NULL;
+	var $info   = NULL;
+
+
+
+
+	/**
+	* Constructor
+	*/
+
+	function AudioInfo() {
+
+		// Initialize getID3 engine
+		$this->getID3 = new getID3;
+		$this->getID3->option_md5_data        = true;
+		$this->getID3->option_md5_data_source = true;
+		$this->getID3->encoding               = 'UTF-8';
+	}
+
+
+
+
+	/**
+	* Extract information - only public function
+	*
+	* @access   public
+	* @param    string  file    Audio file to extract info from.
+	*/
+
+	function Info($file) {
+
+		// Analyze file
+		$this->info = $this->getID3->analyze($file);
+
+		// Exit here on error
+		if (isset($this->info['error'])) {
+			return array ('error' => $this->info['error']);
+		}
+
+		// Init wrapper object
+		$this->result = array ();
+		$this->result['format_name']     = @$this->info['fileformat'].'/'.@$this->info['audio']['dataformat'].(isset($this->info['video']['dataformat']) ? '/'.@$this->info['video']['dataformat'] : '');
+		$this->result['encoder_version'] = @$this->info['audio']['encoder'];
+		$this->result['encoder_options'] = @$this->info['audio']['encoder_options'];
+		$this->result['bitrate_mode']    = @$this->info['audio']['bitrate_mode'];
+		$this->result['channels']        = @$this->info['audio']['channels'];
+		$this->result['sample_rate']     = @$this->info['audio']['sample_rate'];
+		$this->result['bits_per_sample'] = @$this->info['audio']['bits_per_sample'];
+		$this->result['playing_time']    = @$this->info['playtime_seconds'];
+		$this->result['avg_bit_rate']    = @$this->info['audio']['bitrate'];
+		$this->result['tags']            = @$this->info['tags'];
+		$this->result['comments']        = @$this->info['comments'];
+		$this->result['warning']         = @$this->info['warning'];
+		$this->result['md5']             = @$this->info['md5_data'];
+
+		// Post getID3() data handling based on file format
+		$method = @$this->info['fileformat'].'Info';
+		if (@$this->info['fileformat'] && method_exists($this, $method)) {
+			$this->$method();
+		}
+
+		return $this->result;
+	}
+
+
+
+
+	/**
+	* post-getID3() data handling for AAC files.
+	*
+	* @access   private
+	*/
+
+	function aacInfo() {
+		$this->result['format_name']     = 'AAC';
+	}
+
+
+
+
+	/**
+	* post-getID3() data handling for Wave files.
+	*
+	* @access   private
+	*/
+
+	function riffInfo() {
+		if ($this->info['audio']['dataformat'] == 'wav') {
+
+			$this->result['format_name'] = 'Wave';
+
+		} else if (ereg('^mp[1-3]$', $this->info['audio']['dataformat'])) {
+
+			$this->result['format_name'] = strtoupper($this->info['audio']['dataformat']);
+
+		} else {
+
+			$this->result['format_name'] = 'riff/'.$this->info['audio']['dataformat'];
+
+		}
+	}
+
+
+
+
+	/**
+	* * post-getID3() data handling for FLAC files.
+	*
+	* @access   private
+	*/
+
+	function flacInfo() {
+		$this->result['format_name']     = 'FLAC';
+	}
+
+
+
+
+
+	/**
+	* post-getID3() data handling for Monkey's Audio files.
+	*
+	* @access   private
+	*/
+
+	function macInfo() {
+		$this->result['format_name']     = 'Monkey\'s Audio';
+	}
+
+
+
+
+
+	/**
+	* post-getID3() data handling for Lossless Audio files.
+	*
+	* @access   private
+	*/
+
+	function laInfo() {
+		$this->result['format_name']     = 'La';
+	}
+
+
+
+
+
+	/**
+	* post-getID3() data handling for Ogg Vorbis files.
+	*
+	* @access   private
+	*/
+
+	function oggInfo() {
+		if ($this->info['audio']['dataformat'] == 'vorbis') {
+
+			$this->result['format_name']     = 'Ogg Vorbis';
+
+		} else if ($this->info['audio']['dataformat'] == 'flac') {
+
+			$this->result['format_name'] = 'Ogg FLAC';
+
+		} else if ($this->info['audio']['dataformat'] == 'speex') {
+
+			$this->result['format_name'] = 'Ogg Speex';
+
+		} else {
+
+			$this->result['format_name'] = 'Ogg '.$this->info['audio']['dataformat'];
+
+		}
+	}
+
+
+
+
+	/**
+	* post-getID3() data handling for Musepack files.
+	*
+	* @access   private
+	*/
+
+	function mpcInfo() {
+		$this->result['format_name']     = 'Musepack';
+	}
+
+
+
+
+	/**
+	* post-getID3() data handling for MPEG files.
+	*
+	* @access   private
+	*/
+
+	function mp3Info() {
+		$this->result['format_name']     = 'MP3';
+	}
+
+
+
+
+	/**
+	* post-getID3() data handling for MPEG files.
+	*
+	* @access   private
+	*/
+
+	function mp2Info() {
+		$this->result['format_name']     = 'MP2';
+	}
+
+
+
+
+
+	/**
+	* post-getID3() data handling for MPEG files.
+	*
+	* @access   private
+	*/
+
+	function mp1Info() {
+		$this->result['format_name']     = 'MP1';
+	}
+
+
+
+
+	/**
+	* post-getID3() data handling for WMA files.
+	*
+	* @access   private
+	*/
+
+	function asfInfo() {
+		$this->result['format_name']     = strtoupper($this->info['audio']['dataformat']);
+	}
+
+
+
+	/**
+	* post-getID3() data handling for Real files.
+	*
+	* @access   private
+	*/
+
+	function realInfo() {
+		$this->result['format_name']     = 'Real';
+	}
+
+
+
+
+
+	/**
+	* post-getID3() data handling for VQF files.
+	*
+	* @access   private
+	*/
+
+	function vqfInfo() {
+		$this->result['format_name']     = 'VQF';
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.basic.php b/apps/media/getID3/demos/demo.basic.php
new file mode 100644
index 0000000000000000000000000000000000000000..ddd56e51521ace8d35f63627a8867d1dec2a83e8
--- /dev/null
+++ b/apps/media/getID3/demos/demo.basic.php
@@ -0,0 +1,38 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.basic.php - part of getID3()                     //
+// Sample script showing most basic use of getID3()            //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+// include getID3() library (can be in a different directory if full path is specified)
+require_once('../getid3/getid3.php');
+
+// Initialize getID3 engine
+$getID3 = new getID3;
+
+// Analyze file and store returned data in $ThisFileInfo
+$ThisFileInfo = $getID3->analyze($filename);
+
+// Optional: copies data from all subarrays of [tags] into [comments] so
+// metadata is all available in one location for all tag formats
+// metainformation is always available under [tags] even if this is not called
+getid3_lib::CopyTagsToComments($ThisFileInfo);
+
+// Output desired information in whatever format you want
+// Note: all entries in [comments] or [tags] are arrays of strings
+// See structure.txt for information on what information is available where
+// or check out the output of /demos/demo.browse.php for a particular file
+// to see the full detail of what information is returned where in the array
+echo @$ThisFileInfo['comments_html']['artist'][0]; // artist from any/all available tag formats
+echo @$ThisFileInfo['tags']['id3v2']['title'][0];  // title from ID3v2
+echo @$ThisFileInfo['audio']['bitrate'];           // audio bitrate
+echo @$ThisFileInfo['playtime_string'];            // playtime in minutes:seconds, formatted string
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.browse.php b/apps/media/getID3/demos/demo.browse.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d027b63b2fc5bcda6bd17866693f7c8bf48c232
--- /dev/null
+++ b/apps/media/getID3/demos/demo.browse.php
@@ -0,0 +1,679 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.browse.php - part of getID3()                     //
+// Sample script for browsing/scanning files and displaying    //
+// information returned by getID3()                            //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+//die('Due to a security issue, this demo has been disabled. It can be enabled by removing line '.__LINE__.' in demos/'.basename(__FILE__));
+
+
+/////////////////////////////////////////////////////////////////
+// set predefined variables as if magic_quotes_gpc was off,
+// whether the server's got it or not:
+UnifyMagicQuotes(false);
+/////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////
+// showfile is used to display embedded images from table_var_dump()
+// md5 of requested file is required to prevent abuse where any
+// random file on the server could be viewed
+if (@$_REQUEST['showfile']) {
+	if (is_readable($_REQUEST['showfile'])) {
+		if (md5_file($_REQUEST['showfile']) == @$_REQUEST['md5']) {
+			readfile($_REQUEST['showfile']);
+			exit;
+		}
+	}
+	die('Cannot display "'.$_REQUEST['showfile'].'"');
+}
+/////////////////////////////////////////////////////////////////
+
+
+if (!function_exists('getmicrotime')) {
+	function getmicrotime() {
+		list($usec, $sec) = explode(' ', microtime());
+		return ((float) $usec + (float) $sec);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+$writescriptfilename = 'demo.write.php';
+
+require_once('../getid3/getid3.php');
+
+// Needed for windows only
+define('GETID3_HELPERAPPSDIR', 'C:/helperapps/');
+
+// Initialize getID3 engine
+$getID3 = new getID3;
+$getID3->setOption(array('encoding' => 'UTF-8'));
+
+$getID3checkColor_Head           = 'CCCCDD';
+$getID3checkColor_DirectoryLight = 'FFCCCC';
+$getID3checkColor_DirectoryDark  = 'EEBBBB';
+$getID3checkColor_FileLight      = 'EEEEEE';
+$getID3checkColor_FileDark       = 'DDDDDD';
+$getID3checkColor_UnknownLight   = 'CCCCFF';
+$getID3checkColor_UnknownDark    = 'BBBBDD';
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+header('Content-Type: text/html; charset=UTF-8');
+ob_start();
+echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+echo '<html><head>';
+echo '<title>getID3() - /demo/demo.browse.php (sample script)</title>';
+echo '<link rel="stylesheet" href="getid3.css" type="text/css">';
+echo '</head><body>';
+
+if (isset($_REQUEST['deletefile'])) {
+	if (file_exists($_REQUEST['deletefile'])) {
+		if (unlink($_REQUEST['deletefile'])) {
+			$deletefilemessage = 'Successfully deleted '.addslashes($_REQUEST['deletefile']);
+		} else {
+			$deletefilemessage = 'FAILED to delete '.addslashes($_REQUEST['deletefile']).' - error deleting file';
+		}
+	} else {
+		$deletefilemessage = 'FAILED to delete '.addslashes($_REQUEST['deletefile']).' - file does not exist';
+	}
+	if (isset($_REQUEST['noalert'])) {
+		echo '<b><font color="'.(($deletefilemessage{0} == 'F') ? '#FF0000' : '#008000').'">'.$deletefilemessage.'</font></b><hr>';
+	} else {
+		echo '<script type="text/javascript">alert("'.$deletefilemessage.'");</script>';
+	}
+}
+
+
+if (isset($_REQUEST['filename'])) {
+
+	if (!file_exists($_REQUEST['filename']) || !is_file($_REQUEST['filename'])) {
+		die(getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-8', $_REQUEST['filename'].' does not exist'));
+	}
+	$starttime = getmicrotime();
+
+	//$getID3->setOption(array(
+	//	'option_md5_data'  => $AutoGetHashes,
+	//	'option_sha1_data' => $AutoGetHashes,
+	//));
+	$ThisFileInfo = $getID3->analyze($_REQUEST['filename']);
+	$AutoGetHashes = (bool) ((@$ThisFileInfo['filesize'] > 0) && ($ThisFileInfo['filesize'] < (50 * 1048576))); // auto-get md5_data, md5_file, sha1_data, sha1_file if filesize < 50MB, and NOT zero (which may indicate a file>2GB)
+	if ($AutoGetHashes) {
+		$ThisFileInfo['md5_file']  = getid3_lib::md5_file($_REQUEST['filename']);
+		$ThisFileInfo['sha1_file'] = getid3_lib::sha1_file($_REQUEST['filename']);
+	}
+
+
+	getid3_lib::CopyTagsToComments($ThisFileInfo);
+
+	$listdirectory = dirname(getid3_lib::SafeStripSlashes($_REQUEST['filename']));
+	$listdirectory = realpath($listdirectory); // get rid of /../../ references
+
+	if (GETID3_OS_ISWINDOWS) {
+		// this mostly just gives a consistant look to Windows and *nix filesystems
+		// (windows uses \ as directory seperator, *nix uses /)
+		$listdirectory = str_replace('\\', '/', $listdirectory.'/');
+	}
+
+	if (strstr($_REQUEST['filename'], 'http://') || strstr($_REQUEST['filename'], 'ftp://')) {
+		echo '<i>Cannot browse remote filesystems</i><br>';
+	} else {
+		echo 'Browse: <a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'">'.getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-8', $listdirectory).'</a><br>';
+	}
+
+	echo table_var_dump($ThisFileInfo);
+	$endtime = getmicrotime();
+	echo 'File parsed in '.number_format($endtime - $starttime, 3).' seconds.<br>';
+
+} else {
+
+	$listdirectory = (isset($_REQUEST['listdirectory']) ? getid3_lib::SafeStripSlashes($_REQUEST['listdirectory']) : '.');
+	$listdirectory = realpath($listdirectory); // get rid of /../../ references
+	$currentfulldir = $listdirectory.'/';
+
+	if (GETID3_OS_ISWINDOWS) {
+		// this mostly just gives a consistant look to Windows and *nix filesystems
+		// (windows uses \ as directory seperator, *nix uses /)
+		$currentfulldir = str_replace('\\', '/', $listdirectory.'/');
+	}
+
+	if ($handle = @opendir($listdirectory)) {
+
+		echo str_repeat(' ', 300); // IE buffers the first 300 or so chars, making this progressive display useless - fill the buffer with spaces
+		echo 'Processing';
+
+		$starttime = getmicrotime();
+
+		$TotalScannedUnknownFiles  = 0;
+		$TotalScannedKnownFiles    = 0;
+		$TotalScannedPlaytimeFiles = 0;
+		$TotalScannedBitrateFiles  = 0;
+		$TotalScannedFilesize      = 0;
+		$TotalScannedPlaytime      = 0;
+		$TotalScannedBitrate       = 0;
+		$FilesWithWarnings         = 0;
+		$FilesWithErrors           = 0;
+
+		while ($file = readdir($handle)) {
+			$currentfilename = $listdirectory.'/'.$file;
+			set_time_limit(30); // allocate another 30 seconds to process this file - should go much quicker than this unless intense processing (like bitrate histogram analysis) is enabled
+			echo ' .'; // progress indicator dot
+			flush();  // make sure the dot is shown, otherwise it's useless
+
+			switch ($file) {
+				case '..':
+					$ParentDir = realpath($file.'/..').'/';
+					if (GETID3_OS_ISWINDOWS) {
+						$ParentDir = str_replace('\\', '/', $ParentDir);
+					}
+					$DirectoryContents[$currentfulldir]['dir'][$file]['filename'] = $ParentDir;
+					continue 2;
+					break;
+
+				case '.':
+					// ignore
+					continue 2;
+					break;
+			}
+
+			// symbolic-link-resolution enhancements by davidbullock×´ech-center*com
+			$TargetObject     = realpath($currentfilename);  // Find actual file path, resolve if it's a symbolic link
+			$TargetObjectType = filetype($TargetObject);     // Check file type without examining extension
+
+			if ($TargetObjectType == 'dir') {
+
+				$DirectoryContents[$currentfulldir]['dir'][$file]['filename'] = $file;
+
+			} elseif ($TargetObjectType == 'file') {
+
+				$getID3->setOption(array('option_md5_data' => isset($_REQUEST['ShowMD5'])));
+				$fileinformation = $getID3->analyze($currentfilename);
+
+				getid3_lib::CopyTagsToComments($fileinformation);
+
+				$TotalScannedFilesize += @$fileinformation['filesize'];
+
+				if (isset($_REQUEST['ShowMD5'])) {
+					$fileinformation['md5_file'] = md5($currentfilename);
+					$fileinformation['md5_file']  = getid3_lib::md5_file($currentfilename);
+				}
+
+				if (!empty($fileinformation['fileformat'])) {
+					$DirectoryContents[$currentfulldir]['known'][$file] = $fileinformation;
+					$TotalScannedPlaytime += @$fileinformation['playtime_seconds'];
+					$TotalScannedBitrate  += @$fileinformation['bitrate'];
+					$TotalScannedKnownFiles++;
+				} else {
+					$DirectoryContents[$currentfulldir]['other'][$file] = $fileinformation;
+					$DirectoryContents[$currentfulldir]['other'][$file]['playtime_string'] = '-';
+					$TotalScannedUnknownFiles++;
+				}
+				if (isset($fileinformation['playtime_seconds']) && ($fileinformation['playtime_seconds'] > 0)) {
+					$TotalScannedPlaytimeFiles++;
+				}
+				if (isset($fileinformation['bitrate']) && ($fileinformation['bitrate'] > 0)) {
+					$TotalScannedBitrateFiles++;
+				}
+			}
+		}
+		$endtime = getmicrotime();
+		closedir($handle);
+		echo 'done<br>';
+		echo 'Directory scanned in '.number_format($endtime - $starttime, 2).' seconds.<br>';
+		flush();
+
+		$columnsintable = 14;
+		echo '<table class="table" cellspacing="0" cellpadding="3">';
+
+		echo '<tr bgcolor="#'.$getID3checkColor_Head.'"><th colspan="'.$columnsintable.'">Files in '.getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-8', $currentfulldir).'</th></tr>';
+		$rowcounter = 0;
+		foreach ($DirectoryContents as $dirname => $val) {
+			if (isset($DirectoryContents[$dirname]['dir']) && is_array($DirectoryContents[$dirname]['dir'])) {
+				uksort($DirectoryContents[$dirname]['dir'], 'MoreNaturalSort');
+				foreach ($DirectoryContents[$dirname]['dir'] as $filename => $fileinfo) {
+					echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_DirectoryLight : $getID3checkColor_DirectoryDark).'">';
+					if ($filename == '..') {
+						echo '<td colspan="'.$columnsintable.'">';
+						echo '<form action="'.$_SERVER['PHP_SELF'].'" method="get">';
+						echo 'Parent directory: ';
+						echo '<input type="text" name="listdirectory" size="50" style="background-color: '.$getID3checkColor_DirectoryDark.';" value="';
+						if (GETID3_OS_ISWINDOWS) {
+							echo htmlentities(str_replace('\\', '/', realpath($dirname.$filename)), ENT_QUOTES);
+						} else {
+							echo htmlentities(realpath($dirname.$filename), ENT_QUOTES);
+						}
+						echo '"> <input type="submit" value="Go">';
+						echo '</form></td>';
+					} else {
+						echo '<td colspan="'.$columnsintable.'"><a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($dirname.$filename).'"><b>'.FixTextFields($filename).'</b></a></td>';
+					}
+					echo '</tr>';
+				}
+			}
+
+			echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
+			echo '<th>Filename</th>';
+			echo '<th>File Size</th>';
+			echo '<th>Format</th>';
+			echo '<th>Playtime</th>';
+			echo '<th>Bitrate</th>';
+			echo '<th>Artist</th>';
+			echo '<th>Title</th>';
+			if (isset($_REQUEST['ShowMD5'])) {
+				echo '<th>MD5&nbsp;File (File) (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'">disable</a>)</th>';
+				echo '<th>MD5&nbsp;Data (File) (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'">disable</a>)</th>';
+				echo '<th>MD5&nbsp;Data (Source) (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'">disable</a>)</th>';
+			} else {
+				echo '<th colspan="3">MD5&nbsp;Data (<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.rawurlencode(isset($_REQUEST['listdirectory']) ? $_REQUEST['listdirectory'] : '.').'&amp;ShowMD5=1">enable</a>)</th>';
+			}
+			echo '<th>Tags</th>';
+			echo '<th>Errors &amp; Warnings</th>';
+			echo '<th>Edit</th>';
+			echo '<th>Delete</th>';
+			echo '</tr>';
+
+			if (isset($DirectoryContents[$dirname]['known']) && is_array($DirectoryContents[$dirname]['known'])) {
+				uksort($DirectoryContents[$dirname]['known'], 'MoreNaturalSort');
+				foreach ($DirectoryContents[$dirname]['known'] as $filename => $fileinfo) {
+					echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_FileDark : $getID3checkColor_FileLight).'">';
+					echo '<td><a href="'.$_SERVER['PHP_SELF'].'?filename='.urlencode($dirname.$filename).'" title="View detailed analysis">'.FixTextFields(getid3_lib::SafeStripSlashes($filename)).'</a></td>';
+					echo '<td align="right">&nbsp;'.number_format($fileinfo['filesize']).'</td>';
+					echo '<td align="right">&nbsp;'.NiceDisplayFiletypeFormat($fileinfo).'</td>';
+					echo '<td align="right">&nbsp;'.(isset($fileinfo['playtime_string']) ? $fileinfo['playtime_string'] : '-').'</td>';
+					echo '<td align="right">&nbsp;'.(isset($fileinfo['bitrate']) ? BitrateText($fileinfo['bitrate'] / 1000, 0, ((@$fileinfo['audio']['bitrate_mode'] == 'vbr') ? true : false)) : '-').'</td>';
+					echo '<td align="left">&nbsp;'.(isset($fileinfo['comments_html']['artist']) ? implode('<br>', $fileinfo['comments_html']['artist']) : '').'</td>';
+					echo '<td align="left">&nbsp;'.(isset($fileinfo['comments_html']['title']) ? implode('<br>', $fileinfo['comments_html']['title']) : '').'</td>';
+					if (isset($_REQUEST['ShowMD5'])) {
+						echo '<td align="left"><tt>'.(isset($fileinfo['md5_file'])        ? $fileinfo['md5_file']        : '&nbsp;').'</tt></td>';
+						echo '<td align="left"><tt>'.(isset($fileinfo['md5_data'])        ? $fileinfo['md5_data']        : '&nbsp;').'</tt></td>';
+						echo '<td align="left"><tt>'.(isset($fileinfo['md5_data_source']) ? $fileinfo['md5_data_source'] : '&nbsp;').'</tt></td>';
+					} else {
+						echo '<td align="center" colspan="3">-</td>';
+					}
+					echo '<td align="left">&nbsp;'.@implode(', ', array_keys($fileinfo['tags'])).'</td>';
+
+					echo '<td align="left">&nbsp;';
+					if (!empty($fileinfo['warning'])) {
+						$FilesWithWarnings++;
+						echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['warning'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['warning'])).'">warning</a><br>';
+					}
+					if (!empty($fileinfo['error'])) {
+						$FilesWithErrors++;
+						echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['error'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['error'])).'">error</a><br>';
+					}
+					echo '</td>';
+
+					echo '<td align="left">&nbsp;';
+					switch (@$fileinfo['fileformat']) {
+						case 'mp3':
+						case 'mp2':
+						case 'mp1':
+						case 'flac':
+						case 'mpc':
+						case 'real':
+							echo '<a href="'.$writescriptfilename.'?Filename='.urlencode($dirname.$filename).'" title="Edit tags">edit&nbsp;tags</a>';
+							break;
+						case 'ogg':
+							switch (@$fileinfo['audio']['dataformat']) {
+								case 'vorbis':
+									echo '<a href="'.$writescriptfilename.'?Filename='.urlencode($dirname.$filename).'" title="Edit tags">edit&nbsp;tags</a>';
+									break;
+							}
+							break;
+						default:
+							break;
+					}
+					echo '</td>';
+					echo '<td align="left">&nbsp;<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'&amp;deletefile='.urlencode($dirname.$filename).'" onClick="return confirm(\'Are you sure you want to delete '.addslashes(htmlentities($dirname.$filename)).'? \n(this action cannot be un-done)\');" title="Permanently delete '."\n".FixTextFields($filename)."\n".' from'."\n".' '.FixTextFields($dirname).'">delete</a></td>';
+					echo '</tr>';
+				}
+			}
+
+			if (isset($DirectoryContents[$dirname]['other']) && is_array($DirectoryContents[$dirname]['other'])) {
+				uksort($DirectoryContents[$dirname]['other'], 'MoreNaturalSort');
+				foreach ($DirectoryContents[$dirname]['other'] as $filename => $fileinfo) {
+					echo '<tr bgcolor="#'.(($rowcounter++ % 2) ? $getID3checkColor_UnknownDark : $getID3checkColor_UnknownLight).'">';
+					echo '<td><a href="'.$_SERVER['PHP_SELF'].'?filename='.urlencode($dirname.$filename).'"><i>'.$filename.'</i></a></td>';
+					echo '<td align="right">&nbsp;'.(isset($fileinfo['filesize']) ? number_format($fileinfo['filesize']) : '-').'</td>';
+					echo '<td align="right">&nbsp;'.NiceDisplayFiletypeFormat($fileinfo).'</td>';
+					echo '<td align="right">&nbsp;'.(isset($fileinfo['playtime_string']) ? $fileinfo['playtime_string'] : '-').'</td>';
+					echo '<td align="right">&nbsp;'.(isset($fileinfo['bitrate']) ? BitrateText($fileinfo['bitrate'] / 1000) : '-').'</td>';
+					echo '<td align="left">&nbsp;</td>'; // Artist
+					echo '<td align="left">&nbsp;</td>'; // Title
+					echo '<td align="left" colspan="3">&nbsp;</td>'; // MD5_data
+					echo '<td align="left">&nbsp;</td>'; // Tags
+
+					//echo '<td align="left">&nbsp;</td>'; // Warning/Error
+					echo '<td align="left">&nbsp;';
+					if (!empty($fileinfo['warning'])) {
+						$FilesWithWarnings++;
+						echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['warning'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['warning'])).'">warning</a><br>';
+					}
+					if (!empty($fileinfo['error'])) {
+						if ($fileinfo['error'][0] != 'unable to determine file format') {
+							$FilesWithErrors++;
+							echo '<a href="#" onClick="alert(\''.FixTextFields(implode('\\n', $fileinfo['error'])).'\'); return false;" title="'.FixTextFields(implode("\n", $fileinfo['error'])).'">error</a><br>';
+						}
+					}
+					echo '</td>';
+
+					echo '<td align="left">&nbsp;</td>'; // Edit
+					echo '<td align="left">&nbsp;<a href="'.$_SERVER['PHP_SELF'].'?listdirectory='.urlencode($listdirectory).'&amp;deletefile='.urlencode($dirname.$filename).'" onClick="return confirm(\'Are you sure you want to delete '.addslashes($dirname.$filename).'? \n(this action cannot be un-done)\');" title="Permanently delete '.addslashes($dirname.$filename).'">delete</a></td>';
+					echo '</tr>';
+				}
+			}
+
+			echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
+			echo '<td><b>Average:</b></td>';
+			echo '<td align="right">'.number_format($TotalScannedFilesize / max($TotalScannedKnownFiles, 1)).'</td>';
+			echo '<td>&nbsp;</td>';
+			echo '<td align="right">'.getid3_lib::PlaytimeString($TotalScannedPlaytime / max($TotalScannedPlaytimeFiles, 1)).'</td>';
+			echo '<td align="right">'.BitrateText(round(($TotalScannedBitrate / 1000) / max($TotalScannedBitrateFiles, 1))).'</td>';
+			echo '<td rowspan="2" colspan="'.($columnsintable - 5).'"><table class="table" border="0" cellspacing="0" cellpadding="2"><tr><th align="right">Identified Files:</th><td align="right">'.number_format($TotalScannedKnownFiles).'</td><td>&nbsp;&nbsp;&nbsp;</td><th align="right">Errors:</th><td align="right">'.number_format($FilesWithErrors).'</td></tr><tr><th align="right">Unknown Files:</th><td align="right">'.number_format($TotalScannedUnknownFiles).'</td><td>&nbsp;&nbsp;&nbsp;</td><th align="right">Warnings:</th><td align="right">'.number_format($FilesWithWarnings).'</td></tr></table>';
+			echo '</tr>';
+			echo '<tr bgcolor="#'.$getID3checkColor_Head.'">';
+			echo '<td><b>Total:</b></td>';
+			echo '<td align="right">'.number_format($TotalScannedFilesize).'</td>';
+			echo '<td>&nbsp;</td>';
+			echo '<td align="right">'.getid3_lib::PlaytimeString($TotalScannedPlaytime).'</td>';
+			echo '<td>&nbsp;</td>';
+			echo '</tr>';
+		}
+		echo '</table>';
+	} else {
+		echo '<b>ERROR: Could not open directory: <u>'.$currentfulldir.'</u></b><br>';
+	}
+}
+echo PoweredBygetID3();
+echo 'Running on PHP v'.phpversion();
+echo '</body></html>';
+ob_end_flush();
+
+
+/////////////////////////////////////////////////////////////////
+
+
+function RemoveAccents($string) {
+	// Revised version by markstewardרotmail*com
+	// Again revised by James Heinrich (19-June-2006)
+	return strtr(
+		strtr(
+			$string,
+			"\x8A\x8E\x9A\x9E\x9F\xC0\xC1\xC2\xC3\xC4\xC5\xC7\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF\xD1\xD2\xD3\xD4\xD5\xD6\xD8\xD9\xDA\xDB\xDC\xDD\xE0\xE1\xE2\xE3\xE4\xE5\xE7\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF\xF1\xF2\xF3\xF4\xF5\xF6\xF8\xF9\xFA\xFB\xFC\xFD\xFF",
+			'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'
+		),
+		array(
+			"\xDE" => 'TH',
+			"\xFE" => 'th',
+			"\xD0" => 'DH',
+			"\xF0" => 'dh',
+			"\xDF" => 'ss',
+			"\x8C" => 'OE',
+			"\x9C" => 'oe',
+			"\xC6" => 'AE',
+			"\xE6" => 'ae',
+			"\xB5" => 'u'
+		)
+	);
+}
+
+
+function BitrateColor($bitrate, $BitrateMaxScale=768) {
+	// $BitrateMaxScale is bitrate of maximum-quality color (bright green)
+	// below this is gradient, above is solid green
+
+	$bitrate *= (256 / $BitrateMaxScale); // scale from 1-[768]kbps to 1-256
+	$bitrate = round(min(max($bitrate, 1), 256));
+	$bitrate--;    // scale from 1-256kbps to 0-255kbps
+
+	$Rcomponent = max(255 - ($bitrate * 2), 0);
+	$Gcomponent = max(($bitrate * 2) - 255, 0);
+	if ($bitrate > 127) {
+		$Bcomponent = max((255 - $bitrate) * 2, 0);
+	} else {
+		$Bcomponent = max($bitrate * 2, 0);
+	}
+	return str_pad(dechex($Rcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Gcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Bcomponent), 2, '0', STR_PAD_LEFT);
+}
+
+function BitrateText($bitrate, $decimals=0, $vbr=false) {
+	return '<SPAN STYLE="color: #'.BitrateColor($bitrate).($vbr ? '; font-weight: bold;' : '').'">'.number_format($bitrate, $decimals).' kbps</SPAN>';
+}
+
+function FixTextFields($text) {
+	$text = getid3_lib::SafeStripSlashes($text);
+	$text = htmlentities($text, ENT_QUOTES);
+	return $text;
+}
+
+
+function string_var_dump($variable) {
+	ob_start();
+	var_dump($variable);
+	$dumpedvariable = ob_get_contents();
+	ob_end_clean();
+	return $dumpedvariable;
+}
+
+
+function table_var_dump($variable, $wrap_in_td=false) {
+	$returnstring = '';
+	switch (gettype($variable)) {
+		case 'array':
+			$returnstring .= ($wrap_in_td ? '<td>' : '');
+			$returnstring .= '<table class="dump" cellspacing="0" cellpadding="2">';
+			foreach ($variable as $key => $value) {
+				$returnstring .= '<tr><td valign="top"><b>'.str_replace("\x00", ' ', $key).'</b></td>';
+				$returnstring .= '<td valign="top">'.gettype($value);
+				if (is_array($value)) {
+					$returnstring .= '&nbsp;('.count($value).')';
+				} elseif (is_string($value)) {
+					$returnstring .= '&nbsp;('.strlen($value).')';
+				}
+				if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) {
+					$imageinfo = array();
+					$imagechunkcheck = getid3_lib::GetDataImageSize($value, $imageinfo);
+					$DumpedImageSRC = (!empty($_REQUEST['filename']) ? $_REQUEST['filename'] : '.getid3').'.'.$variable['dataoffset'].'.'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
+					if ($tempimagefile = @fopen($DumpedImageSRC, 'wb')) {
+						fwrite($tempimagefile, $value);
+						fclose($tempimagefile);
+					}
+					$returnstring .= '</td><td><img src="'.$_SERVER['PHP_SELF'].'?showfile='.urlencode($DumpedImageSRC).'&md5='.md5_file($DumpedImageSRC).'" width="'.$imagechunkcheck[0].'" height="'.$imagechunkcheck[1].'"></td></tr>';
+				} else {
+					$returnstring .= '</td>'.table_var_dump($value, true).'</tr>';
+				}
+			}
+			$returnstring .= '</table>';
+			$returnstring .= ($wrap_in_td ? '</td>' : '');
+			break;
+
+		case 'boolean':
+			$returnstring .= ($wrap_in_td ? '<td class="dump_boolean">' : '').($variable ? 'TRUE' : 'FALSE').($wrap_in_td ? '</td>' : '');
+			break;
+
+		case 'integer':
+			$returnstring .= ($wrap_in_td ? '<td class="dump_integer">' : '').$variable.($wrap_in_td ? '</td>' : '');
+			break;
+
+		case 'double':
+		case 'float':
+			$returnstring .= ($wrap_in_td ? '<td class="dump_double">' : '').$variable.($wrap_in_td ? '</td>' : '');
+			break;
+
+		case 'object':
+		case 'null':
+			$returnstring .= ($wrap_in_td ? '<td>' : '').string_var_dump($variable).($wrap_in_td ? '</td>' : '');
+			break;
+
+		case 'string':
+			$variable = str_replace("\x00", ' ', $variable);
+			$varlen = strlen($variable);
+			for ($i = 0; $i < $varlen; $i++) {
+				if (ereg('['."\x0A\x0D".' -;0-9A-Za-z]', $variable{$i})) {
+					$returnstring .= $variable{$i};
+				} else {
+					$returnstring .= '&#'.str_pad(ord($variable{$i}), 3, '0', STR_PAD_LEFT).';';
+				}
+			}
+			$returnstring = ($wrap_in_td ? '<td class="dump_string">' : '').nl2br($returnstring).($wrap_in_td ? '</td>' : '');
+			break;
+
+		default:
+			$imageinfo = array();
+			$imagechunkcheck = getid3_lib::GetDataImageSize($variable, $imageinfo);
+			if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
+				$returnstring .= ($wrap_in_td ? '<td>' : '');
+				$returnstring .= '<table class="dump" cellspacing="0" cellpadding="2">';
+				$returnstring .= '<tr><td><b>type</b></td><td>'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]).'</td></tr>';
+				$returnstring .= '<tr><td><b>width</b></td><td>'.number_format($imagechunkcheck[0]).' px</td></tr>';
+				$returnstring .= '<tr><td><b>height</b></td><td>'.number_format($imagechunkcheck[1]).' px</td></tr>';
+				$returnstring .= '<tr><td><b>size</b></td><td>'.number_format(strlen($variable)).' bytes</td></tr></table>';
+				$returnstring .= ($wrap_in_td ? '</td>' : '');
+			} else {
+				$returnstring .= ($wrap_in_td ? '<td>' : '').nl2br(htmlspecialchars(str_replace("\x00", ' ', $variable))).($wrap_in_td ? '</td>' : '');
+			}
+			break;
+	}
+	return $returnstring;
+}
+
+
+function NiceDisplayFiletypeFormat(&$fileinfo) {
+
+	if (empty($fileinfo['fileformat'])) {
+		return '-';
+	}
+
+	$output  = $fileinfo['fileformat'];
+	if (empty($fileinfo['video']['dataformat']) && empty($fileinfo['audio']['dataformat'])) {
+		return $output;  // 'gif'
+	}
+	if (empty($fileinfo['video']['dataformat']) && !empty($fileinfo['audio']['dataformat'])) {
+		if ($fileinfo['fileformat'] == $fileinfo['audio']['dataformat']) {
+			return $output; // 'mp3'
+		}
+		$output .= '.'.$fileinfo['audio']['dataformat']; // 'ogg.flac'
+		return $output;
+	}
+	if (!empty($fileinfo['video']['dataformat']) && empty($fileinfo['audio']['dataformat'])) {
+		if ($fileinfo['fileformat'] == $fileinfo['video']['dataformat']) {
+			return $output; // 'mpeg'
+		}
+		$output .= '.'.$fileinfo['video']['dataformat']; // 'riff.avi'
+		return $output;
+	}
+	if ($fileinfo['video']['dataformat'] == $fileinfo['audio']['dataformat']) {
+		if ($fileinfo['fileformat'] == $fileinfo['video']['dataformat']) {
+			return $output; // 'real'
+		}
+		$output .= '.'.$fileinfo['video']['dataformat']; // any examples?
+		return $output;
+	}
+	$output .= '.'.$fileinfo['video']['dataformat'];
+	$output .= '.'.$fileinfo['audio']['dataformat']; // asf.wmv.wma
+	return $output;
+
+}
+
+function MoreNaturalSort($ar1, $ar2) {
+	if ($ar1 === $ar2) {
+		return 0;
+	}
+	$len1     = strlen($ar1);
+	$len2     = strlen($ar2);
+	$shortest = min($len1, $len2);
+	if (substr($ar1, 0, $shortest) === substr($ar2, 0, $shortest)) {
+		// the shorter argument is the beginning of the longer one, like "str" and "string"
+		if ($len1 < $len2) {
+			return -1;
+		} elseif ($len1 > $len2) {
+			return 1;
+		}
+		return 0;
+	}
+	$ar1 = RemoveAccents(strtolower(trim($ar1)));
+	$ar2 = RemoveAccents(strtolower(trim($ar2)));
+	$translatearray = array('\''=>'', '"'=>'', '_'=>' ', '('=>'', ')'=>'', '-'=>' ', '  '=>' ', '.'=>'', ','=>'');
+	foreach ($translatearray as $key => $val) {
+		$ar1 = str_replace($key, $val, $ar1);
+		$ar2 = str_replace($key, $val, $ar2);
+	}
+
+	if ($ar1 < $ar2) {
+		return -1;
+	} elseif ($ar1 > $ar2) {
+		return 1;
+	}
+	return 0;
+}
+
+function PoweredBygetID3($string='<br><HR NOSHADE><DIV STYLE="font-size: 8pt; font-face: sans-serif;">Powered by <a href="http://getid3.sourceforge.net" TARGET="_blank"><b>getID3() v<!--GETID3VER--></b><br>http://getid3.sourceforge.net</a></DIV>') {
+	return str_replace('<!--GETID3VER-->', GETID3_VERSION, $string);
+}
+
+
+/////////////////////////////////////////////////////////////////
+// Unify the contents of GPC,
+// whether magic_quotes_gpc is on or off
+
+function AddStripSlashesArray($input, $addslashes=false) {
+	if (is_array($input)) {
+
+		$output = $input;
+		foreach ($input as $key => $value) {
+			$output[$key] = AddStripSlashesArray($input[$key]);
+		}
+		return $output;
+
+	} elseif ($addslashes) {
+		return addslashes($input);
+	}
+	return stripslashes($input);
+}
+
+function UnifyMagicQuotes($turnon=false) {
+	global $HTTP_GET_VARS, $HTTP_POST_VARS, $HTTP_COOKIE_VARS;
+
+	if (get_magic_quotes_gpc() && !$turnon) {
+
+		// magic_quotes_gpc is on and we want it off!
+		$_GET    = AddStripSlashesArray($_GET,    true);
+		$_POST   = AddStripSlashesArray($_POST,   true);
+		$_COOKIE = AddStripSlashesArray($_COOKIE, true);
+
+		unset($_REQUEST);
+		$_REQUEST = array_merge_recursive($_GET, $_POST, $_COOKIE);
+
+	} elseif (!get_magic_quotes_gpc() && $turnon) {
+
+		// magic_quotes_gpc is off and we want it on (why??)
+		$_GET    = AddStripSlashesArray($_GET,    true);
+		$_POST   = AddStripSlashesArray($_POST,   true);
+		$_COOKIE = AddStripSlashesArray($_COOKIE, true);
+
+		unset($_REQUEST);
+		$_REQUEST = array_merge_recursive($_GET, $_POST, $_COOKIE);
+
+	}
+	$HTTP_GET_VARS    = $_GET;
+	$HTTP_POST_VARS   = $_POST;
+	$HTTP_COOKIE_VARS = $_COOKIE;
+
+	return true;
+}
+/////////////////////////////////////////////////////////////////
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.cache.dbm.php b/apps/media/getID3/demos/demo.cache.dbm.php
new file mode 100644
index 0000000000000000000000000000000000000000..acaaa0f3f2f0a8156204957597daf8f42e07a5ba
--- /dev/null
+++ b/apps/media/getID3/demos/demo.cache.dbm.php
@@ -0,0 +1,29 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.cache.dbm.php - part of getID3()                 //
+// Sample script demonstrating the use of the DBM caching      //
+// extension for getID3()                                      //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+require_once('../getid3/getid3.php');
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.dbm.php', __FILE__, true);
+
+$getID3 = new getID3_cached_dbm('db3', '/zimweb/test/test.dbm', '/zimweb/test/test.lock');
+
+$r = $getID3->analyze('/path/to/files/filename.mp3');
+
+echo '<pre>';
+var_dump($r);
+echo '</pre>';
+
+// uncomment to clear cache
+// $getID3->clear_cache();
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.cache.mysql.php b/apps/media/getID3/demos/demo.cache.mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..537b2f0ceb6f081dabbb80f26f2748335e54b1aa
--- /dev/null
+++ b/apps/media/getID3/demos/demo.cache.mysql.php
@@ -0,0 +1,29 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.cache.mysql.php - part of getID3()               //
+// Sample script demonstrating the use of the DBM caching      //
+// extension for getID3()                                      //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+require_once('../getid3/getid3.php');
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'extension.cache.mysql.php', __FILE__, true);
+
+$getID3 = new getID3_cached_mysql('localhost', 'database', 'username', 'password');
+
+$r = $getID3->analyze('/path/to/files/filename.mp3');
+
+echo '<pre>';
+var_dump($r);
+echo '</pre>';
+
+// uncomment to clear cache
+//$getID3->clear_cache();
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.joinmp3.php b/apps/media/getID3/demos/demo.joinmp3.php
new file mode 100644
index 0000000000000000000000000000000000000000..976884f92ebddc2d43f8ea08b528e02a62edea53
--- /dev/null
+++ b/apps/media/getID3/demos/demo.joinmp3.php
@@ -0,0 +1,96 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.joinmp3.php - part of getID3()                   //
+// Sample script for splicing two or more MP3s together into   //
+// one file. Does not attempt to fix VBR header frames.        //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+// sample usage:
+// $FilenameOut   = 'combined.mp3';
+// $FilenamesIn[] = 'file1.mp3';
+// $FilenamesIn[] = 'file2.mp3';
+// $FilenamesIn[] = 'file3.mp3';
+//
+// if (CombineMultipleMP3sTo($FilenameOut, $FilenamesIn)) {
+//     echo 'Successfully copied '.implode(' + ', $FilenamesIn).' to '.$FilenameOut;
+// } else {
+//     echo 'Failed to copy '.implode(' + ', $FilenamesIn).' to '.$FilenameOut;
+// }
+
+function CombineMultipleMP3sTo($FilenameOut, $FilenamesIn) {
+
+	foreach ($FilenamesIn as $nextinputfilename) {
+		if (!is_readable($nextinputfilename)) {
+			echo 'Cannot read "'.$nextinputfilename.'"<BR>';
+			return false;
+		}
+	}
+	if (!is_writeable($FilenameOut)) {
+		echo 'Cannot write "'.$FilenameOut.'"<BR>';
+		return false;
+	}
+
+	require_once('../getid3/getid3.php');
+	if ($fp_output = @fopen($FilenameOut, 'wb')) {
+
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+		foreach ($FilenamesIn as $nextinputfilename) {
+
+			$CurrentFileInfo = $getID3->analyze($nextinputfilename);
+			if ($CurrentFileInfo['fileformat'] == 'mp3') {
+
+				if ($fp_source = @fopen($nextinputfilename, 'rb')) {
+
+					$CurrentOutputPosition = ftell($fp_output);
+
+					// copy audio data from first file
+					fseek($fp_source, $CurrentFileInfo['avdataoffset'], SEEK_SET);
+					while (!feof($fp_source) && (ftell($fp_source) < $CurrentFileInfo['avdataend'])) {
+						fwrite($fp_output, fread($fp_source, 32768));
+					}
+					fclose($fp_source);
+
+					// trim post-audio data (if any) copied from first file that we don't need or want
+					$EndOfFileOffset = $CurrentOutputPosition + ($CurrentFileInfo['avdataend'] - $CurrentFileInfo['avdataoffset']);
+					fseek($fp_output, $EndOfFileOffset, SEEK_SET);
+					ftruncate($fp_output, $EndOfFileOffset);
+
+				} else {
+
+					echo 'failed to open '.$nextinputfilename.' for reading';
+					fclose($fp_output);
+					return false;
+
+				}
+
+			} else {
+
+				echo $nextinputfilename.' is not MP3 format';
+				fclose($fp_output);
+				return false;
+
+			}
+
+		}
+
+	} else {
+
+		echo 'failed to open '.$FilenameOut.' for writing';
+		return false;
+
+	}
+
+	fclose($fp_output);
+	return true;
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.mimeonly.php b/apps/media/getID3/demos/demo.mimeonly.php
new file mode 100644
index 0000000000000000000000000000000000000000..dd6dec6fe3d68ddf6c5b6f36c2564057093952fd
--- /dev/null
+++ b/apps/media/getID3/demos/demo.mimeonly.php
@@ -0,0 +1,53 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.mimeonly.php - part of getID3()                  //
+// Sample script for scanning a single file and returning only //
+// the MIME information                                        //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+echo '<HTML><HEAD><STYLE>BODY, TD, TH { font-family: sans-serif; font-size: 10pt; }</STYLE></HEAD><BODY>';
+
+if (!empty($_REQUEST['filename'])) {
+
+	echo 'The file "'.$_REQUEST['filename'].'" has a MIME type of "'.GetMIMEtype($_REQUEST['filename']).'"';
+
+} else {
+
+	echo 'Usage: <TT>'.$_SERVER['PHP_SELF'].'?filename=<I>filename.ext</I></TT>';
+
+}
+
+
+function GetMIMEtype($filename) {
+	// include getID3() library (can be in a different directory if full path is specified)
+	require_once('../getid3/getid3.php');
+	// Initialize getID3 engine
+	$getID3 = new getID3;
+
+	$DeterminedMIMEtype = '';
+	if ($fp = fopen($filename, 'rb')) {
+		$ThisFileInfo = array('avdataoffset'=>0, 'avdataend'=>0);
+
+		getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
+		$tag = new getid3_id3v2($fp, $ThisFileInfo);
+
+		fseek($fp, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$formattest = fread($fp, 16);  // 16 bytes is sufficient for any format except ISO CD-image
+		fclose($fp);
+
+		$DeterminedFormatInfo = $getID3->GetFileFormat($formattest);
+		$DeterminedMIMEtype = $DeterminedFormatInfo['mime_type'];
+	}
+	return $DeterminedMIMEtype;
+}
+
+?>
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.mp3header.php b/apps/media/getID3/demos/demo.mp3header.php
new file mode 100644
index 0000000000000000000000000000000000000000..2c9c1f223288bcdcee5edfe6619c75126717d677
--- /dev/null
+++ b/apps/media/getID3/demos/demo.mp3header.php
@@ -0,0 +1,2890 @@
+<?php
+
+if (!function_exists('PrintHexBytes')) {
+	function PrintHexBytes($string) {
+		$returnstring = '';
+		for ($i = 0; $i < strlen($string); $i++) {
+			$returnstring .= str_pad(dechex(ord(substr($string, $i, 1))), 2, '0', STR_PAD_LEFT).' ';
+		}
+		return $returnstring;
+	}
+}
+
+if (!function_exists('PrintTextBytes')) {
+	function PrintTextBytes($string) {
+		$returnstring = '';
+		for ($i = 0; $i < strlen($string); $i++) {
+			if (ord(substr($string, $i, 1)) <= 31) {
+				$returnstring .= '   ';
+			} else {
+				$returnstring .= ' '.substr($string, $i, 1).' ';
+			}
+		}
+		return $returnstring;
+	}
+}
+
+if (!function_exists('FixDBFields')) {
+	function FixDBFields($text) {
+		return mysql_escape_string($text);
+	}
+}
+
+if (!function_exists('FixTextFields')) {
+	function FixTextFields($text) {
+		$text = SafeStripSlashes($text);
+		$text = htmlentities($text, ENT_QUOTES);
+		return $text;
+	}
+}
+
+if (!function_exists('SafeStripSlashes')) {
+	function SafeStripSlashes($text) {
+		if (get_magic_quotes_gpc()) {
+			return stripslashes($text);
+		}
+		return $text;
+	}
+}
+
+
+if (!function_exists('table_var_dump')) {
+	function table_var_dump($variable) {
+		$returnstring = '';
+		switch (gettype($variable)) {
+			case 'array':
+				$returnstring .= '<TABLE BORDER="1" CELLSPACING="0" CELLPADDING="2">';
+				foreach ($variable as $key => $value) {
+					$returnstring .= '<TR><TD VALIGN="TOP"><B>'.str_replace(chr(0), ' ', $key).'</B></TD>';
+					$returnstring .= '<TD VALIGN="TOP">'.gettype($value);
+					if (is_array($value)) {
+						$returnstring .= '&nbsp;('.count($value).')';
+					} elseif (is_string($value)) {
+						$returnstring .= '&nbsp;('.strlen($value).')';
+					}
+					if (($key == 'data') && isset($variable['image_mime']) && isset($variable['dataoffset'])) {
+						require_once(GETID3_INCLUDEPATH.'getid3.getimagesize.php');
+						$imageinfo = array();
+						$imagechunkcheck = GetDataImageSize($value, $imageinfo);
+						$DumpedImageSRC = (!empty($_REQUEST['filename']) ? $_REQUEST['filename'] : '.getid3').'.'.$variable['dataoffset'].'.'.ImageTypesLookup($imagechunkcheck[2]);
+						if ($tempimagefile = fopen($DumpedImageSRC, 'wb')) {
+							fwrite($tempimagefile, $value);
+							fclose($tempimagefile);
+						}
+						$returnstring .= '</TD><TD><IMG SRC="'.$DumpedImageSRC.'" WIDTH="'.$imagechunkcheck[0].'" HEIGHT="'.$imagechunkcheck[1].'"></TD></TR>';
+					} else {
+						$returnstring .= '</TD><TD>'.table_var_dump($value).'</TD></TR>';
+					}
+				}
+				$returnstring .= '</TABLE>';
+				break;
+
+			case 'boolean':
+				$returnstring .= ($variable ? 'TRUE' : 'FALSE');
+				break;
+
+			case 'integer':
+			case 'double':
+			case 'float':
+				$returnstring .= $variable;
+				break;
+
+			case 'object':
+			case 'null':
+				$returnstring .= string_var_dump($variable);
+				break;
+
+			case 'string':
+				$variable = str_replace(chr(0), ' ', $variable);
+				$varlen = strlen($variable);
+				for ($i = 0; $i < $varlen; $i++) {
+					if (ereg('['.chr(0x0A).chr(0x0D).' -;0-9A-Za-z]', $variable{$i})) {
+						$returnstring .= $variable{$i};
+					} else {
+						$returnstring .= '&#'.str_pad(ord($variable{$i}), 3, '0', STR_PAD_LEFT).';';
+					}
+				}
+				$returnstring = nl2br($returnstring);
+				break;
+
+			default:
+				require_once(GETID3_INCLUDEPATH.'getid3.getimagesize.php');
+				$imageinfo = array();
+				$imagechunkcheck = GetDataImageSize(substr($variable, 0, FREAD_BUFFER_SIZE), $imageinfo);
+
+				if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
+					$returnstring .= '<TABLE BORDER="1" CELLSPACING="0" CELLPADDING="2">';
+					$returnstring .= '<TR><TD><B>type</B></TD><TD>'.ImageTypesLookup($imagechunkcheck[2]).'</TD></TR>';
+					$returnstring .= '<TR><TD><B>width</B></TD><TD>'.number_format($imagechunkcheck[0]).' px</TD></TR>';
+					$returnstring .= '<TR><TD><B>height</B></TD><TD>'.number_format($imagechunkcheck[1]).' px</TD></TR>';
+					$returnstring .= '<TR><TD><B>size</B></TD><TD>'.number_format(strlen($variable)).' bytes</TD></TR></TABLE>';
+				} else {
+					$returnstring .= nl2br(htmlspecialchars(str_replace(chr(0), ' ', $variable)));
+				}
+				break;
+		}
+		return $returnstring;
+	}
+}
+
+if (!function_exists('string_var_dump')) {
+	function string_var_dump($variable) {
+		ob_start();
+		var_dump($variable);
+		$dumpedvariable = ob_get_contents();
+		ob_end_clean();
+		return $dumpedvariable;
+	}
+}
+
+if (!function_exists('fileextension')) {
+	function fileextension($filename, $numextensions=1) {
+		if (strstr($filename, '.')) {
+			$reversedfilename = strrev($filename);
+			$offset = 0;
+			for ($i = 0; $i < $numextensions; $i++) {
+				$offset = strpos($reversedfilename, '.', $offset + 1);
+				if ($offset === false) {
+					return '';
+				}
+			}
+			return strrev(substr($reversedfilename, 0, $offset));
+		}
+		return '';
+	}
+}
+
+if (!function_exists('RemoveAccents')) {
+	function RemoveAccents($string) {
+		// return strtr($string, 'ŠŒŽšœžŸ¥µÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿ', 'SOZsozYYuAAAAAAACEEEEIIIIDNOOOOOOUUUUYsaaaaaaaceeeeiiiionoooooouuuuyy');
+		// Revised version by marksteward@hotmail.com
+		return strtr(strtr($string, 'ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'), array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u'));
+	}
+}
+
+if (!function_exists('MoreNaturalSort')) {
+	function MoreNaturalSort($ar1, $ar2) {
+		if ($ar1 === $ar2) {
+			return 0;
+		}
+		$len1     = strlen($ar1);
+		$len2     = strlen($ar2);
+		$shortest = min($len1, $len2);
+		if (substr($ar1, 0, $shortest) === substr($ar2, 0, $shortest)) {
+			// the shorter argument is the beginning of the longer one, like "str" and "string"
+			if ($len1 < $len2) {
+				return -1;
+			} elseif ($len1 > $len2) {
+				return 1;
+			}
+			return 0;
+		}
+		$ar1 = RemoveAccents(strtolower(trim($ar1)));
+		$ar2 = RemoveAccents(strtolower(trim($ar2)));
+		$translatearray = array('\''=>'', '"'=>'', '_'=>' ', '('=>'', ')'=>'', '-'=>' ', '  '=>' ', '.'=>'', ','=>'');
+		foreach ($translatearray as $key => $val) {
+			$ar1 = str_replace($key, $val, $ar1);
+			$ar2 = str_replace($key, $val, $ar2);
+		}
+
+		if ($ar1 < $ar2) {
+			return -1;
+		} elseif ($ar1 > $ar2) {
+			return 1;
+		}
+		return 0;
+	}
+}
+
+if (!function_exists('trunc')) {
+	function trunc($floatnumber) {
+		// truncates a floating-point number at the decimal point
+		// returns int (if possible, otherwise float)
+		if ($floatnumber >= 1) {
+			$truncatednumber = floor($floatnumber);
+		} elseif ($floatnumber <= -1) {
+			$truncatednumber = ceil($floatnumber);
+		} else {
+			$truncatednumber = 0;
+		}
+		if ($truncatednumber <= pow(2, 30)) {
+			$truncatednumber = (int) $truncatednumber;
+		}
+		return $truncatednumber;
+	}
+}
+
+if (!function_exists('CastAsInt')) {
+	function CastAsInt($floatnum) {
+		// convert to float if not already
+		$floatnum = (float) $floatnum;
+
+		// convert a float to type int, only if possible
+		if (trunc($floatnum) == $floatnum) {
+			// it's not floating point
+			if ($floatnum <= pow(2, 30)) {
+				// it's within int range
+				$floatnum = (int) $floatnum;
+			}
+		}
+		return $floatnum;
+	}
+}
+
+if (!function_exists('getmicrotime')) {
+	function getmicrotime() {
+		list($usec, $sec) = explode(' ', microtime());
+		return ((float) $usec + (float) $sec);
+	}
+}
+
+if (!function_exists('DecimalBinary2Float')) {
+	function DecimalBinary2Float($binarynumerator) {
+		$numerator   = Bin2Dec($binarynumerator);
+		$denominator = Bin2Dec(str_repeat('1', strlen($binarynumerator)));
+		return ($numerator / $denominator);
+	}
+}
+
+if (!function_exists('NormalizeBinaryPoint')) {
+	function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
+		if (strpos($binarypointnumber, '.') === false) {
+			$binarypointnumber = '0.'.$binarypointnumber;
+		} elseif ($binarypointnumber{0} == '.') {
+			$binarypointnumber = '0'.$binarypointnumber;
+		}
+		$exponent = 0;
+		while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
+			if (substr($binarypointnumber, 1, 1) == '.') {
+				$exponent--;
+				$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
+			} else {
+				$pointpos = strpos($binarypointnumber, '.');
+				$exponent += ($pointpos - 1);
+				$binarypointnumber = str_replace('.', '', $binarypointnumber);
+				$binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
+			}
+		}
+		$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
+		return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
+	}
+}
+
+if (!function_exists('Float2BinaryDecimal')) {
+	function Float2BinaryDecimal($floatvalue) {
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
+		$maxbits = 128; // to how many bits of precision should the calculations be taken?
+		$intpart   = trunc($floatvalue);
+		$floatpart = abs($floatvalue - $intpart);
+		$pointbitstring = '';
+		while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
+			$floatpart *= 2;
+			$pointbitstring .= (string) trunc($floatpart);
+			$floatpart -= trunc($floatpart);
+		}
+		$binarypointnumber = decbin($intpart).'.'.$pointbitstring;
+		return $binarypointnumber;
+	}
+}
+
+if (!function_exists('Float2String')) {
+	function Float2String($floatvalue, $bits) {
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
+		switch ($bits) {
+			case 32:
+				$exponentbits = 8;
+				$fractionbits = 23;
+				break;
+
+			case 64:
+				$exponentbits = 11;
+				$fractionbits = 52;
+				break;
+
+			default:
+				return false;
+				break;
+		}
+		if ($floatvalue >= 0) {
+			$signbit = '0';
+		} else {
+			$signbit = '1';
+		}
+		$normalizedbinary  = NormalizeBinaryPoint(Float2BinaryDecimal($floatvalue), $fractionbits);
+		$biasedexponent    = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
+		$exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
+		$fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
+
+		return BigEndian2String(Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
+	}
+}
+
+if (!function_exists('LittleEndian2Float')) {
+	function LittleEndian2Float($byteword) {
+		return BigEndian2Float(strrev($byteword));
+	}
+}
+
+if (!function_exists('BigEndian2Float')) {
+	function BigEndian2Float($byteword) {
+		// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
+		// http://www.psc.edu/general/software/packages/ieee/ieee.html
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
+
+		$bitword = BigEndian2Bin($byteword);
+		$signbit = $bitword{0};
+
+		switch (strlen($byteword) * 8) {
+			case 32:
+				$exponentbits = 8;
+				$fractionbits = 23;
+				break;
+
+			case 64:
+				$exponentbits = 11;
+				$fractionbits = 52;
+				break;
+
+			case 80:
+				$exponentbits = 16;
+				$fractionbits = 64;
+				break;
+
+			default:
+				return false;
+				break;
+		}
+		$exponentstring = substr($bitword, 1, $exponentbits - 1);
+		$fractionstring = substr($bitword, $exponentbits, $fractionbits);
+		$exponent = Bin2Dec($exponentstring);
+		$fraction = Bin2Dec($fractionstring);
+
+		if (($exponentbits == 16) && ($fractionbits == 64)) {
+			// 80-bit
+			// As used in Apple AIFF for sample_rate
+			// A bit of a hack, but it works ;)
+			return pow(2, ($exponent  - 16382)) * DecimalBinary2Float($fractionstring);
+		}
+
+
+		if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
+			// Not a Number
+			$floatvalue = false;
+		} elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
+			if ($signbit == '1') {
+				$floatvalue = '-infinity';
+			} else {
+				$floatvalue = '+infinity';
+			}
+		} elseif (($exponent == 0) && ($fraction == 0)) {
+			if ($signbit == '1') {
+				$floatvalue = -0;
+			} else {
+				$floatvalue = 0;
+			}
+			$floatvalue = ($signbit ? 0 : -0);
+		} elseif (($exponent == 0) && ($fraction != 0)) {
+			// These are 'unnormalized' values
+			$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * DecimalBinary2Float($fractionstring);
+			if ($signbit == '1') {
+				$floatvalue *= -1;
+			}
+		} elseif ($exponent != 0) {
+			$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + DecimalBinary2Float($fractionstring));
+			if ($signbit == '1') {
+				$floatvalue *= -1;
+			}
+		}
+		return (float) $floatvalue;
+	}
+}
+
+if (!function_exists('BigEndian2Int')) {
+	function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
+		$intvalue = 0;
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
+				$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7);
+			} else {
+				$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
+			}
+		}
+		if ($signed && !$synchsafe) {
+			// synchsafe ints are not allowed to be signed
+			switch ($bytewordlen) {
+				case 1:
+				case 2:
+				case 3:
+				case 4:
+					$signmaskbit = 0x80 << (8 * ($bytewordlen - 1));
+					if ($intvalue & $signmaskbit) {
+						$intvalue = 0 - ($intvalue & ($signmaskbit - 1));
+					}
+					break;
+
+				default:
+					die('ERROR: Cannot have signed integers larger than 32-bits in BigEndian2Int()');
+					break;
+			}
+		}
+		return CastAsInt($intvalue);
+	}
+}
+
+if (!function_exists('LittleEndian2Int')) {
+	function LittleEndian2Int($byteword, $signed=false) {
+		return BigEndian2Int(strrev($byteword), false, $signed);
+	}
+}
+
+if (!function_exists('BigEndian2Bin')) {
+	function BigEndian2Bin($byteword) {
+		$binvalue = '';
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			$binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
+		}
+		return $binvalue;
+	}
+}
+
+if (!function_exists('BigEndian2String')) {
+	function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
+		if ($number < 0) {
+			return false;
+		}
+		$maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
+		$intstring = '';
+		if ($signed) {
+			if ($minbytes > 4) {
+				die('ERROR: Cannot have signed integers larger than 32-bits in BigEndian2String()');
+			}
+			$number = $number & (0x80 << (8 * ($minbytes - 1)));
+		}
+		while ($number != 0) {
+			$quotient = ($number / ($maskbyte + 1));
+			$intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
+			$number = floor($quotient);
+		}
+		return str_pad($intstring, $minbytes, chr(0), STR_PAD_LEFT);
+	}
+}
+
+if (!function_exists('Dec2Bin')) {
+	function Dec2Bin($number) {
+		while ($number >= 256) {
+			$bytes[] = (($number / 256) - (floor($number / 256))) * 256;
+			$number = floor($number / 256);
+		}
+		$bytes[] = $number;
+		$binstring = '';
+		for ($i = 0; $i < count($bytes); $i++) {
+			$binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
+		}
+		return $binstring;
+	}
+}
+
+if (!function_exists('Bin2Dec')) {
+	function Bin2Dec($binstring) {
+		$decvalue = 0;
+		for ($i = 0; $i < strlen($binstring); $i++) {
+			$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
+		}
+		return CastAsInt($decvalue);
+	}
+}
+
+if (!function_exists('Bin2String')) {
+	function Bin2String($binstring) {
+		// return 'hi' for input of '0110100001101001'
+		$string = '';
+		$binstringreversed = strrev($binstring);
+		for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
+			$string = chr(Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
+		}
+		return $string;
+	}
+}
+
+if (!function_exists('LittleEndian2String')) {
+	function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
+		$intstring = '';
+		while ($number > 0) {
+			if ($synchsafe) {
+				$intstring = $intstring.chr($number & 127);
+				$number >>= 7;
+			} else {
+				$intstring = $intstring.chr($number & 255);
+				$number >>= 8;
+			}
+		}
+		return str_pad($intstring, $minbytes, chr(0), STR_PAD_RIGHT);
+	}
+}
+
+if (!function_exists('Bool2IntString')) {
+	function Bool2IntString($intvalue) {
+		return ($intvalue ? '1' : '0');
+	}
+}
+
+if (!function_exists('IntString2Bool')) {
+	function IntString2Bool($char) {
+		if ($char == '1') {
+			return true;
+		} elseif ($char == '0') {
+			return false;
+		}
+		return null;
+	}
+}
+
+if (!function_exists('InverseBoolean')) {
+	function InverseBoolean($value) {
+		return ($value ? false : true);
+	}
+}
+
+if (!function_exists('DeUnSynchronise')) {
+	function DeUnSynchronise($data) {
+		return str_replace(chr(0xFF).chr(0x00), chr(0xFF), $data);
+	}
+}
+
+if (!function_exists('Unsynchronise')) {
+	function Unsynchronise($data) {
+		// Whenever a false synchronisation is found within the tag, one zeroed
+		// byte is inserted after the first false synchronisation byte. The
+		// format of a correct sync that should be altered by ID3 encoders is as
+		// follows:
+		//      %11111111 111xxxxx
+		// And should be replaced with:
+		//      %11111111 00000000 111xxxxx
+		// This has the side effect that all $FF 00 combinations have to be
+		// altered, so they won't be affected by the decoding process. Therefore
+		// all the $FF 00 combinations have to be replaced with the $FF 00 00
+		// combination during the unsynchronisation.
+
+		$data = str_replace(chr(0xFF).chr(0x00), chr(0xFF).chr(0x00).chr(0x00), $data);
+		$unsyncheddata = '';
+		for ($i = 0; $i < strlen($data); $i++) {
+			$thischar = $data{$i};
+			$unsyncheddata .= $thischar;
+			if ($thischar == chr(255)) {
+				$nextchar = ord(substr($data, $i + 1, 1));
+				if (($nextchar | 0xE0) == 0xE0) {
+					// previous byte = 11111111, this byte = 111?????
+					$unsyncheddata .= chr(0);
+				}
+			}
+		}
+		return $unsyncheddata;
+	}
+}
+
+if (!function_exists('is_hash')) {
+	function is_hash($var) {
+		// written by dev-null@christophe.vg
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (is_array($var)) {
+			$keys = array_keys($var);
+			$all_num = true;
+			for ($i = 0; $i < count($keys); $i++) {
+				if (is_string($keys[$i])) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+}
+
+if (!function_exists('array_join_merge')) {
+	function array_join_merge($arr1, $arr2) {
+		// written by dev-null@christophe.vg
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (is_array($arr1) && is_array($arr2)) {
+			// the same -> merge
+			$new_array = array();
+
+			if (is_hash($arr1) && is_hash($arr2)) {
+				// hashes -> merge based on keys
+				$keys = array_merge(array_keys($arr1), array_keys($arr2));
+				foreach ($keys as $key) {
+					$new_array[$key] = array_join_merge(@$arr1[$key], @$arr2[$key]);
+				}
+			} else {
+				// two real arrays -> merge
+				$new_array = array_reverse(array_unique(array_reverse(array_merge($arr1,$arr2))));
+			}
+			return $new_array;
+		} else {
+			// not the same ... take new one if defined, else the old one stays
+			return $arr2 ? $arr2 : $arr1;
+		}
+	}
+}
+
+if (!function_exists('array_merge_clobber')) {
+	function array_merge_clobber($array1, $array2) {
+		// written by kc@hireability.com
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (!is_array($array1) || !is_array($array2)) {
+			return false;
+		}
+		$newarray = $array1;
+		foreach ($array2 as $key => $val) {
+			if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
+				$newarray[$key] = array_merge_clobber($newarray[$key], $val);
+			} else {
+				$newarray[$key] = $val;
+			}
+		}
+		return $newarray;
+	}
+}
+
+if (!function_exists('array_merge_noclobber')) {
+	function array_merge_noclobber($array1, $array2) {
+		if (!is_array($array1) || !is_array($array2)) {
+			return false;
+		}
+		$newarray = $array1;
+		foreach ($array2 as $key => $val) {
+			if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
+				$newarray[$key] = array_merge_noclobber($newarray[$key], $val);
+			} elseif (!isset($newarray[$key])) {
+				$newarray[$key] = $val;
+			}
+		}
+		return $newarray;
+	}
+}
+
+if (!function_exists('RoughTranslateUnicodeToASCII')) {
+	function RoughTranslateUnicodeToASCII($rawdata, $frame_textencoding) {
+		// rough translation of data for application that can't handle Unicode data
+
+		$tempstring = '';
+		switch ($frame_textencoding) {
+			case 0: // ISO-8859-1. Terminated with $00.
+				$asciidata = $rawdata;
+				break;
+
+			case 1: // UTF-16 encoded Unicode with BOM. Terminated with $00 00.
+				$asciidata = $rawdata;
+				if (substr($asciidata, 0, 2) == chr(0xFF).chr(0xFE)) {
+					// remove BOM, only if present (it should be, but...)
+					$asciidata = substr($asciidata, 2);
+				}
+				if (substr($asciidata, strlen($asciidata) - 2, 2) == chr(0).chr(0)) {
+					$asciidata = substr($asciidata, 0, strlen($asciidata) - 2); // remove terminator, only if present (it should be, but...)
+				}
+				for ($i = 0; $i < strlen($asciidata); $i += 2) {
+					if ((ord($asciidata{$i}) <= 0x7F) || (ord($asciidata{$i}) >= 0xA0)) {
+						$tempstring .= $asciidata{$i};
+					} else {
+						$tempstring .= '?';
+					}
+				}
+				$asciidata = $tempstring;
+				break;
+
+			case 2: // UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
+				$asciidata = $rawdata;
+				if (substr($asciidata, strlen($asciidata) - 2, 2) == chr(0).chr(0)) {
+					$asciidata = substr($asciidata, 0, strlen($asciidata) - 2); // remove terminator, only if present (it should be, but...)
+				}
+				for ($i = 0; $i < strlen($asciidata); $i += 2) {
+					if ((ord($asciidata{$i}) <= 0x7F) || (ord($asciidata{$i}) >= 0xA0)) {
+						$tempstring .= $asciidata{$i};
+					} else {
+						$tempstring .= '?';
+					}
+				}
+				$asciidata = $tempstring;
+				break;
+
+			case 3: // UTF-8 encoded Unicode. Terminated with $00.
+				$asciidata = utf8_decode($rawdata);
+				break;
+
+			case 255: // Unicode, Big-Endian. Terminated with $00 00.
+				$asciidata = $rawdata;
+				if (substr($asciidata, strlen($asciidata) - 2, 2) == chr(0).chr(0)) {
+					$asciidata = substr($asciidata, 0, strlen($asciidata) - 2); // remove terminator, only if present (it should be, but...)
+				}
+				for ($i = 0; ($i + 1) < strlen($asciidata); $i += 2) {
+					if ((ord($asciidata{($i + 1)}) <= 0x7F) || (ord($asciidata{($i + 1)}) >= 0xA0)) {
+						$tempstring .= $asciidata{($i + 1)};
+					} else {
+						$tempstring .= '?';
+					}
+				}
+				$asciidata = $tempstring;
+				break;
+
+
+			default:
+				// shouldn't happen, but in case $frame_textencoding is not 1 <= $frame_textencoding <= 4
+				// just pass the data through unchanged.
+				$asciidata = $rawdata;
+				break;
+		}
+		if (substr($asciidata, strlen($asciidata) - 1, 1) == chr(0)) {
+			// remove null terminator, if present
+			$asciidata = NoNullString($asciidata);
+		}
+		return $asciidata;
+		// return str_replace(chr(0), '', $asciidata); // just in case any nulls slipped through
+	}
+}
+
+if (!function_exists('PlaytimeString')) {
+	function PlaytimeString($playtimeseconds) {
+		$contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60);
+		$contentminutes = floor($playtimeseconds / 60);
+		if ($contentseconds >= 60) {
+			$contentseconds -= 60;
+			$contentminutes++;
+		}
+		return number_format($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT);
+	}
+}
+
+if (!function_exists('CloseMatch')) {
+	function CloseMatch($value1, $value2, $tolerance) {
+		return (abs($value1 - $value2) <= $tolerance);
+	}
+}
+
+if (!function_exists('ID3v1matchesID3v2')) {
+	function ID3v1matchesID3v2($id3v1, $id3v2) {
+
+		$requiredindices = array('title', 'artist', 'album', 'year', 'genre', 'comment');
+		foreach ($requiredindices as $requiredindex) {
+			if (!isset($id3v1["$requiredindex"])) {
+				$id3v1["$requiredindex"] = '';
+			}
+			if (!isset($id3v2["$requiredindex"])) {
+				$id3v2["$requiredindex"] = '';
+			}
+		}
+
+		if (trim($id3v1['title']) != trim(substr($id3v2['title'], 0, 30))) {
+			return false;
+		}
+		if (trim($id3v1['artist']) != trim(substr($id3v2['artist'], 0, 30))) {
+			return false;
+		}
+		if (trim($id3v1['album']) != trim(substr($id3v2['album'], 0, 30))) {
+			return false;
+		}
+		if (trim($id3v1['year']) != trim(substr($id3v2['year'], 0, 4))) {
+			return false;
+		}
+		if (trim($id3v1['genre']) != trim($id3v2['genre'])) {
+			return false;
+		}
+		if (isset($id3v1['track'])) {
+			if (!isset($id3v1['track']) || (trim($id3v1['track']) != trim($id3v2['track']))) {
+				return false;
+			}
+			if (trim($id3v1['comment']) != trim(substr($id3v2['comment'], 0, 28))) {
+				return false;
+			}
+		} else {
+			if (trim($id3v1['comment']) != trim(substr($id3v2['comment'], 0, 30))) {
+				return false;
+			}
+		}
+		return true;
+	}
+}
+
+if (!function_exists('FILETIMEtoUNIXtime')) {
+	function FILETIMEtoUNIXtime($FILETIME, $round=true) {
+		// FILETIME is a 64-bit unsigned integer representing
+		// the number of 100-nanosecond intervals since January 1, 1601
+		// UNIX timestamp is number of seconds since January 1, 1970
+		// 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days
+		if ($round) {
+			return round(($FILETIME - 116444736000000000) / 10000000);
+		}
+		return ($FILETIME - 116444736000000000) / 10000000;
+	}
+}
+
+if (!function_exists('GUIDtoBytestring')) {
+	function GUIDtoBytestring($GUIDstring) {
+		// Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way:
+		// first 4 bytes are in little-endian order
+		// next 2 bytes are appended in little-endian order
+		// next 2 bytes are appended in little-endian order
+		// next 2 bytes are appended in big-endian order
+		// next 6 bytes are appended in big-endian order
+
+		// AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string:
+		// $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp
+
+		$hexbytecharstring  = chr(hexdec(substr($GUIDstring,  6, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  4, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  2, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  0, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 11, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  9, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 16, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 14, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 19, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 21, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 24, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 26, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 28, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 30, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 32, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 34, 2)));
+
+		return $hexbytecharstring;
+	}
+}
+
+if (!function_exists('BytestringToGUID')) {
+	function BytestringToGUID($Bytestring) {
+		$GUIDstring  = str_pad(dechex(ord($Bytestring{3})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{2})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{1})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{0})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{5})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{4})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{7})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{6})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{8})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{9})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT);
+
+		return strtoupper($GUIDstring);
+	}
+}
+
+if (!function_exists('BitrateColor')) {
+	function BitrateColor($bitrate) {
+		$bitrate /= 3; // scale from 1-768kbps to 1-256kbps
+		$bitrate--;    // scale from 1-256kbps to 0-255kbps
+		$bitrate = max($bitrate, 0);
+		$bitrate = min($bitrate, 255);
+		//$bitrate = max($bitrate, 32);
+		//$bitrate = min($bitrate, 143);
+		//$bitrate = ($bitrate * 2) - 32;
+
+		$Rcomponent = max(255 - ($bitrate * 2), 0);
+		$Gcomponent = max(($bitrate * 2) - 255, 0);
+		if ($bitrate > 127) {
+			$Bcomponent = max((255 - $bitrate) * 2, 0);
+		} else {
+			$Bcomponent = max($bitrate * 2, 0);
+		}
+		return str_pad(dechex($Rcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Gcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Bcomponent), 2, '0', STR_PAD_LEFT);
+	}
+}
+
+if (!function_exists('BitrateText')) {
+	function BitrateText($bitrate) {
+		return '<SPAN STYLE="color: #'.BitrateColor($bitrate).'">'.round($bitrate).' kbps</SPAN>';
+	}
+}
+
+if (!function_exists('image_type_to_mime_type')) {
+	function image_type_to_mime_type($imagetypeid) {
+		// only available in PHP v4.3.0+
+		static $image_type_to_mime_type = array();
+		if (empty($image_type_to_mime_type)) {
+			$image_type_to_mime_type[1]  = 'image/gif';                     // GIF
+			$image_type_to_mime_type[2]  = 'image/jpeg';                    // JPEG
+			$image_type_to_mime_type[3]  = 'image/png';                     // PNG
+			$image_type_to_mime_type[4]  = 'application/x-shockwave-flash'; // Flash
+			$image_type_to_mime_type[5]  = 'image/psd';                     // PSD
+			$image_type_to_mime_type[6]  = 'image/bmp';                     // BMP
+			$image_type_to_mime_type[7]  = 'image/tiff';                    // TIFF: little-endian (Intel)
+			$image_type_to_mime_type[8]  = 'image/tiff';                    // TIFF: big-endian (Motorola)
+			//$image_type_to_mime_type[9]  = 'image/jpc';                   // JPC
+			//$image_type_to_mime_type[10] = 'image/jp2';                   // JPC
+			//$image_type_to_mime_type[11] = 'image/jpx';                   // JPC
+			//$image_type_to_mime_type[12] = 'image/jb2';                   // JPC
+			$image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave
+			$image_type_to_mime_type[14] = 'image/iff';                     // IFF
+		}
+		return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream');
+	}
+}
+
+if (!function_exists('utf8_decode')) {
+	// PHP has this function built-in if it's configured with the --with-xml option
+	// This version of the function is only provided in case XML isn't installed
+	function utf8_decode($utf8text) {
+		// http://www.php.net/manual/en/function.utf8-encode.php
+		// bytes  bits  representation
+		//   1     7    0bbbbbbb
+		//   2     11   110bbbbb 10bbbbbb
+		//   3     16   1110bbbb 10bbbbbb 10bbbbbb
+		//   4     21   11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
+
+		$utf8length = strlen($utf8text);
+		$decodedtext = '';
+		for ($i = 0; $i < $utf8length; $i++) {
+			if ((ord($utf8text{$i}) & 0x80) == 0) {
+				$decodedtext .= $utf8text{$i};
+			} elseif ((ord($utf8text{$i}) & 0xF0) == 0xF0) {
+				$decodedtext .= '?';
+				$i += 3;
+			} elseif ((ord($utf8text{$i}) & 0xE0) == 0xE0) {
+				$decodedtext .= '?';
+				$i += 2;
+			} elseif ((ord($utf8text{$i}) & 0xC0) == 0xC0) {
+				//   2     11   110bbbbb 10bbbbbb
+				$decodedchar = Bin2Dec(substr(Dec2Bin(ord($utf8text{$i})), 3, 5).substr(Dec2Bin(ord($utf8text{($i + 1)})), 2, 6));
+				if ($decodedchar <= 255) {
+					$decodedtext .= chr($decodedchar);
+				} else {
+					$decodedtext .= '?';
+				}
+				$i += 1;
+			}
+		}
+		return $decodedtext;
+	}
+}
+
+if (!function_exists('DateMac2Unix')) {
+	function DateMac2Unix($macdate) {
+		// Macintosh timestamp: seconds since 00:00h January 1, 1904
+		// UNIX timestamp:      seconds since 00:00h January 1, 1970
+		return CastAsInt($macdate - 2082844800);
+	}
+}
+
+
+if (!function_exists('FixedPoint8_8')) {
+	function FixedPoint8_8($rawdata) {
+		return BigEndian2Int(substr($rawdata, 0, 1)) + (float) (BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
+	}
+}
+
+
+if (!function_exists('FixedPoint16_16')) {
+	function FixedPoint16_16($rawdata) {
+		return BigEndian2Int(substr($rawdata, 0, 2)) + (float) (BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
+	}
+}
+
+
+if (!function_exists('FixedPoint2_30')) {
+	function FixedPoint2_30($rawdata) {
+		$binarystring = BigEndian2Bin($rawdata);
+		return Bin2Dec(substr($binarystring, 0, 2)) + (float) (Bin2Dec(substr($binarystring, 2, 30)) / pow(2, 30));
+	}
+}
+
+
+if (!function_exists('Pascal2String')) {
+	function Pascal2String($pascalstring) {
+		// Pascal strings have 1 byte at the beginning saying how many chars are in the string
+		return substr($pascalstring, 1);
+	}
+}
+
+if (!function_exists('NoNullString')) {
+	function NoNullString($nullterminatedstring) {
+		// remove the single null terminator on null terminated strings
+		if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === chr(0)) {
+			return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
+		}
+		return $nullterminatedstring;
+	}
+}
+
+if (!function_exists('FileSizeNiceDisplay')) {
+	function FileSizeNiceDisplay($filesize, $precision=2) {
+		if ($filesize < 1000) {
+			$sizeunit  = 'bytes';
+			$precision = 0;
+		} else {
+			$filesize /= 1024;
+			$sizeunit = 'kB';
+		}
+		if ($filesize >= 1000) {
+			$filesize /= 1024;
+			$sizeunit = 'MB';
+		}
+		if ($filesize >= 1000) {
+			$filesize /= 1024;
+			$sizeunit = 'GB';
+		}
+		return number_format($filesize, $precision).' '.$sizeunit;
+	}
+}
+
+if (!function_exists('DOStime2UNIXtime')) {
+	function DOStime2UNIXtime($DOSdate, $DOStime) {
+		// wFatDate
+		// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
+		// Bits      Contents
+		// 0-4    Day of the month (1-31)
+		// 5-8    Month (1 = January, 2 = February, and so on)
+		// 9-15   Year offset from 1980 (add 1980 to get actual year)
+
+		$UNIXday    =  ($DOSdate & 0x001F);
+		$UNIXmonth  = (($DOSdate & 0x01E0) >> 5);
+		$UNIXyear   = (($DOSdate & 0xFE00) >> 9) + 1980;
+
+		// wFatTime
+		// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
+		// Bits   Contents
+		// 0-4    Second divided by 2
+		// 5-10   Minute (0-59)
+		// 11-15  Hour (0-23 on a 24-hour clock)
+
+		$UNIXsecond =  ($DOStime & 0x001F) * 2;
+		$UNIXminute = (($DOStime & 0x07E0) >> 5);
+		$UNIXhour   = (($DOStime & 0xF800) >> 11);
+
+		return mktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+	}
+}
+
+if (!function_exists('CreateDeepArray')) {
+	function CreateDeepArray($ArrayPath, $Separator, $Value) {
+		// assigns $Value to a nested array path:
+		//   $foo = CreateDeepArray('/path/to/my', '/', 'file.txt')
+		// is the same as:
+		//   $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
+		// or
+		//   $foo['path']['to']['my'] = 'file.txt';
+		while ($ArrayPath{0} == $Separator) {
+			$ArrayPath = substr($ArrayPath, 1);
+		}
+		if (($pos = strpos($ArrayPath, $Separator)) !== false) {
+			$ReturnedArray[substr($ArrayPath, 0, $pos)] = CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
+		} else {
+			$ReturnedArray["$ArrayPath"] = $Value;
+		}
+		return $ReturnedArray;
+	}
+}
+
+if (!function_exists('md5_file')) {
+	// Allan Hansen <ah@artemis.dk>
+	// md5_file() exists in PHP 4.2.0.
+	// The following works under UNIX only, but dies on windows
+	function md5_file($file) {
+		if (substr(php_uname(), 0, 7) == 'Windows') {
+			die('PHP 4.2.0 or newer required for md5_file()');
+		}
+
+		$file = str_replace('`', '\\`', $file);
+		if (ereg("^([0-9a-f]{32})[ \t\n\r]", `md5sum "$file"`, $r)) {
+			return $r[1];
+		}
+		return false;
+	}
+}
+
+if (!function_exists('md5_data')) {
+	// Allan Hansen <ah@artemis.dk>
+	// md5_data() - returns md5sum for a file from startuing position to absolute end position
+
+	function md5_data($file, $offset, $end, $invertsign=false) {
+		// first try and create a temporary file in the same directory as the file being scanned
+		if (($dataMD5filename = tempnam(dirname($file), eregi_replace('[^[:alnum:]]', '', basename($file)))) === false) {
+			// if that fails, create a temporary file in the system temp directory
+			if (($dataMD5filename = tempnam('/tmp', 'getID3')) === false) {
+				// if that fails, create a temporary file in the current directory
+				if (($dataMD5filename = tempnam('.', eregi_replace('[^[:alnum:]]', '', basename($file)))) === false) {
+					// can't find anywhere to create a temp file, just die
+					return false;
+				}
+			}
+		}
+		$md5 = false;
+		set_time_limit(max(filesize($file) / 1000000, 30));
+
+		// copy parts of file
+		if ($fp = @fopen($file, 'rb')) {
+
+			if ($MD5fp = @fopen($dataMD5filename, 'wb')) {
+
+				if ($invertsign) {
+					// Load conversion lookup strings for 8-bit unsigned->signed conversion below
+	                $from = '';
+	                $to   = '';
+					for ($i = 0; $i < 128; $i++) {
+						$from .= chr($i);
+						$to   .= chr($i + 128);
+					}
+					for ($i = 128; $i < 256; $i++) {
+						$from .= chr($i);
+						$to   .= chr($i - 128);
+					}
+				}
+
+				fseek($fp, $offset, SEEK_SET);
+				$byteslefttowrite = $end - $offset;
+				while (($byteslefttowrite > 0) && ($buffer = fread($fp, FREAD_BUFFER_SIZE))) {
+					if ($invertsign) {
+						// Possibly FLAC-specific (?)
+						// FLAC calculates the MD5sum of the source data of 8-bit files
+						// not on the actual byte values in the source file, but of those
+						// values converted from unsigned to signed, or more specifcally,
+						// with the MSB inverted. ex: 01 -> 81; F5 -> 75; etc
+
+						// Therefore, 8-bit WAV data has to be converted before getting the
+						// md5_data value so as to match the FLAC value
+
+						// Flip the MSB for each byte in the buffer before copying
+						$buffer = strtr($buffer, $from, $to);
+					}
+					$byteswritten = fwrite($MD5fp, $buffer, $byteslefttowrite);
+					$byteslefttowrite -= $byteswritten;
+				}
+				fclose($MD5fp);
+				$md5 = md5_file($dataMD5filename);
+
+			}
+			fclose($fp);
+
+		}
+		unlink($dataMD5filename);
+		return $md5;
+	}
+}
+
+if (!function_exists('TwosCompliment2Decimal')) {
+	function TwosCompliment2Decimal($BinaryValue) {
+		// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
+		// First check if the number is negative or positive by looking at the sign bit.
+		// If it is positive, simply convert it to decimal.
+		// If it is negative, make it positive by inverting the bits and adding one.
+		// Then, convert the result to decimal.
+		// The negative of this number is the value of the original binary.
+
+		if ($BinaryValue & 0x80) {
+
+			// negative number
+			return (0 - ((~$BinaryValue & 0xFF) + 1));
+
+		} else {
+
+			// positive number
+			return $BinaryValue;
+
+		}
+
+	}
+}
+
+if (!function_exists('LastArrayElement')) {
+	function LastArrayElement($MyArray) {
+		if (!is_array($MyArray)) {
+			return false;
+		}
+		if (empty($MyArray)) {
+			return null;
+		}
+		foreach ($MyArray as $key => $value) {
+		}
+		return $value;
+	}
+}
+
+if (!function_exists('safe_inc')) {
+	function safe_inc(&$variable, $increment=1) {
+		if (isset($variable)) {
+			$variable += $increment;
+		} else {
+			$variable = $increment;
+		}
+		return true;
+	}
+}
+
+if (!function_exists('CalculateCompressionRatioVideo')) {
+	function CalculateCompressionRatioVideo(&$ThisFileInfo) {
+		if (empty($ThisFileInfo['video'])) {
+			return false;
+		}
+		if (empty($ThisFileInfo['video']['resolution_x']) || empty($ThisFileInfo['video']['resolution_y'])) {
+			return false;
+		}
+		if (empty($ThisFileInfo['video']['bits_per_sample'])) {
+			return false;
+		}
+
+		switch ($ThisFileInfo['video']['dataformat']) {
+			case 'bmp':
+			case 'gif':
+			case 'jpeg':
+			case 'jpg':
+			case 'png':
+			case 'tiff':
+				$FrameRate = 1;
+				$PlaytimeSeconds = 1;
+				$BitrateCompressed = $ThisFileInfo['filesize'] * 8;
+				break;
+
+			default:
+				if (!empty($ThisFileInfo['video']['frame_rate'])) {
+					$FrameRate = $ThisFileInfo['video']['frame_rate'];
+				} else {
+					return false;
+				}
+				if (!empty($ThisFileInfo['playtime_seconds'])) {
+					$PlaytimeSeconds = $ThisFileInfo['playtime_seconds'];
+				} else {
+					return false;
+				}
+				if (!empty($ThisFileInfo['video']['bitrate'])) {
+					$BitrateCompressed = $ThisFileInfo['video']['bitrate'];
+				} else {
+					return false;
+				}
+				break;
+		}
+		$BitrateUncompressed = $ThisFileInfo['video']['resolution_x'] * $ThisFileInfo['video']['resolution_y'] * $ThisFileInfo['video']['bits_per_sample'] * $FrameRate;
+
+		$ThisFileInfo['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
+		return true;
+	}
+}
+
+if (!function_exists('CalculateCompressionRatioAudio')) {
+	function CalculateCompressionRatioAudio(&$ThisFileInfo) {
+		if (empty($ThisFileInfo['audio']['bitrate']) || empty($ThisFileInfo['audio']['channels']) || empty($ThisFileInfo['audio']['sample_rate']) || empty($ThisFileInfo['audio']['bits_per_sample'])) {
+			return false;
+		}
+		$ThisFileInfo['audio']['compression_ratio'] = $ThisFileInfo['audio']['bitrate'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * $ThisFileInfo['audio']['bits_per_sample']);
+		return true;
+	}
+}
+
+if (!function_exists('IsValidMIMEstring')) {
+	function IsValidMIMEstring($mimestring) {
+	    if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
+			return true;
+	    }
+	    return false;
+	}
+}
+
+if (!function_exists('IsWithinBitRange')) {
+	function IsWithinBitRange($number, $maxbits, $signed=false) {
+	    if ($signed) {
+			if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
+				return true;
+			}
+	    } else {
+			if (($number >= 0) && ($number <= pow(2, $maxbits))) {
+				return true;
+			}
+	    }
+	    return false;
+	}
+}
+
+if (!function_exists('safe_parse_url')) {
+	function safe_parse_url($url) {
+	    $parts = @parse_url($url);
+	    $parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
+	    $parts['host']   = (isset($parts['host'])   ? $parts['host']   : '');
+	    $parts['user']   = (isset($parts['user'])   ? $parts['user']   : '');
+	    $parts['pass']   = (isset($parts['pass'])   ? $parts['pass']   : '');
+	    $parts['path']   = (isset($parts['path'])   ? $parts['path']   : '');
+	    $parts['query']  = (isset($parts['query'])  ? $parts['query']  : '');
+	    return $parts;
+	}
+}
+
+if (!function_exists('IsValidURL')) {
+	function IsValidURL($url, $allowUserPass=false) {
+	    if ($url == '') {
+			return false;
+	    }
+	    if ($allowUserPass !== true) {
+			if (strstr($url, '@')) {
+				// in the format http://user:pass@example.com  or http://user@example.com
+				// but could easily be somebody incorrectly entering an email address in place of a URL
+				return false;
+			}
+	    }
+	    if ($parts = safe_parse_url($url)) {
+			if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) {
+				return false;
+			} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['user'], $regs)) {
+				return false;
+			} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['pass'], $regs)) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]/_\.@~-]*$", $parts['path'], $regs)) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]?&=+:;_()%#/,\.-]*$", $parts['query'], $regs)) {
+				return false;
+			} else {
+				return true;
+			}
+		}
+		return false;
+	}
+}
+
+echo '<FORM ACTION="'.$_SERVER['PHP_SELF'].'" METHOD="POST">';
+echo 'Enter 4 hex bytes of MPEG-audio header (ie <I>FF FA 92 44</I>)<BR>';
+echo '<INPUT TYPE="TEXT" NAME="HeaderHexBytes" VALUE="'.(isset($_POST['HeaderHexBytes']) ? strtoupper($_POST['HeaderHexBytes']) : '').'" SIZE="11" MAXLENGTH="11">';
+echo '<INPUT TYPE="SUBMIT" NAME="Analyze" VALUE="Analyze"></FORM>';
+echo '<HR>';
+
+echo '<FORM ACTION="'.$_SERVER['PHP_SELF'].'" METHOD="POST">';
+echo 'Generate a MPEG-audio 4-byte header from these values:<BR>';
+echo '<TABLE BORDER="0">';
+
+$MPEGgenerateValues = array(
+								'version'=>array('1', '2', '2.5'),
+								'layer'=>array('I', 'II', 'III'),
+								'protection'=>array('Y', 'N'),
+								'bitrate'=>array('free', '8', '16', '24', '32', '40', '48', '56', '64', '80', '96', '112', '128', '144', '160', '176', '192', '224', '256', '288', '320', '352', '384', '416', '448'),
+								'frequency'=>array('8000', '11025', '12000', '16000', '22050', '24000', '32000', '44100', '48000'),
+								'padding'=>array('Y', 'N'),
+								'private'=>array('Y', 'N'),
+								'channelmode'=>array('stereo', 'joint stereo', 'dual channel', 'mono'),
+								'modeextension'=>array('none', 'IS', 'MS', 'IS+MS', '4-31', '8-31', '12-31', '16-31'),
+								'copyright'=>array('Y', 'N'),
+								'original'=>array('Y', 'N'),
+								'emphasis'=>array('none', '50/15ms', 'CCIT J.17')
+							);
+
+foreach ($MPEGgenerateValues as $name => $dataarray) {
+    echo '<TR><TH>'.$name.':</TH><TD><SELECT NAME="'.$name.'">';
+    foreach ($dataarray as $key => $value) {
+		echo '<OPTION'.((isset($_POST["$name"]) && ($_POST["$name"] == $value)) ? ' SELECTED' : '').'>'.$value.'</OPTION>';
+    }
+    echo '</SELECT></TD></TR>';
+}
+
+if (isset($_POST['bitrate'])) {
+	echo '<TR><TH>Frame Length:</TH><TD>'.(int) MPEGaudioFrameLength($_POST['bitrate'], $_POST['version'], $_POST['layer'], (($_POST['padding'] == 'Y') ? '1' : '0'), $_POST['frequency']).'</TD></TR>';
+}
+echo '</TABLE>';
+echo '<INPUT TYPE="SUBMIT" NAME="Generate" VALUE="Generate"></FORM>';
+echo '<HR>';
+
+
+if (isset($_POST['Analyze']) && $_POST['HeaderHexBytes']) {
+
+    $headerbytearray = explode(' ', $_POST['HeaderHexBytes']);
+    if (count($headerbytearray) != 4) {
+		die('Invalid byte pattern');
+    }
+    $headerstring = '';
+    foreach ($headerbytearray as $textbyte) {
+		$headerstring .= chr(hexdec($textbyte));
+    }
+
+    $MP3fileInfo['error'] = '';
+
+    $MPEGheaderRawArray = MPEGaudioHeaderDecode(substr($headerstring, 0, 4));
+
+    if (MPEGaudioHeaderValid($MPEGheaderRawArray, true)) {
+
+		$MP3fileInfo['raw'] = $MPEGheaderRawArray;
+
+		$MP3fileInfo['version']              = MPEGaudioVersionLookup($MP3fileInfo['raw']['version']);
+		$MP3fileInfo['layer']                = MPEGaudioLayerLookup($MP3fileInfo['raw']['layer']);
+		$MP3fileInfo['protection']           = MPEGaudioCRCLookup($MP3fileInfo['raw']['protection']);
+		$MP3fileInfo['bitrate']              = MPEGaudioBitrateLookup($MP3fileInfo['version'], $MP3fileInfo['layer'], $MP3fileInfo['raw']['bitrate']);
+		$MP3fileInfo['frequency']            = MPEGaudioFrequencyLookup($MP3fileInfo['version'], $MP3fileInfo['raw']['sample_rate']);
+		$MP3fileInfo['padding']              = (bool) $MP3fileInfo['raw']['padding'];
+		$MP3fileInfo['private']              = (bool) $MP3fileInfo['raw']['private'];
+		$MP3fileInfo['channelmode']          = MPEGaudioChannelModeLookup($MP3fileInfo['raw']['channelmode']);
+		$MP3fileInfo['channels']             = (($MP3fileInfo['channelmode'] == 'mono') ? 1 : 2);
+		$MP3fileInfo['modeextension']        = MPEGaudioModeExtensionLookup($MP3fileInfo['layer'], $MP3fileInfo['raw']['modeextension']);
+		$MP3fileInfo['copyright']            = (bool) $MP3fileInfo['raw']['copyright'];
+		$MP3fileInfo['original']             = (bool) $MP3fileInfo['raw']['original'];
+		$MP3fileInfo['emphasis']             = MPEGaudioEmphasisLookup($MP3fileInfo['raw']['emphasis']);
+
+		if ($MP3fileInfo['protection']) {
+			$MP3fileInfo['crc'] = BigEndian2Int(substr($headerstring, 4, 2));
+		}
+
+		if ($MP3fileInfo['frequency'] > 0) {
+			$MP3fileInfo['framelength'] = MPEGaudioFrameLength($MP3fileInfo['bitrate'], $MP3fileInfo['version'], $MP3fileInfo['layer'], (int) $MP3fileInfo['padding'], $MP3fileInfo['frequency']);
+		}
+		if ($MP3fileInfo['bitrate'] != 'free') {
+			$MP3fileInfo['bitrate'] *= 1000;
+		}
+
+    } else {
+
+		$MP3fileInfo['error'] .= "\n".'Invalid MPEG audio header';
+
+    }
+
+    if (!$MP3fileInfo['error']) {
+		unset($MP3fileInfo['error']);
+    }
+
+    echo table_var_dump($MP3fileInfo);
+
+} elseif (isset($_POST['Generate'])) {
+
+    // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
+
+    $headerbitstream  = '11111111111';                               // A - Frame sync (all bits set)
+
+    $MPEGversionLookup = array('2.5'=>'00', '2'=>'10', '1'=>'11');
+    $headerbitstream .= $MPEGversionLookup[$_POST['version']];       // B - MPEG Audio version ID
+
+    $MPEGlayerLookup = array('III'=>'01', 'II'=>'10', 'I'=>'11');
+    $headerbitstream .= $MPEGlayerLookup[$_POST['layer']];           // C - Layer description
+
+    $headerbitstream .= (($_POST['protection'] == 'Y') ? '0' : '1'); // D - Protection bit
+
+    $MPEGaudioBitrateLookup['1']['I']     = array('free'=>'0000', '32'=>'0001', '64'=>'0010', '96'=>'0011', '128'=>'0100', '160'=>'0101', '192'=>'0110', '224'=>'0111', '256'=>'1000', '288'=>'1001', '320'=>'1010', '352'=>'1011', '384'=>'1100', '416'=>'1101', '448'=>'1110');
+    $MPEGaudioBitrateLookup['1']['II']    = array('free'=>'0000', '32'=>'0001', '48'=>'0010', '56'=>'0011',  '64'=>'0100',  '80'=>'0101',  '96'=>'0110', '112'=>'0111', '128'=>'1000', '160'=>'1001', '192'=>'1010', '224'=>'1011', '256'=>'1100', '320'=>'1101', '384'=>'1110');
+    $MPEGaudioBitrateLookup['1']['III']   = array('free'=>'0000', '32'=>'0001', '40'=>'0010', '48'=>'0011',  '56'=>'0100',  '64'=>'0101',  '80'=>'0110',  '96'=>'0111', '112'=>'1000', '128'=>'1001', '160'=>'1010', '192'=>'1011', '224'=>'1100', '256'=>'1101', '320'=>'1110');
+    $MPEGaudioBitrateLookup['2']['I']     = array('free'=>'0000', '32'=>'0001', '48'=>'0010', '56'=>'0011',  '64'=>'0100',  '80'=>'0101',  '96'=>'0110', '112'=>'0111', '128'=>'1000', '144'=>'1001', '160'=>'1010', '176'=>'1011', '192'=>'1100', '224'=>'1101', '256'=>'1110');
+    $MPEGaudioBitrateLookup['2']['II']    = array('free'=>'0000',  '8'=>'0001', '16'=>'0010', '24'=>'0011',  '32'=>'0100',  '40'=>'0101',  '48'=>'0110',  '56'=>'0111',  '64'=>'1000',  '80'=>'1001',  '96'=>'1010', '112'=>'1011', '128'=>'1100', '144'=>'1101', '160'=>'1110');
+    $MPEGaudioBitrateLookup['2']['III']   = $MPEGaudioBitrateLookup['2']['II'];
+    $MPEGaudioBitrateLookup['2.5']['I']   = $MPEGaudioBitrateLookup['2']['I'];
+    $MPEGaudioBitrateLookup['2.5']['II']  = $MPEGaudioBitrateLookup['2']['II'];
+    $MPEGaudioBitrateLookup['2.5']['III'] = $MPEGaudioBitrateLookup['2']['II'];
+    if (isset($MPEGaudioBitrateLookup[$_POST['version']][$_POST['layer']][$_POST['bitrate']])) {
+		$headerbitstream .= $MPEGaudioBitrateLookup[$_POST['version']][$_POST['layer']][$_POST['bitrate']]; // E - Bitrate index
+    } else {
+		die('Invalid <B>Bitrate</B>');
+    }
+
+    $MPEGaudioFrequencyLookup['1']   = array('44100'=>'00', '48000'=>'01', '32000'=>'10');
+    $MPEGaudioFrequencyLookup['2']   = array('22050'=>'00', '24000'=>'01', '16000'=>'10');
+    $MPEGaudioFrequencyLookup['2.5'] = array('11025'=>'00', '12000'=>'01', '8000'=>'10');
+    if (isset($MPEGaudioFrequencyLookup[$_POST['version']][$_POST['frequency']])) {
+		$headerbitstream .= $MPEGaudioFrequencyLookup[$_POST['version']][$_POST['frequency']];  // F - Sampling rate frequency index
+    } else {
+		die('Invalid <B>Frequency</B>');
+    }
+
+    $headerbitstream .= (($_POST['padding'] == 'Y') ? '1' : '0');            // G - Padding bit
+
+    $headerbitstream .= (($_POST['private'] == 'Y') ? '1' : '0');            // H - Private bit
+
+    $MPEGaudioChannelModeLookup = array('stereo'=>'00', 'joint stereo'=>'01', 'dual channel'=>'10', 'mono'=>'11');
+    $headerbitstream .= $MPEGaudioChannelModeLookup[$_POST['channelmode']];  // I - Channel Mode
+
+    $MPEGaudioModeExtensionLookup['I']   = array('4-31'=>'00', '8-31'=>'01', '12-31'=>'10', '16-31'=>'11');
+    $MPEGaudioModeExtensionLookup['II']  = $MPEGaudioModeExtensionLookup['I'];
+    $MPEGaudioModeExtensionLookup['III'] = array('none'=>'00',   'IS'=>'01',    'MS'=>'10', 'IS+MS'=>'11');
+    if ($_POST['channelmode'] != 'joint stereo') {
+		$headerbitstream .= '00';
+    } elseif (isset($MPEGaudioModeExtensionLookup[$_POST['layer']][$_POST['modeextension']])) {
+		$headerbitstream .= $MPEGaudioModeExtensionLookup[$_POST['layer']][$_POST['modeextension']];  // J - Mode extension (Only if Joint stereo)
+    } else {
+		die('Invalid <B>Mode Extension</B>');
+    }
+
+    $headerbitstream .= (($_POST['copyright'] == 'Y') ? '1' : '0');          // K - Copyright
+
+    $headerbitstream .= (($_POST['original']  == 'Y') ? '1' : '0');          // L - Original
+
+    $MPEGaudioEmphasisLookup = array('none'=>'00', '50/15ms'=>'01', 'CCIT J.17'=>'11');
+    if (isset($MPEGaudioEmphasisLookup[$_POST['emphasis']])) {
+		$headerbitstream .= $MPEGaudioEmphasisLookup[$_POST['emphasis']];    // M - Emphasis
+    } else {
+		die('Invalid <B>Emphasis</B>');
+    }
+
+    echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream,  0, 8))), 2, '0', STR_PAD_LEFT)).' ';
+    echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream,  8, 8))), 2, '0', STR_PAD_LEFT)).' ';
+    echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream, 16, 8))), 2, '0', STR_PAD_LEFT)).' ';
+    echo strtoupper(str_pad(dechex(bindec(substr($headerbitstream, 24, 8))), 2, '0', STR_PAD_LEFT)).'<BR>';
+
+}
+
+function MPEGaudioVersionLookup($rawversion) {
+	$MPEGaudioVersionLookup = array('2.5', FALSE, '2', '1');
+	return (isset($MPEGaudioVersionLookup["$rawversion"]) ? $MPEGaudioVersionLookup["$rawversion"] : FALSE);
+}
+
+function MPEGaudioLayerLookup($rawlayer) {
+	$MPEGaudioLayerLookup = array(FALSE, 'III', 'II', 'I');
+	return (isset($MPEGaudioLayerLookup["$rawlayer"]) ? $MPEGaudioLayerLookup["$rawlayer"] : FALSE);
+}
+
+function MPEGaudioBitrateLookup($version, $layer, $rawbitrate) {
+	static $MPEGaudioBitrateLookup;
+	if (empty($MPEGaudioBitrateLookup)) {
+		$MPEGaudioBitrateLookup = MPEGaudioBitrateArray();
+	}
+	return (isset($MPEGaudioBitrateLookup["$version"]["$layer"]["$rawbitrate"]) ? $MPEGaudioBitrateLookup["$version"]["$layer"]["$rawbitrate"] : FALSE);
+}
+
+function MPEGaudioFrequencyLookup($version, $rawfrequency) {
+	static $MPEGaudioFrequencyLookup;
+	if (empty($MPEGaudioFrequencyLookup)) {
+		$MPEGaudioFrequencyLookup = MPEGaudioFrequencyArray();
+	}
+	return (isset($MPEGaudioFrequencyLookup["$version"]["$rawfrequency"]) ? $MPEGaudioFrequencyLookup["$version"]["$rawfrequency"] : FALSE);
+}
+
+function MPEGaudioChannelModeLookup($rawchannelmode) {
+	$MPEGaudioChannelModeLookup = array('stereo', 'joint stereo', 'dual channel', 'mono');
+	return (isset($MPEGaudioChannelModeLookup["$rawchannelmode"]) ? $MPEGaudioChannelModeLookup["$rawchannelmode"] : FALSE);
+}
+
+function MPEGaudioModeExtensionLookup($layer, $rawmodeextension) {
+	$MPEGaudioModeExtensionLookup['I']   = array('4-31', '8-31', '12-31', '16-31');
+	$MPEGaudioModeExtensionLookup['II']  = array('4-31', '8-31', '12-31', '16-31');
+	$MPEGaudioModeExtensionLookup['III'] = array('', 'IS', 'MS', 'IS+MS');
+	return (isset($MPEGaudioModeExtensionLookup["$layer"]["$rawmodeextension"]) ? $MPEGaudioModeExtensionLookup["$layer"]["$rawmodeextension"] : FALSE);
+}
+
+function MPEGaudioEmphasisLookup($rawemphasis) {
+	$MPEGaudioEmphasisLookup = array('none', '50/15ms', FALSE, 'CCIT J.17');
+	return (isset($MPEGaudioEmphasisLookup["$rawemphasis"]) ? $MPEGaudioEmphasisLookup["$rawemphasis"] : FALSE);
+}
+
+function MPEGaudioCRCLookup($CRCbit) {
+	// inverse boolean cast :)
+	if ($CRCbit == '0') {
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                ///
+//            or http://www.getid3.org                        ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// getid3.mp3.php - part of getID3()                           //
+// See getid3.readme.txt for more details                      //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+
+// number of frames to scan to determine if MPEG-audio sequence is valid
+// Lower this number to 5-20 for faster scanning
+// Increase this number to 50+ for most accurate detection of valid VBR/CBR
+// mpeg-audio streams
+define('MPEG_VALID_CHECK_FRAMES', 35);
+
+function getMP3headerFilepointer(&$fd, &$ThisFileInfo) {
+
+	getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset']);
+
+	if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) {
+		$ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
+	}
+
+	if (((isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) {
+
+		$ThisFileInfo['warning'] .= "\n".'Unknown data before synch ';
+		if (isset($ThisFileInfo['id3v2']['headerlength'])) {
+			$ThisFileInfo['warning'] .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, ';
+		} else {
+			$ThisFileInfo['warning'] .= '(should be at beginning of file, ';
+		}
+		$ThisFileInfo['warning'] .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')';
+		if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
+			if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) {
+				$ThisFileInfo['warning'] .= '. This is a known problem with some versions of LAME (3.91, 3.92) DLL in CBR mode.';
+				$ThisFileInfo['audio']['codec'] = 'LAME';
+			} elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) {
+				$ThisFileInfo['warning'] .= '. This is a known problem with some versions of LAME (3.91, 3.92) DLL in CBR mode.';
+				$ThisFileInfo['audio']['codec'] = 'LAME';
+			}
+		}
+
+	}
+
+	if (isset($ThisFileInfo['mpeg']['audio']['layer']) && ($ThisFileInfo['mpeg']['audio']['layer'] == 'II')) {
+		$ThisFileInfo['audio']['dataformat'] = 'mp2';
+	} elseif (isset($ThisFileInfo['mpeg']['audio']['layer']) && ($ThisFileInfo['mpeg']['audio']['layer'] == 'I')) {
+		$ThisFileInfo['audio']['dataformat'] = 'mp1';
+	}
+	if ($ThisFileInfo['fileformat'] == 'mp3') {
+		switch ($ThisFileInfo['audio']['dataformat']) {
+			case 'mp1':
+			case 'mp2':
+			case 'mp3':
+				$ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat'];
+				break;
+
+			default:
+				$ThisFileInfo['warning'] .= "\n".'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"';
+				break;
+		}
+	}
+
+	if (empty($ThisFileInfo['fileformat'])) {
+		$ThisFileInfo['error'] .= "\n".'Synch not found';
+		unset($ThisFileInfo['fileformat']);
+		unset($ThisFileInfo['audio']['bitrate_mode']);
+		unset($ThisFileInfo['avdataoffset']);
+		unset($ThisFileInfo['avdataend']);
+		return false;
+	}
+
+	$ThisFileInfo['mime_type']         = 'audio/mpeg';
+	$ThisFileInfo['audio']['lossless'] = false;
+
+	// Calculate playtime
+	if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) {
+		$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate'];
+	}
+
+	if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) {
+		$ThisFileInfo['audio']['codec'] = 'LAME';
+		if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) {
+			$ThisFileInfo['audio']['encoder'] = trim($ThisFileInfo['mpeg']['audio']['LAME']['long_version']);
+		}
+	}
+
+	return true;
+}
+
+
+function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
+
+	static $MPEGaudioVersionLookup;
+	static $MPEGaudioLayerLookup;
+	static $MPEGaudioBitrateLookup;
+	static $MPEGaudioFrequencyLookup;
+	static $MPEGaudioChannelModeLookup;
+	static $MPEGaudioModeExtensionLookup;
+	static $MPEGaudioEmphasisLookup;
+	if (empty($MPEGaudioVersionLookup)) {
+		$MPEGaudioVersionLookup       = MPEGaudioVersionArray();
+		$MPEGaudioLayerLookup         = MPEGaudioLayerArray();
+		$MPEGaudioBitrateLookup       = MPEGaudioBitrateArray();
+		$MPEGaudioFrequencyLookup     = MPEGaudioFrequencyArray();
+		$MPEGaudioChannelModeLookup   = MPEGaudioChannelModeArray();
+		$MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray();
+		$MPEGaudioEmphasisLookup      = MPEGaudioEmphasisArray();
+	}
+
+	if ($offset >= $ThisFileInfo['avdataend']) {
+		$ThisFileInfo['error'] .= "\n".'end of file encounter looking for MPEG synch';
+		return false;
+	}
+	fseek($fd, $offset, SEEK_SET);
+	$headerstring = fread($fd, 1441); // worse-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
+
+	// MP3 audio frame structure:
+	// $aa $aa $aa $aa [$bb $bb] $cc...
+	// where $aa..$aa is the four-byte mpeg-audio header (below)
+	// $bb $bb is the optional 2-byte CRC
+	// and $cc... is the audio data
+
+	$head4 = substr($headerstring, 0, 4);
+
+	static $MPEGaudioHeaderDecodeCache = array();
+	if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
+		$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
+	} else {
+		$MPEGheaderRawArray = MPEGaudioHeaderDecode($head4);
+		$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
+	}
+
+	static $MPEGaudioHeaderValidCache = array();
+
+	// Not in cache
+	if (!isset($MPEGaudioHeaderValidCache[$head4])) {
+		$MPEGaudioHeaderValidCache[$head4] = MPEGaudioHeaderValid($MPEGheaderRawArray);
+	}
+
+	if ($MPEGaudioHeaderValidCache[$head4]) {
+		$ThisFileInfo['mpeg']['audio']['raw'] = $MPEGheaderRawArray;
+	} else {
+		$ThisFileInfo['error'] .= "\n".'Invalid MPEG audio header at offset '.$offset;
+		return false;
+	}
+
+	if (!$FastMPEGheaderScan) {
+
+		$ThisFileInfo['mpeg']['audio']['version']       = $MPEGaudioVersionLookup[$ThisFileInfo['mpeg']['audio']['raw']['version']];
+		$ThisFileInfo['mpeg']['audio']['layer']         = $MPEGaudioLayerLookup[$ThisFileInfo['mpeg']['audio']['raw']['layer']];
+
+		$ThisFileInfo['mpeg']['audio']['channelmode']   = $MPEGaudioChannelModeLookup[$ThisFileInfo['mpeg']['audio']['raw']['channelmode']];
+		$ThisFileInfo['mpeg']['audio']['channels']      = (($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') ? 1 : 2);
+		$ThisFileInfo['mpeg']['audio']['sample_rate']   = $MPEGaudioFrequencyLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['raw']['sample_rate']];
+		$ThisFileInfo['mpeg']['audio']['protection']    = !$ThisFileInfo['mpeg']['audio']['raw']['protection'];
+		$ThisFileInfo['mpeg']['audio']['private']       = (bool) $ThisFileInfo['mpeg']['audio']['raw']['private'];
+		$ThisFileInfo['mpeg']['audio']['modeextension'] = $MPEGaudioModeExtensionLookup[$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['modeextension']];
+		$ThisFileInfo['mpeg']['audio']['copyright']     = (bool) $ThisFileInfo['mpeg']['audio']['raw']['copyright'];
+		$ThisFileInfo['mpeg']['audio']['original']      = (bool) $ThisFileInfo['mpeg']['audio']['raw']['original'];
+		$ThisFileInfo['mpeg']['audio']['emphasis']      = $MPEGaudioEmphasisLookup[$ThisFileInfo['mpeg']['audio']['raw']['emphasis']];
+
+		$ThisFileInfo['audio']['channels']    = $ThisFileInfo['mpeg']['audio']['channels'];
+		$ThisFileInfo['audio']['sample_rate'] = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+
+		if ($ThisFileInfo['mpeg']['audio']['protection']) {
+			$ThisFileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2));
+		}
+
+	}
+
+	if ($ThisFileInfo['mpeg']['audio']['raw']['bitrate'] == 15) {
+		// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
+		$ThisFileInfo['warning'] .= "\n".'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
+		$ThisFileInfo['mpeg']['audio']['raw']['bitrate'] = 0;
+	}
+	$ThisFileInfo['mpeg']['audio']['padding'] = (bool) $ThisFileInfo['mpeg']['audio']['raw']['padding'];
+	$ThisFileInfo['mpeg']['audio']['bitrate'] = $MPEGaudioBitrateLookup[$ThisFileInfo['mpeg']['audio']['version']][$ThisFileInfo['mpeg']['audio']['layer']][$ThisFileInfo['mpeg']['audio']['raw']['bitrate']];
+
+	if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) {
+		// only skip multiple frame check if free-format bitstream found at beginning of file
+		// otherwise is quite possibly simply corrupted data
+		$recursivesearch = false;
+	}
+
+	// For Layer II there are some combinations of bitrate and mode which are not allowed.
+	if (!$FastMPEGheaderScan && ($ThisFileInfo['mpeg']['audio']['layer'] == 'II')) {
+
+		$ThisFileInfo['audio']['dataformat'] = 'mp2';
+		switch ($ThisFileInfo['mpeg']['audio']['channelmode']) {
+
+			case 'mono':
+				if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') || ($ThisFileInfo['mpeg']['audio']['bitrate'] <= 192)) {
+					// these are ok
+				} else {
+					$ThisFileInfo['error'] .= "\n".$ThisFileInfo['mpeg']['audio']['bitrate'].'kbps not allowed in Layer II, '.$ThisFileInfo['mpeg']['audio']['channelmode'].'.';
+					return false;
+				}
+				break;
+
+			case 'stereo':
+			case 'joint stereo':
+			case 'dual channel':
+				if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') || ($ThisFileInfo['mpeg']['audio']['bitrate'] == 64) || ($ThisFileInfo['mpeg']['audio']['bitrate'] >= 96)) {
+					// these are ok
+				} else {
+					$ThisFileInfo['error'] .= "\n".$ThisFileInfo['mpeg']['audio']['bitrate'].'kbps not allowed in Layer II, '.$ThisFileInfo['mpeg']['audio']['channelmode'].'.';
+					return false;
+				}
+				break;
+
+		}
+
+	}
+
+
+	if ($ThisFileInfo['audio']['sample_rate'] > 0) {
+		$ThisFileInfo['mpeg']['audio']['framelength'] = MPEGaudioFrameLength($ThisFileInfo['mpeg']['audio']['bitrate'], $ThisFileInfo['mpeg']['audio']['version'], $ThisFileInfo['mpeg']['audio']['layer'], (int) $ThisFileInfo['mpeg']['audio']['padding'], $ThisFileInfo['audio']['sample_rate']);
+	}
+
+	if ($ThisFileInfo['mpeg']['audio']['bitrate'] != 'free') {
+
+		$ThisFileInfo['audio']['bitrate'] = 1000 * $ThisFileInfo['mpeg']['audio']['bitrate'];
+
+		if (isset($ThisFileInfo['mpeg']['audio']['framelength'])) {
+			$nextframetestoffset = $offset + $ThisFileInfo['mpeg']['audio']['framelength'];
+		} else {
+			$ThisFileInfo['error'] .= "\n".'Frame at offset('.$offset.') is has an invalid frame length.';
+			return false;
+		}
+
+	}
+
+	$ExpectedNumberOfAudioBytes = 0;
+
+	////////////////////////////////////////////////////////////////////////////////////
+	// Variable-bitrate headers
+
+	if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
+		// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
+		// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
+
+		$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
+		$ThisFileInfo['mpeg']['audio']['VBR_method']   = 'Fraunhofer';
+		$ThisFileInfo['audio']['codec']                = 'Fraunhofer';
+
+		$SideInfoData = substr($headerstring, 4 + 2, 32);
+
+		$FraunhoferVBROffset = 36;
+
+		$ThisFileInfo['mpeg']['audio']['VBR_encoder_version']     = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2));
+		$ThisFileInfo['mpeg']['audio']['VBR_encoder_delay']       = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2));
+		$ThisFileInfo['mpeg']['audio']['VBR_quality']             = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2));
+		$ThisFileInfo['mpeg']['audio']['VBR_bytes']               = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4));
+		$ThisFileInfo['mpeg']['audio']['VBR_frames']              = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4));
+		$ThisFileInfo['mpeg']['audio']['VBR_seek_offsets']        = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2));
+		//$ThisFileInfo['mpeg']['audio']['reserved']              = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 4)); // hardcoded $00 $01 $00 $02  - purpose unknown
+		$ThisFileInfo['mpeg']['audio']['VBR_seek_offsets_stride'] = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2));
+
+		$ExpectedNumberOfAudioBytes = $ThisFileInfo['mpeg']['audio']['VBR_bytes'];
+
+		$previousbyteoffset = $offset;
+		for ($i = 0; $i < $ThisFileInfo['mpeg']['audio']['VBR_seek_offsets']; $i++) {
+			$Fraunhofer_OffsetN = BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, 2));
+			$FraunhoferVBROffset += 2;
+			$ThisFileInfo['mpeg']['audio']['VBR_offsets_relative'][$i] = $Fraunhofer_OffsetN;
+			$ThisFileInfo['mpeg']['audio']['VBR_offsets_absolute'][$i] = $Fraunhofer_OffsetN + $previousbyteoffset;
+			$previousbyteoffset += $Fraunhofer_OffsetN;
+		}
+
+
+	} else {
+
+		// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
+		// depending on MPEG layer and number of channels
+
+		if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+			if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') {
+				// MPEG-1 (mono)
+				$VBRidOffset  = 4 + 17; // 0x15
+				$SideInfoData = substr($headerstring, 4 + 2, 17);
+			} else {
+				// MPEG-1 (stereo, joint-stereo, dual-channel)
+				$VBRidOffset = 4 + 32; // 0x24
+				$SideInfoData = substr($headerstring, 4 + 2, 32);
+			}
+		} else { // 2 or 2.5
+			if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') {
+				// MPEG-2, MPEG-2.5 (mono)
+				$VBRidOffset = 4 + 9;  // 0x0D
+				$SideInfoData = substr($headerstring, 4 + 2, 9);
+			} else {
+				// MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
+				$VBRidOffset = 4 + 17; // 0x15
+				$SideInfoData = substr($headerstring, 4 + 2, 17);
+			}
+		}
+
+		if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
+			// 'Xing' is traditional Xing VBR frame
+			// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
+
+			$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
+			$ThisFileInfo['mpeg']['audio']['VBR_method']   = 'Xing';
+
+			$ThisFileInfo['mpeg']['audio']['xing_flags_raw'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
+
+			$ThisFileInfo['mpeg']['audio']['xing_flags']['frames']    = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000001);
+			$ThisFileInfo['mpeg']['audio']['xing_flags']['bytes']     = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000002);
+			$ThisFileInfo['mpeg']['audio']['xing_flags']['toc']       = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000004);
+			$ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale'] = (bool) ($ThisFileInfo['mpeg']['audio']['xing_flags_raw'] & 0x00000008);
+
+			if ($ThisFileInfo['mpeg']['audio']['xing_flags']['frames']) {
+				$ThisFileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
+			}
+			if ($ThisFileInfo['mpeg']['audio']['xing_flags']['bytes']) {
+				$ThisFileInfo['mpeg']['audio']['VBR_bytes']  = BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
+			}
+
+			if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') && !empty($ThisFileInfo['mpeg']['audio']['VBR_frames']) && !empty($ThisFileInfo['mpeg']['audio']['VBR_bytes'])) {
+				$framelengthfloat = $ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames'];
+				if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') {
+					// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
+					$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 12;
+				} else {
+					// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
+					$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 144;
+				}
+				$ThisFileInfo['mpeg']['audio']['framelength'] = floor($framelengthfloat);
+			}
+
+			if ($ThisFileInfo['mpeg']['audio']['xing_flags']['toc']) {
+				$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
+				for ($i = 0; $i < 100; $i++) {
+					$ThisFileInfo['mpeg']['audio']['toc'][$i] = ord($LAMEtocData{$i});
+				}
+			}
+			if ($ThisFileInfo['mpeg']['audio']['xing_flags']['vbr_scale']) {
+				$ThisFileInfo['mpeg']['audio']['VBR_scale'] = BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
+			}
+
+			// http://gabriel.mp3-tech.org/mp3infotag.html
+			if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
+				$ThisFileInfo['mpeg']['audio']['LAME']['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
+				$ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], 0, 9);
+				$ThisFileInfo['mpeg']['audio']['LAME']['long_version']  = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x55\xAA");
+
+				if ($ThisFileInfo['mpeg']['audio']['LAME']['short_version'] >= 'LAME3.90.') {
+
+					// It the LAME tag was only introduced in LAME v3.90
+					// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
+
+					// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
+					// are assuming a 'Xing' identifier offset of 0x24, which is the case for
+					// MPEG-1 non-mono, but not for other combinations
+					$LAMEtagOffsetContant = $VBRidOffset - 0x24;
+
+					// byte $9B  VBR Quality
+					// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
+					// Actually overwrites original Xing bytes
+					unset($ThisFileInfo['mpeg']['audio']['VBR_scale']);
+					$ThisFileInfo['mpeg']['audio']['LAME']['vbr_quality'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
+
+					// bytes $9C-$A4  Encoder short VersionString
+					$ThisFileInfo['mpeg']['audio']['LAME']['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
+					$ThisFileInfo['mpeg']['audio']['LAME']['long_version']  = $ThisFileInfo['mpeg']['audio']['LAME']['short_version'];
+
+					// byte $A5  Info Tag revision + VBR method
+					$LAMEtagRevisionVBRmethod = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
+
+					$ThisFileInfo['mpeg']['audio']['LAME']['tag_revision']      = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
+					$ThisFileInfo['mpeg']['audio']['LAME']['vbr_method']        = LAMEvbrMethodLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method']);
+
+					// byte $A6  Lowpass filter value
+					$ThisFileInfo['mpeg']['audio']['LAME']['lowpass_frequency'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
+
+					// bytes $A7-$AE  Replay Gain
+					// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
+					// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
+					$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = BigEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio']      =   BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] =   BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
+
+					if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] == 0) {
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] = false;
+					}
+
+					if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] != 0) {
+						require_once(GETID3_INCLUDEPATH.'getid3.rgad.php');
+
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['name']        = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0xE000) >> 13;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['originator']  = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x1C00) >> 10;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['sign_bit']    = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x0200) >> 9;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['gain_adjust'] =  $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_radio'] & 0x01FF;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['name']       = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['name']);
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['originator']);
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['gain_db']    = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['raw']['sign_bit']);
+
+						if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] !== false) {
+							$ThisFileInfo['replay_gain']['radio']['peak']   = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'];
+						}
+						$ThisFileInfo['replay_gain']['radio']['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['originator'];
+						$ThisFileInfo['replay_gain']['radio']['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['radio']['gain_db'];
+					}
+					if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] != 0) {
+						require_once(GETID3_INCLUDEPATH.'getid3.rgad.php');
+
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['name']        = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0xE000) >> 13;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['originator']  = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x1C00) >> 10;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['sign_bit']    = ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x0200) >> 9;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['gain_adjust'] = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['RGAD_audiophile'] & 0x01FF;
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['name']       = RGADnameLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['name']);
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['originator'] = RGADoriginatorLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['originator']);
+						$ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['gain_db']    = RGADadjustmentLookup($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['gain_adjust'], $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['raw']['sign_bit']);
+
+						if ($ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'] !== false) {
+							$ThisFileInfo['replay_gain']['audiophile']['peak']   = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['peak_amplitude'];
+						}
+						$ThisFileInfo['replay_gain']['audiophile']['originator'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['originator'];
+						$ThisFileInfo['replay_gain']['audiophile']['adjustment'] = $ThisFileInfo['mpeg']['audio']['LAME']['RGAD']['audiophile']['gain_db'];
+					}
+
+
+					// byte $AF  Encoding flags + ATH Type
+					$EncodingFlagsATHtype = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
+					$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
+					$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
+					$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
+					$ThisFileInfo['mpeg']['audio']['LAME']['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
+					$ThisFileInfo['mpeg']['audio']['LAME']['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
+
+					// byte $B0  if ABR {specified bitrate} else {minimal bitrate}
+					$ABRbitrateMinBitrate = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
+					if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] == 2) { // Average BitRate (ABR)
+						$ThisFileInfo['mpeg']['audio']['LAME']['bitrate_abr'] = $ABRbitrateMinBitrate;
+					} elseif ($ABRbitrateMinBitrate > 0) { // Variable BitRate (VBR) - minimum bitrate
+						$ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] = $ABRbitrateMinBitrate;
+					}
+
+					// bytes $B1-$B3  Encoder delays
+					$EncoderDelays = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
+					$ThisFileInfo['mpeg']['audio']['LAME']['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
+					$ThisFileInfo['mpeg']['audio']['LAME']['end_padding']   =  $EncoderDelays & 0x000FFF;
+
+					// byte $B4  Misc
+					$MiscByte = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['noise_shaping']       = ($MiscByte & 0x03);
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
+					$ThisFileInfo['mpeg']['audio']['LAME']['noise_shaping']       = $ThisFileInfo['mpeg']['audio']['LAME']['raw']['noise_shaping'];
+					$ThisFileInfo['mpeg']['audio']['LAME']['stereo_mode']         = LAMEmiscStereoModeLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['stereo_mode']);
+					$ThisFileInfo['mpeg']['audio']['LAME']['not_optimal_quality'] = (bool) $ThisFileInfo['mpeg']['audio']['LAME']['raw']['not_optimal_quality'];
+					$ThisFileInfo['mpeg']['audio']['LAME']['source_sample_freq']  = LAMEmiscSourceSampleFrequencyLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['source_sample_freq']);
+
+					// byte $B5  MP3 Gain
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['mp3_gain'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
+					$ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db']     = 1.5 * $ThisFileInfo['mpeg']['audio']['LAME']['raw']['mp3_gain'];
+					$ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_factor'] = pow(2, ($ThisFileInfo['mpeg']['audio']['LAME']['mp3_gain_db'] / 6));
+
+					// bytes $B6-$B7  Preset and surround info
+					$PresetSurroundBytes = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
+					// Reserved                                                    = ($PresetSurroundBytes & 0xC000);
+					$ThisFileInfo['mpeg']['audio']['LAME']['raw']['surround_info'] = ($PresetSurroundBytes & 0x3800);
+					$ThisFileInfo['mpeg']['audio']['LAME']['surround_info']        = LAMEsurroundInfoLookup($ThisFileInfo['mpeg']['audio']['LAME']['raw']['surround_info']);
+					$ThisFileInfo['mpeg']['audio']['LAME']['preset_used_id']       = ($PresetSurroundBytes & 0x07FF);
+
+					// bytes $B8-$BB  MusicLength
+					$ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
+					$ExpectedNumberOfAudioBytes = (($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] > 0) ? $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'] : $ThisFileInfo['mpeg']['audio']['VBR_bytes']);
+
+					// bytes $BC-$BD  MusicCRC
+					$ThisFileInfo['mpeg']['audio']['LAME']['music_crc']    = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
+
+					// bytes $BE-$BF  CRC-16 of Info Tag
+					$ThisFileInfo['mpeg']['audio']['LAME']['lame_tag_crc'] = BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
+
+
+					// LAME CBR
+					if ($ThisFileInfo['mpeg']['audio']['LAME']['raw']['vbr_method'] == 1) {
+
+						$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
+						if (empty($ThisFileInfo['mpeg']['audio']['bitrate']) || ($ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'] != 255)) {
+							$ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['LAME']['bitrate_min'];
+						}
+
+					}
+
+				}
+			}
+
+		} else {
+
+			// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
+			$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
+			if ($recursivesearch) {
+				$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
+				if (RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) {
+					$recursivesearch = false;
+					$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
+				}
+				if ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') {
+					$ThisFileInfo['warning'] .= "\n".'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
+				}
+			}
+
+		}
+
+	}
+
+	if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) {
+		if (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) {
+			$ThisFileInfo['warning'] .= "\n".'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
+		} elseif ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) {
+			$ThisFileInfo['warning'] .= "\n".'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)';
+		} else {
+			$ThisFileInfo['warning'] .= "\n".'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
+		}
+	}
+
+	if (($ThisFileInfo['mpeg']['audio']['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) {
+		if (($offset == $ThisFileInfo['avdataoffset']) && empty($ThisFileInfo['mpeg']['audio']['VBR_frames'])) {
+			$framebytelength = FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true);
+			if ($framebytelength > 0) {
+				$ThisFileInfo['mpeg']['audio']['framelength'] = $framebytelength;
+				if ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') {
+					// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
+					$ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 12;
+				} else {
+					// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
+					$ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($ThisFileInfo['mpeg']['audio']['padding'])) * $ThisFileInfo['mpeg']['audio']['sample_rate']) / 144;
+				}
+			} else {
+				$ThisFileInfo['error'] .= "\n".'Error calculating frame length of free-format MP3 without Xing/LAME header';
+			}
+		}
+	}
+
+	if (($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && isset($ThisFileInfo['mpeg']['audio']['VBR_frames']) && ($ThisFileInfo['mpeg']['audio']['VBR_frames'] > 1)) {
+		$ThisFileInfo['mpeg']['audio']['VBR_frames']--; // don't count the Xing / VBRI frame
+		if (($ThisFileInfo['mpeg']['audio']['version'] == '1') && ($ThisFileInfo['mpeg']['audio']['layer'] == 'I')) {
+			$ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = ((($ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384)) / 1000;
+		} elseif ((($ThisFileInfo['mpeg']['audio']['version'] == '2') || ($ThisFileInfo['mpeg']['audio']['version'] == '2.5')) && ($ThisFileInfo['mpeg']['audio']['layer'] == 'III')) {
+			$ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = ((($ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576)) / 1000;
+		} else {
+			$ThisFileInfo['mpeg']['audio']['VBR_bitrate'] = ((($ThisFileInfo['mpeg']['audio']['VBR_bytes'] / $ThisFileInfo['mpeg']['audio']['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152)) / 1000;
+		}
+		if ($ThisFileInfo['mpeg']['audio']['VBR_bitrate'] > 0) {
+			$ThisFileInfo['audio']['bitrate']         = 1000 * $ThisFileInfo['mpeg']['audio']['VBR_bitrate'];
+			$ThisFileInfo['mpeg']['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['VBR_bitrate']; // to avoid confusion
+		}
+	}
+
+	// End variable-bitrate headers
+	////////////////////////////////////////////////////////////////////////////////////
+
+	if ($recursivesearch) {
+
+		if (!RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) {
+			return false;
+		}
+
+	}
+
+
+	//if (false) {
+	//	// experimental side info parsing section - not returning anything useful yet
+    //
+	//	$SideInfoBitstream = BigEndian2Bin($SideInfoData);
+	//	$SideInfoOffset = 0;
+    //
+	//	if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+	//		if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') {
+	//			// MPEG-1 (mono)
+	//			$ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+	//			$SideInfoOffset += 9;
+	//			$SideInfoOffset += 5;
+	//		} else {
+	//			// MPEG-1 (stereo, joint-stereo, dual-channel)
+	//			$ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+	//			$SideInfoOffset += 9;
+	//			$SideInfoOffset += 3;
+	//		}
+	//	} else { // 2 or 2.5
+	//		if ($ThisFileInfo['mpeg']['audio']['channelmode'] == 'mono') {
+	//			// MPEG-2, MPEG-2.5 (mono)
+	//			$ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
+	//			$SideInfoOffset += 8;
+	//			$SideInfoOffset += 1;
+	//		} else {
+	//			// MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
+	//			$ThisFileInfo['mpeg']['audio']['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
+	//			$SideInfoOffset += 8;
+	//			$SideInfoOffset += 2;
+	//		}
+	//	}
+    //
+	//	if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+	//		for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
+	//			for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
+	//				$ThisFileInfo['mpeg']['audio']['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+	//				$SideInfoOffset += 2;
+	//			}
+	//		}
+	//	}
+	//	for ($granule = 0; $granule < (($ThisFileInfo['mpeg']['audio']['version'] == '1') ? 2 : 1); $granule++) {
+	//		for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
+	//			$ThisFileInfo['mpeg']['audio']['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
+	//			$SideInfoOffset += 12;
+	//			$ThisFileInfo['mpeg']['audio']['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+	//			$SideInfoOffset += 9;
+	//			$ThisFileInfo['mpeg']['audio']['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
+	//			$SideInfoOffset += 8;
+	//			if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+	//				$ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
+	//				$SideInfoOffset += 4;
+	//			} else {
+	//				$ThisFileInfo['mpeg']['audio']['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+	//				$SideInfoOffset += 9;
+	//			}
+	//			$ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+	//			$SideInfoOffset += 1;
+    //
+	//			if ($ThisFileInfo['mpeg']['audio']['window_switching_flag'][$granule][$channel] == '1') {
+    //
+	//				$ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
+	//				$SideInfoOffset += 2;
+	//				$ThisFileInfo['mpeg']['audio']['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+	//				$SideInfoOffset += 1;
+    //
+	//				for ($region = 0; $region < 2; $region++) {
+	//					$ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
+	//					$SideInfoOffset += 5;
+	//				}
+	//				$ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][2] = 0;
+    //
+	//				for ($window = 0; $window < 3; $window++) {
+	//					$ThisFileInfo['mpeg']['audio']['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
+	//					$SideInfoOffset += 3;
+	//				}
+    //
+	//			} else {
+    //
+	//				for ($region = 0; $region < 3; $region++) {
+	//					$ThisFileInfo['mpeg']['audio']['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
+	//					$SideInfoOffset += 5;
+	//				}
+    //
+	//				$ThisFileInfo['mpeg']['audio']['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
+	//				$SideInfoOffset += 4;
+	//				$ThisFileInfo['mpeg']['audio']['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
+	//				$SideInfoOffset += 3;
+	//				$ThisFileInfo['mpeg']['audio']['block_type'][$granule][$channel] = 0;
+	//			}
+    //
+	//			if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+	//				$ThisFileInfo['mpeg']['audio']['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+	//				$SideInfoOffset += 1;
+	//			}
+	//			$ThisFileInfo['mpeg']['audio']['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+	//			$SideInfoOffset += 1;
+	//			$ThisFileInfo['mpeg']['audio']['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+	//			$SideInfoOffset += 1;
+	//		}
+	//	}
+	//}
+
+	return true;
+}
+
+function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) {
+	for ($i = 0; $i < MPEG_VALID_CHECK_FRAMES; $i++) {
+		// check next MPEG_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
+		if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) {
+			// end of file
+			return true;
+		}
+
+		$nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']);
+		if (decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) {
+			if ($ScanAsCBR) {
+				// force CBR mode, used for trying to pick out invalid audio streams with
+				// valid(?) VBR headers, or VBR streams with no VBR header
+				if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) {
+					return false;
+				}
+			}
+
+
+			// next frame is OK, get ready to check the one after that
+			if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
+				$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
+			} else {
+				$ThisFileInfo['error'] .= "\n".'Frame at offset ('.$offset.') is has an invalid frame length.';
+				return false;
+			}
+
+		} else {
+
+			// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
+			$ThisFileInfo['error'] .= "\n".'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
+
+			return false;
+		}
+	}
+	return true;
+}
+
+function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) {
+	fseek($fd, $offset, SEEK_SET);
+	$MPEGaudioData = fread($fd, 32768);
+
+	$SyncPattern1 = substr($MPEGaudioData, 0, 4);
+	// may be different pattern due to padding
+	$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
+	if ($SyncPattern2 === $SyncPattern1) {
+		$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
+	}
+
+	$framelength = false;
+	$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
+	$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
+	if ($framelength1 > 4) {
+		$framelength = $framelength1;
+	}
+	if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
+		$framelength = $framelength2;
+	}
+	if (!$framelength) {
+
+		// LAME 3.88 has a different value for modeextension on the first frame vs the rest
+		$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
+		$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
+
+		if ($framelength1 > 4) {
+			$framelength = $framelength1;
+		}
+		if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
+			$framelength = $framelength2;
+		}
+		if (!$framelength) {
+			$ThisFileInfo['error'] .= "\n".'Cannot find next free-format synch pattern ('.PrintHexBytes($SyncPattern1).' or '.PrintHexBytes($SyncPattern2).') after offset '.$offset;
+			return false;
+		} else {
+			$ThisFileInfo['warning'] .= "\n".'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
+			$ThisFileInfo['audio']['codec']   = 'LAME';
+			$ThisFileInfo['audio']['encoder'] = 'LAME3.88';
+			$SyncPattern1 = substr($SyncPattern1, 0, 3);
+			$SyncPattern2 = substr($SyncPattern2, 0, 3);
+		}
+	}
+
+	if ($deepscan) {
+
+		$ActualFrameLengthValues = array();
+		$nextoffset = $offset + $framelength;
+		while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) {
+			fseek($fd, $nextoffset - 1, SEEK_SET);
+			$NextSyncPattern = fread($fd, 6);
+			if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
+				// good - found where expected
+				$ActualFrameLengthValues[] = $framelength;
+			} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
+				// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
+				$ActualFrameLengthValues[] = ($framelength - 1);
+				$nextoffset--;
+			} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
+				// ok - found one byte later than expected (last frame was padded, first frame wasn't)
+				$ActualFrameLengthValues[] = ($framelength + 1);
+				$nextoffset++;
+			} else {
+				$ThisFileInfo['error'] .= "\n".'Did not find expected free-format sync pattern at offset '.$nextoffset;
+				return false;
+			}
+			$nextoffset += $framelength;
+		}
+		if (count($ActualFrameLengthValues) > 0) {
+			$framelength = round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues));
+		}
+	}
+	return $framelength;
+}
+
+
+function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) {
+	// looks for synch, decodes MPEG audio header
+
+	fseek($fd, $avdataoffset, SEEK_SET);
+	$header = '';
+	$SynchSeekOffset = 0;
+
+	if (!defined('CONST_FF')) {
+		define('CONST_FF', chr(0xFF));
+		define('CONST_E0', chr(0xE0));
+	}
+
+	static $MPEGaudioVersionLookup;
+	static $MPEGaudioLayerLookup;
+	static $MPEGaudioBitrateLookup;
+	if (empty($MPEGaudioVersionLookup)) {
+		$MPEGaudioVersionLookup = MPEGaudioVersionArray();
+		$MPEGaudioLayerLookup   = MPEGaudioLayerArray();
+		$MPEGaudioBitrateLookup = MPEGaudioBitrateArray();
+
+	}
+
+	$header_len = strlen($header) - round(FREAD_BUFFER_SIZE / 2);
+	while (true) {
+
+		if (($SynchSeekOffset > $header_len) && (($avdataoffset + $SynchSeekOffset)  < $ThisFileInfo['avdataend']) && !feof($fd)) {
+
+			if ($SynchSeekOffset > 131072) {
+				// if a synch's not found within the first 128k bytes, then give up
+				$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch within the first 131072 bytes';
+				if (isset($ThisFileInfo['audio']['bitrate'])) {
+					unset($ThisFileInfo['audio']['bitrate']);
+				}
+				if (isset($ThisFileInfo['mpeg']['audio'])) {
+					unset($ThisFileInfo['mpeg']['audio']);
+				}
+				if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) {
+					unset($ThisFileInfo['mpeg']);
+				}
+				return false;
+
+			} elseif ($header .= fread($fd, FREAD_BUFFER_SIZE)) {
+
+				// great
+				$header_len = strlen($header) - round(FREAD_BUFFER_SIZE / 2);
+
+			} else {
+
+				$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch before end of file';
+				if (isset($ThisFileInfo['audio']['bitrate'])) {
+					unset($ThisFileInfo['audio']['bitrate']);
+				}
+				if (isset($ThisFileInfo['mpeg']['audio'])) {
+					unset($ThisFileInfo['mpeg']['audio']);
+				}
+				if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) {
+					unset($ThisFileInfo['mpeg']);
+				}
+				return false;
+
+			}
+		}
+
+		if (($SynchSeekOffset + 1) >= strlen($header)) {
+			$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch before end of file';
+			return false;
+		}
+
+		if (($header{$SynchSeekOffset} == CONST_FF) && ($header{($SynchSeekOffset + 1)} > CONST_E0)) { // synch detected
+
+			if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) {
+				$FirstFrameThisfileInfo = $ThisFileInfo;
+				$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
+				if (!decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) {
+					// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
+					// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
+					unset($FirstFrameThisfileInfo);
+				}
+			}
+			$dummy = $ThisFileInfo; // only overwrite real data if valid header found
+
+			if (decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) {
+
+				$ThisFileInfo = $dummy;
+				$ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
+				switch ($ThisFileInfo['fileformat']) {
+					case '':
+					case 'id3':
+					case 'ape':
+					case 'mp3':
+						$ThisFileInfo['fileformat']               = 'mp3';
+						$ThisFileInfo['audio']['dataformat']      = 'mp3';
+				}
+				if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
+					if (!CloseMatch($ThisFileInfo['audio']['bitrate'], $FirstFrameThisfileInfo['audio']['bitrate'], 1)) {
+						// If there is garbage data between a valid VBR header frame and a sequence
+						// of valid MPEG-audio frames the VBR data is no longer discarded.
+						$ThisFileInfo = $FirstFrameThisfileInfo;
+						$ThisFileInfo['avdataoffset']        = $FirstFrameAVDataOffset;
+						$ThisFileInfo['fileformat']          = 'mp3';
+						$ThisFileInfo['audio']['dataformat'] = 'mp3';
+						$dummy                               = $ThisFileInfo;
+						unset($dummy['mpeg']['audio']);
+						$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
+						$GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
+						if (decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) {
+
+							$ThisFileInfo = $dummy;
+							$ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd;
+							$ThisFileInfo['warning'] .= "\n".'apparently-valid VBR header not used because could not find '.MPEG_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
+
+						} else {
+
+							$ThisFileInfo['warning'] .= "\n".'using data from VBR header even though could not find '.MPEG_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
+
+						}
+					}
+				}
+
+				if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) {
+					// VBR file with no VBR header
+					$BitrateHistogram = true;
+				}
+
+				if ($BitrateHistogram) {
+
+					$ThisFileInfo['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
+					$ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
+
+					if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+						if ($ThisFileInfo['mpeg']['audio']['layer'] == 'III') {
+							$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 40=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 160=>0, 192=>0, 224=>0, 256=>0, 320=>0);
+						} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'II') {
+							$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 160=>0, 192=>0, 224=>0, 256=>0, 320=>0, 384=>0);
+						} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') {
+							$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 64=>0, 96=>0, 128=>0, 160=>0, 192=>0, 224=>0, 256=>0, 288=>0, 320=>0, 352=>0, 384=>0, 416=>0, 448=>0);
+						}
+					} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 'I') {
+						$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 144=>0, 160=>0, 176=>0, 192=>0, 224=>0, 256=>0);
+					} else {
+						$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8=>0, 16=>0, 24=>0, 32=>0, 40=>0, 48=>0, 56=>0, 64=>0, 80=>0, 96=>0, 112=>0, 128=>0, 144=>0, 160=>0);
+					}
+
+					$dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']);
+					$synchstartoffset = $ThisFileInfo['avdataoffset'];
+
+					$FastMode = false;
+					while (decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) {
+						$FastMode = true;
+						$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
+
+						$ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++;
+						$ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++;
+						$ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++;
+						if (empty($dummy['mpeg']['audio']['framelength'])) {
+							$ThisFileInfo['warning'] .= "\n".'Invalid/missing framelength in histogram analysis - aborting';
+$synchstartoffset += 4;
+//							return false;
+						}
+						$synchstartoffset += $dummy['mpeg']['audio']['framelength'];
+					}
+
+					$bittotal     = 0;
+					$framecounter = 0;
+					foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
+						$framecounter += $bitratecount;
+						if ($bitratevalue != 'free') {
+							$bittotal += ($bitratevalue * $bitratecount);
+						}
+					}
+					if ($framecounter == 0) {
+						$ThisFileInfo['error'] .= "\n".'Corrupt MP3 file: framecounter == zero';
+						return false;
+					}
+					$ThisFileInfo['mpeg']['audio']['frame_count'] = $framecounter;
+					$ThisFileInfo['mpeg']['audio']['bitrate']     = 1000 * ($bittotal / $framecounter);
+
+					$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
+
+
+					// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
+					$distinct_bitrates = 0;
+					foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
+						if ($bitrate_count > 0) {
+							$distinct_bitrates++;
+						}
+					}
+					if ($distinct_bitrates > 1) {
+						$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
+					} else {
+						$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
+					}
+					$ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
+
+				}
+
+				break; // exit while()
+			}
+		}
+
+		$SynchSeekOffset++;
+		if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) {
+			// end of file/data
+
+			if (empty($ThisFileInfo['mpeg']['audio'])) {
+
+				$ThisFileInfo['error'] .= "\n".'could not find valid MPEG synch before end of file';
+				if (isset($ThisFileInfo['audio']['bitrate'])) {
+					unset($ThisFileInfo['audio']['bitrate']);
+				}
+				if (isset($ThisFileInfo['mpeg']['audio'])) {
+					unset($ThisFileInfo['mpeg']['audio']);
+				}
+				if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) {
+					unset($ThisFileInfo['mpeg']);
+				}
+				return false;
+
+			}
+			break;
+		}
+
+	}
+	$ThisFileInfo['audio']['bits_per_sample'] = 16;
+	$ThisFileInfo['audio']['channels']        = $ThisFileInfo['mpeg']['audio']['channels'];
+	$ThisFileInfo['audio']['channelmode']     = $ThisFileInfo['mpeg']['audio']['channelmode'];
+	$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+	return true;
+}
+
+
+function MPEGaudioVersionArray() {
+	static $MPEGaudioVersion = array('2.5', false, '2', '1');
+	return $MPEGaudioVersion;
+}
+
+function MPEGaudioLayerArray() {
+	static $MPEGaudioLayer = array(false, 'III', 'II', 'I');
+	return $MPEGaudioLayer;
+}
+
+function MPEGaudioBitrateArray() {
+	static $MPEGaudioBitrate;
+	if (empty($MPEGaudioBitrate)) {
+		$MPEGaudioBitrate['1']['I']     = array('free', 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448);
+		$MPEGaudioBitrate['1']['II']    = array('free', 32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384);
+		$MPEGaudioBitrate['1']['III']   = array('free', 32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320);
+		$MPEGaudioBitrate['2']['I']     = array('free', 32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256);
+		$MPEGaudioBitrate['2']['II']    = array('free',  8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160);
+		$MPEGaudioBitrate['2']['III']   = $MPEGaudioBitrate['2']['II'];
+		$MPEGaudioBitrate['2.5']['I']   = $MPEGaudioBitrate['2']['I'];
+		$MPEGaudioBitrate['2.5']['II']  = $MPEGaudioBitrate['2']['II'];
+		$MPEGaudioBitrate['2.5']['III'] = $MPEGaudioBitrate['2']['III'];
+	}
+	return $MPEGaudioBitrate;
+}
+
+function MPEGaudioFrequencyArray() {
+	static $MPEGaudioFrequency;
+	if (empty($MPEGaudioFrequency)) {
+		$MPEGaudioFrequency['1']   = array(44100, 48000, 32000);
+		$MPEGaudioFrequency['2']   = array(22050, 24000, 16000);
+		$MPEGaudioFrequency['2.5'] = array(11025, 12000,  8000);
+	}
+	return $MPEGaudioFrequency;
+}
+
+function MPEGaudioChannelModeArray() {
+	static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
+	return $MPEGaudioChannelMode;
+}
+
+function MPEGaudioModeExtensionArray() {
+	static $MPEGaudioModeExtension;
+	if (empty($MPEGaudioModeExtension)) {
+		$MPEGaudioModeExtension['I']   = array('4-31', '8-31', '12-31', '16-31');
+		$MPEGaudioModeExtension['II']  = array('4-31', '8-31', '12-31', '16-31');
+		$MPEGaudioModeExtension['III'] = array('', 'IS', 'MS', 'IS+MS');
+	}
+	return $MPEGaudioModeExtension;
+}
+
+function MPEGaudioEmphasisArray() {
+	static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
+	return $MPEGaudioEmphasis;
+}
+
+
+function MPEGaudioHeaderBytesValid($head4) {
+	return MPEGaudioHeaderValid(MPEGaudioHeaderDecode($head4));
+}
+
+function MPEGaudioHeaderValid($rawarray, $echoerrors=false) {
+
+	if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
+		return false;
+	}
+
+	static $MPEGaudioVersionLookup;
+	static $MPEGaudioLayerLookup;
+	static $MPEGaudioBitrateLookup;
+	static $MPEGaudioFrequencyLookup;
+	static $MPEGaudioChannelModeLookup;
+	static $MPEGaudioModeExtensionLookup;
+	static $MPEGaudioEmphasisLookup;
+	if (empty($MPEGaudioVersionLookup)) {
+		$MPEGaudioVersionLookup       = MPEGaudioVersionArray();
+		$MPEGaudioLayerLookup         = MPEGaudioLayerArray();
+		$MPEGaudioBitrateLookup       = MPEGaudioBitrateArray();
+		$MPEGaudioFrequencyLookup     = MPEGaudioFrequencyArray();
+		$MPEGaudioChannelModeLookup   = MPEGaudioChannelModeArray();
+		$MPEGaudioModeExtensionLookup = MPEGaudioModeExtensionArray();
+		$MPEGaudioEmphasisLookup      = MPEGaudioEmphasisArray();
+	}
+
+	if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
+		$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
+	} else {
+		if ($echoerrors) {
+			echo "\n".'invalid Version ('.$rawarray['version'].')';
+		}
+		return false;
+	}
+	if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
+		$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
+	} else {
+		if ($echoerrors) {
+			echo "\n".'invalid Layer ('.$rawarray['layer'].')';
+		}
+		return false;
+	}
+	if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
+		if ($echoerrors) {
+			echo "\n".'invalid Bitrate ('.$rawarray['bitrate'].')';
+		}
+		if ($rawarray['bitrate'] == 15) {
+			// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
+			// let it go through here otherwise file will not be identified
+		} else {
+			return false;
+		}
+	}
+	if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
+		if ($echoerrors) {
+			echo "\n".'invalid Frequency ('.$rawarray['sample_rate'].')';
+		}
+		return false;
+	}
+	if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
+		if ($echoerrors) {
+			echo "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')';
+		}
+		return false;
+	}
+	if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
+		if ($echoerrors) {
+			echo "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')';
+		}
+		return false;
+	}
+	if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
+		if ($echoerrors) {
+			echo "\n".'invalid Emphasis ('.$rawarray['emphasis'].')';
+		}
+		return false;
+	}
+	// These are just either set or not set, you can't mess that up :)
+	// $rawarray['protection'];
+	// $rawarray['padding'];
+	// $rawarray['private'];
+	// $rawarray['copyright'];
+	// $rawarray['original'];
+
+	return true;
+}
+
+function MPEGaudioHeaderDecode($Header4Bytes) {
+	// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
+	// A - Frame sync (all bits set)
+	// B - MPEG Audio version ID
+	// C - Layer description
+	// D - Protection bit
+	// E - Bitrate index
+	// F - Sampling rate frequency index
+	// G - Padding bit
+	// H - Private bit
+	// I - Channel Mode
+	// J - Mode extension (Only if Joint stereo)
+	// K - Copyright
+	// L - Original
+	// M - Emphasis
+
+	if (strlen($Header4Bytes) != 4) {
+		return false;
+	}
+
+	$MPEGrawHeader['synch']         = (BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
+	$MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
+	$MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
+	$MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
+	$MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
+	$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
+	$MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
+	$MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
+	$MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
+	$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
+	$MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
+	$MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
+	$MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
+
+	return $MPEGrawHeader;
+}
+
+function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
+	static $AudioFrameLengthCache = array();
+
+	if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
+		$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
+		if ($bitrate != 'free') {
+
+			if ($version == '1') {
+
+				if ($layer == 'I') {
+
+					// For Layer I slot is 32 bits long
+					$FrameLengthCoefficient = 48;
+					$SlotLength = 4;
+
+				} else { // Layer II / III
+
+					// for Layer II and Layer III slot is 8 bits long.
+					$FrameLengthCoefficient = 144;
+					$SlotLength = 1;
+
+				}
+
+			} else { // MPEG-2 / MPEG-2.5
+
+				if ($layer == 'I') {
+
+					// For Layer I slot is 32 bits long
+					$FrameLengthCoefficient = 24;
+					$SlotLength = 4;
+
+				} elseif ($layer == 'II') {
+
+					// for Layer II and Layer III slot is 8 bits long.
+					$FrameLengthCoefficient = 144;
+					$SlotLength = 1;
+
+				} else { // III
+
+					// for Layer II and Layer III slot is 8 bits long.
+					$FrameLengthCoefficient = 72;
+					$SlotLength = 1;
+
+				}
+
+			}
+
+			// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
+			// http://66.96.216.160/cgi-bin/YaBB.pl?board=c&action=display&num=1018474068
+			// -> [Finding the next frame synch] on www.r3mix.net forums if the above link goes dead
+			if ($samplerate > 0) {
+				$NewFramelength  = ($FrameLengthCoefficient * $bitrate * 1000) / $samplerate;
+				$NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer II/III, 4 bytes for Layer I)
+				if ($padding) {
+					$NewFramelength += $SlotLength;
+				}
+				$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
+			}
+		}
+	}
+	return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
+}
+
+function LAMEvbrMethodLookup($VBRmethodID) {
+	static $LAMEvbrMethodLookup = array();
+	if (empty($LAMEvbrMethodLookup)) {
+		$LAMEvbrMethodLookup[0x00] = 'unknown';
+		$LAMEvbrMethodLookup[0x01] = 'cbr';
+		$LAMEvbrMethodLookup[0x02] = 'abr';
+		$LAMEvbrMethodLookup[0x03] = 'vbr-old / vbr-rh';
+		$LAMEvbrMethodLookup[0x04] = 'vbr-mtrh';
+		$LAMEvbrMethodLookup[0x05] = 'vbr-new / vbr-mt';
+	}
+	return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
+}
+
+function LAMEmiscStereoModeLookup($StereoModeID) {
+	static $LAMEmiscStereoModeLookup = array();
+	if (empty($LAMEmiscStereoModeLookup)) {
+		$LAMEmiscStereoModeLookup[0] = 'mono';
+		$LAMEmiscStereoModeLookup[1] = 'stereo';
+		$LAMEmiscStereoModeLookup[2] = 'dual';
+		$LAMEmiscStereoModeLookup[3] = 'joint';
+		$LAMEmiscStereoModeLookup[4] = 'forced';
+		$LAMEmiscStereoModeLookup[5] = 'auto';
+		$LAMEmiscStereoModeLookup[6] = 'intensity';
+		$LAMEmiscStereoModeLookup[7] = 'other';
+	}
+	return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
+}
+
+function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
+	static $LAMEmiscSourceSampleFrequencyLookup = array();
+	if (empty($LAMEmiscSourceSampleFrequencyLookup)) {
+		$LAMEmiscSourceSampleFrequencyLookup[0] = '<= 32 kHz';
+		$LAMEmiscSourceSampleFrequencyLookup[1] = '44.1 kHz';
+		$LAMEmiscSourceSampleFrequencyLookup[2] = '48 kHz';
+		$LAMEmiscSourceSampleFrequencyLookup[3] = '> 48kHz';
+	}
+	return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
+}
+
+function LAMEsurroundInfoLookup($SurroundInfoID) {
+	static $LAMEsurroundInfoLookup = array();
+	if (empty($LAMEsurroundInfoLookup)) {
+		$LAMEsurroundInfoLookup[0] = 'no surround info';
+		$LAMEsurroundInfoLookup[1] = 'DPL encoding';
+		$LAMEsurroundInfoLookup[2] = 'DPL2 encoding';
+		$LAMEsurroundInfoLookup[3] = 'Ambisonic encoding';
+	}
+	return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.mysql.php b/apps/media/getID3/demos/demo.mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6b7c6b5effeb5cce5b5e09dea12e80e52466209
--- /dev/null
+++ b/apps/media/getID3/demos/demo.mysql.php
@@ -0,0 +1,2182 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.mysql.php - part of getID3()                     //
+// Sample script for recursively scanning directories and      //
+// storing the results in a database                           //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+//die('Due to a security issue, this demo has been disabled. It can be enabled by removing line 16 in demos/demo.mysql.php');
+
+
+// OPTIONS:
+$getid3_demo_mysql_encoding = 'ISO-8859-1';
+$getid3_demo_mysql_md5_data = false;        // All data hashes are by far the slowest part of scanning
+$getid3_demo_mysql_md5_file = false;
+
+define('GETID3_DB_HOST',  'localhost');
+define('GETID3_DB_USER',  'root');
+define('GETID3_DB_PASS',  'password');
+define('GETID3_DB_DB',    'getid3');
+define('GETID3_DB_TABLE', 'files');
+
+// CREATE DATABASE `getid3`;
+
+if (!@mysql_connect(GETID3_DB_HOST, GETID3_DB_USER, GETID3_DB_PASS)) {
+	die('Could not connect to MySQL host: <blockquote style="background-color: #FF9933; padding: 10px;">'.mysql_error().'</blockquote>');
+}
+if (!@mysql_select_db(GETID3_DB_DB)) {
+	die('Could not select database: <blockquote style="background-color: #FF9933; padding: 10px;">'.mysql_error().'</blockquote>');
+}
+
+if (!@include_once('../getid3/getid3.php')) {
+	die('Cannot open '.realpath('../getid3/getid3.php'));
+}
+// Initialize getID3 engine
+$getID3 = new getID3;
+$getID3->setOption(array(
+	'option_md5_data' => $getid3_demo_mysql_md5_data,
+	'encoding'        => $getid3_demo_mysql_encoding,
+));
+
+
+function RemoveAccents($string) {
+	// Revised version by markstewardØhotmail*com
+	return strtr(strtr($string, 'ŠŽšžŸÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÑÒÓÔÕÖØÙÚÛÜÝàáâãäåçèéêëìíîïñòóôõöøùúûüýÿ', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy'), array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u'));
+}
+
+function FixTextFields($text) {
+	$text = getid3_lib::SafeStripSlashes($text);
+	$text = htmlentities($text, ENT_QUOTES);
+	return $text;
+}
+
+function BitrateColor($bitrate, $BitrateMaxScale=768) {
+	// $BitrateMaxScale is bitrate of maximum-quality color (bright green)
+	// below this is gradient, above is solid green
+
+	$bitrate *= (256 / $BitrateMaxScale); // scale from 1-[768]kbps to 1-256
+	$bitrate = round(min(max($bitrate, 1), 256));
+	$bitrate--;    // scale from 1-256kbps to 0-255kbps
+
+	$Rcomponent = max(255 - ($bitrate * 2), 0);
+	$Gcomponent = max(($bitrate * 2) - 255, 0);
+	if ($bitrate > 127) {
+		$Bcomponent = max((255 - $bitrate) * 2, 0);
+	} else {
+		$Bcomponent = max($bitrate * 2, 0);
+	}
+	return str_pad(dechex($Rcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Gcomponent), 2, '0', STR_PAD_LEFT).str_pad(dechex($Bcomponent), 2, '0', STR_PAD_LEFT);
+}
+
+function BitrateText($bitrate, $decimals=0) {
+	return '<span style="color: #'.BitrateColor($bitrate).'">'.number_format($bitrate, $decimals).' kbps</span>';
+}
+
+function fileextension($filename, $numextensions=1) {
+	if (strstr($filename, '.')) {
+		$reversedfilename = strrev($filename);
+		$offset = 0;
+		for ($i = 0; $i < $numextensions; $i++) {
+			$offset = strpos($reversedfilename, '.', $offset + 1);
+			if ($offset === false) {
+				return '';
+			}
+		}
+		return strrev(substr($reversedfilename, 0, $offset));
+	}
+	return '';
+}
+
+function RenameFileFromTo($from, $to, &$results) {
+	$success = true;
+	if ($from === $to) {
+		$results = '<span style="color: #FF0000;"><b>Source and Destination filenames identical</b><br>FAILED to rename';
+	} elseif (!file_exists($from)) {
+		$results = '<span style="color: #FF0000;"><b>Source file does not exist</b><br>FAILED to rename';
+	} elseif (file_exists($to) && (strtolower($from) !== strtolower($to))) {
+		$results = '<span style="color: #FF0000;"><b>Destination file already exists</b><br>FAILED to rename';
+	} elseif (@rename($from, $to)) {
+		$SQLquery  = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`filename` = "'.mysql_escape_string($from).'")';
+		safe_mysql_query($SQLquery);
+		$results = '<span style="color: #008000;">Successfully renamed';
+	} else {
+		$results = '<br><span style="color: #FF0000;">FAILED to rename';
+		$success = false;
+	}
+	$results .= ' from:<br><i>'.$from.'</i><br>to:<br><i>'.$to.'</i></span><hr>';
+	return $success;
+}
+
+if (!empty($_REQUEST['renamefilefrom']) && !empty($_REQUEST['renamefileto'])) {
+
+	$results = '';
+	RenameFileFromTo($_REQUEST['renamefilefrom'], $_REQUEST['renamefileto'], $results);
+	echo $results;
+	exit;
+
+} elseif (!empty($_REQUEST['m3ufilename'])) {
+
+	header('Content-type: audio/x-mpegurl');
+	echo '#EXTM3U'."\n";
+	echo WindowsShareSlashTranslate($_REQUEST['m3ufilename'])."\n";
+	exit;
+
+} elseif (!isset($_REQUEST['m3u']) && !isset($_REQUEST['m3uartist']) && !isset($_REQUEST['m3utitle'])) {
+
+	echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"  "http://www.w3.org/TR/html4/loose.dtd">';
+	echo '<html><head><title>getID3() demo - /demo/mysql.php</title><style>BODY, TD, TH { font-family: sans-serif; font-size: 10pt; } A { text-decoration: none; } A:hover { text-decoration: underline; } A:visited { font-style: italic; }</style></head><body>';
+
+}
+
+
+function WindowsShareSlashTranslate($filename) {
+	if (substr($filename, 0, 2) == '//') {
+		return str_replace('/', '\\', $filename);
+	}
+	return $filename;
+}
+
+function safe_mysql_query($SQLquery) {
+	$result = @mysql_query($SQLquery);
+	if (mysql_error()) {
+		die('<FONT COLOR="red">'.mysql_error().'</FONT><hr><TT>'.$SQLquery.'</TT>');
+	}
+	return $result;
+}
+
+function mysql_table_exists($tablename) {
+	return (bool) mysql_query('DESCRIBE '.$tablename);
+}
+
+function AcceptableExtensions($fileformat, $audio_dataformat='', $video_dataformat='') {
+	static $AcceptableExtensionsAudio = array();
+	if (empty($AcceptableExtensionsAudio)) {
+		$AcceptableExtensionsAudio['mp3']['mp3']  = array('mp3');
+		$AcceptableExtensionsAudio['mp2']['mp2']  = array('mp2');
+		$AcceptableExtensionsAudio['mp1']['mp1']  = array('mp1');
+		$AcceptableExtensionsAudio['asf']['asf']  = array('asf');
+		$AcceptableExtensionsAudio['asf']['wma']  = array('wma');
+		$AcceptableExtensionsAudio['riff']['mp3'] = array('wav');
+		$AcceptableExtensionsAudio['riff']['wav'] = array('wav');
+	}
+	static $AcceptableExtensionsVideo = array();
+	if (empty($AcceptableExtensionsVideo)) {
+		$AcceptableExtensionsVideo['mp3']['mp3']  = array('mp3');
+		$AcceptableExtensionsVideo['mp2']['mp2']  = array('mp2');
+		$AcceptableExtensionsVideo['mp1']['mp1']  = array('mp1');
+		$AcceptableExtensionsVideo['asf']['asf']  = array('asf');
+		$AcceptableExtensionsVideo['asf']['wmv']  = array('wmv');
+		$AcceptableExtensionsVideo['gif']['gif']  = array('gif');
+		$AcceptableExtensionsVideo['jpg']['jpg']  = array('jpg');
+		$AcceptableExtensionsVideo['png']['png']  = array('png');
+		$AcceptableExtensionsVideo['bmp']['bmp']  = array('bmp');
+	}
+	if (!empty($video_dataformat)) {
+		return (isset($AcceptableExtensionsVideo[$fileformat][$video_dataformat]) ? $AcceptableExtensionsVideo[$fileformat][$video_dataformat] : array());
+	} else {
+		return (isset($AcceptableExtensionsAudio[$fileformat][$audio_dataformat]) ? $AcceptableExtensionsAudio[$fileformat][$audio_dataformat] : array());
+	}
+}
+
+
+if (!empty($_REQUEST['scan'])) {
+	if (mysql_table_exists(GETID3_DB_TABLE)) {
+		$SQLquery  = 'DROP TABLE `'.GETID3_DB_TABLE.'`';
+		safe_mysql_query($SQLquery);
+	}
+}
+if (!mysql_table_exists(GETID3_DB_TABLE)) {
+	$SQLquery  = 'CREATE TABLE `'.GETID3_DB_TABLE.'` (';
+	$SQLquery .= ' `ID` mediumint(8) unsigned NOT NULL auto_increment,';
+	$SQLquery .= ' `filename` text NOT NULL,';
+	$SQLquery .= ' `LastModified` int(11) NOT NULL default "0",';
+	$SQLquery .= ' `md5_file` varchar(32) NOT NULL default "",';
+	$SQLquery .= ' `md5_data` varchar(32) NOT NULL default "",';
+	$SQLquery .= ' `md5_data_source` varchar(32) NOT NULL default "",';
+	$SQLquery .= ' `filesize` int(10) unsigned NOT NULL default "0",';
+	$SQLquery .= ' `fileformat` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `audio_dataformat` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `video_dataformat` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `audio_bitrate` float NOT NULL default "0",';
+	$SQLquery .= ' `video_bitrate` float NOT NULL default "0",';
+	$SQLquery .= ' `playtime_seconds` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `tags` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `artist` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `title` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `remix` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `album` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `genre` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `comment` text NOT NULL,';
+	$SQLquery .= ' `track` varchar(7) NOT NULL default "",';
+	$SQLquery .= ' `comments_all` text NOT NULL,';
+	$SQLquery .= ' `comments_id3v2` text NOT NULL,';
+	$SQLquery .= ' `comments_ape` text NOT NULL,';
+	$SQLquery .= ' `comments_lyrics3` text NOT NULL,';
+	$SQLquery .= ' `comments_id3v1` text NOT NULL,';
+	$SQLquery .= ' `warning` text NOT NULL,';
+	$SQLquery .= ' `error` text NOT NULL,';
+	$SQLquery .= ' `track_volume` float NOT NULL default "0",';
+	$SQLquery .= ' `encoder_options` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' `vbr_method` varchar(255) NOT NULL default "",';
+	$SQLquery .= ' PRIMARY KEY (`ID`)';
+	$SQLquery .= ') TYPE=MyISAM;';
+
+	safe_mysql_query($SQLquery);
+}
+
+$ExistingTableFields = array();
+$result = mysql_query('DESCRIBE `'.GETID3_DB_TABLE.'`');
+while ($row = mysql_fetch_array($result)) {
+	$ExistingTableFields[$row['Field']] = $row;
+}
+if (!isset($ExistingTableFields['encoder_options'])) { // Added in 1.7.0b2
+	echo '<b>adding field `encoder_options`</b><br>';
+	mysql_query('ALTER TABLE `'.GETID3_DB_TABLE.'` ADD `encoder_options` VARCHAR(255) DEFAULT "" NOT NULL AFTER `error`');
+	mysql_query('OPTIMIZE TABLE `'.GETID3_DB_TABLE.'`');
+}
+if (isset($ExistingTableFields['track']) && ($ExistingTableFields['track']['Type'] != 'varchar(7)')) { // Changed in 1.7.0b2
+	echo '<b>changing field `track` to VARCHAR(7)</b><br>';
+	mysql_query('ALTER TABLE `'.GETID3_DB_TABLE.'` CHANGE `track` `track` VARCHAR(7) DEFAULT "" NOT NULL');
+	mysql_query('OPTIMIZE TABLE `'.GETID3_DB_TABLE.'`');
+}
+if (!isset($ExistingTableFields['track_volume'])) { // Added in 1.7.0b5
+	echo '<H1><FONT COLOR="red">WARNING! You should erase your database and rescan everything because the comment storing has been changed since the last version</FONT></H1><hr>';
+	echo '<b>adding field `track_volume`</b><br>';
+	mysql_query('ALTER TABLE `'.GETID3_DB_TABLE.'` ADD `track_volume` FLOAT NOT NULL AFTER `error`');
+	mysql_query('OPTIMIZE TABLE `'.GETID3_DB_TABLE.'`');
+}
+if (!isset($ExistingTableFields['remix'])) { // Added in 1.7.3b1
+	echo '<b>adding field `encoder_options`, `alternate_name`, `parody`</b><br>';
+	mysql_query('ALTER TABLE `'.GETID3_DB_TABLE.'` ADD `remix` VARCHAR(255) DEFAULT "" NOT NULL AFTER `title`');
+	mysql_query('ALTER TABLE `'.GETID3_DB_TABLE.'` ADD `alternate_name` VARCHAR(255) DEFAULT "" NOT NULL AFTER `track`');
+	mysql_query('ALTER TABLE `'.GETID3_DB_TABLE.'` ADD `parody` VARCHAR(255) DEFAULT "" NOT NULL AFTER `alternate_name`');
+	mysql_query('OPTIMIZE TABLE `'.GETID3_DB_TABLE.'`');
+}
+
+
+function SynchronizeAllTags($filename, $synchronizefrom='all', $synchronizeto='A12', &$errors) {
+	global $getID3;
+
+	set_time_limit(30);
+
+	$ThisFileInfo = $getID3->analyze($filename);
+	getid3_lib::CopyTagsToComments($ThisFileInfo);
+
+	if ($synchronizefrom == 'all') {
+		$SourceArray = @$ThisFileInfo['comments'];
+	} elseif (!empty($ThisFileInfo['tags'][$synchronizefrom])) {
+		$SourceArray = @$ThisFileInfo['tags'][$synchronizefrom];
+	} else {
+		die('ERROR: $ThisFileInfo[tags]['.$synchronizefrom.'] does not exist');
+	}
+
+	$SQLquery  = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`filename` = "'.mysql_escape_string($filename).'")';
+	safe_mysql_query($SQLquery);
+
+
+	$TagFormatsToWrite = array();
+	if ((strpos($synchronizeto, '2') !== false) && ($synchronizefrom != 'id3v2')) {
+		$TagFormatsToWrite[] = 'id3v2.3';
+	}
+	if ((strpos($synchronizeto, 'A') !== false) && ($synchronizefrom != 'ape')) {
+		$TagFormatsToWrite[] = 'ape';
+	}
+	if ((strpos($synchronizeto, 'L') !== false) && ($synchronizefrom != 'lyrics3')) {
+		$TagFormatsToWrite[] = 'lyrics3';
+	}
+	if ((strpos($synchronizeto, '1') !== false) && ($synchronizefrom != 'id3v1')) {
+		$TagFormatsToWrite[] = 'id3v1';
+	}
+
+	getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
+	$tagwriter = new getid3_writetags;
+	$tagwriter->filename       = $filename;
+	$tagwriter->tagformats     = $TagFormatsToWrite;
+	$tagwriter->overwrite_tags = true;
+	$tagwriter->tag_encoding   = $getID3->encoding;
+	$tagwriter->tag_data       = $SourceArray;
+
+	if ($tagwriter->WriteTags()) {
+		$errors = $tagwriter->errors;
+		return true;
+	}
+	$errors = $tagwriter->errors;
+	return false;
+}
+
+$IgnoreNoTagFormats = array('', 'png', 'jpg', 'gif', 'bmp', 'swf', 'pdf', 'zip', 'rar', 'mid', 'mod', 'xm', 'it', 's3m');
+
+if (!empty($_REQUEST['scan']) || !empty($_REQUEST['newscan']) || !empty($_REQUEST['rescanerrors'])) {
+
+	$SQLquery  = 'DELETE from `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`fileformat` = "")';
+	safe_mysql_query($SQLquery);
+
+	$FilesInDir = array();
+
+	if (!empty($_REQUEST['rescanerrors'])) {
+
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF']).'">abort</a><hr>';
+
+		echo 'Re-scanning all media files already in database that had errors and/or warnings in last scan<hr>';
+
+		$SQLquery  = 'SELECT `filename`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`error` <> "")';
+		$SQLquery .= ' OR (`warning` <> "")';
+		$SQLquery .= ' ORDER BY `filename` ASC';
+		$result = safe_mysql_query($SQLquery);
+		while ($row = mysql_fetch_array($result)) {
+
+			if (!file_exists($row['filename'])) {
+				echo '<b>File missing: '.$row['filename'].'</b><br>';
+				$SQLquery = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+				$SQLquery .= ' WHERE (`filename` = "'.mysql_escape_string($row['filename']).'")';
+				safe_mysql_query($SQLquery);
+			} else {
+				$FilesInDir[] = $row['filename'];
+			}
+
+		}
+
+	} elseif (!empty($_REQUEST['scan']) || !empty($_REQUEST['newscan'])) {
+
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF']).'">abort</a><hr>';
+
+		echo 'Scanning all media files in <b>'.str_replace('\\', '/', realpath(!empty($_REQUEST['scan']) ? $_REQUEST['scan'] : $_REQUEST['newscan'])).'</b> (and subdirectories)<hr>';
+
+		$SQLquery  = 'SELECT COUNT(*) AS `num`, `filename`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' GROUP BY `filename`';
+		$SQLquery .= ' ORDER BY `num` DESC';
+		$result = safe_mysql_query($SQLquery);
+		$DupesDeleted = 0;
+		while ($row = mysql_fetch_array($result)) {
+			set_time_limit(30);
+			if ($row['num'] <= 1) {
+				break;
+			}
+			$SQLquery  = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE `filename` LIKE "'.mysql_escape_string($row['filename']).'"';
+			safe_mysql_query($SQLquery);
+			$DupesDeleted++;
+		}
+		if ($DupesDeleted > 0) {
+			echo 'Deleted <b>'.number_format($DupesDeleted).'</b> duplicate filenames<hr>';
+		}
+
+		if (!empty($_REQUEST['newscan'])) {
+			$AlreadyInDatabase = array();
+			set_time_limit(60);
+			$SQLquery  = 'SELECT `filename`';
+			$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' ORDER BY `filename` ASC';
+			$result = safe_mysql_query($SQLquery);
+			while ($row = mysql_fetch_array($result)) {
+				//$AlreadyInDatabase[] = strtolower($row['filename']);
+				$AlreadyInDatabase[] = $row['filename'];
+			}
+		}
+
+		$DirectoriesToScan  = array(@$_REQUEST['scan'] ? $_REQUEST['scan'] : $_REQUEST['newscan']);
+		$DirectoriesScanned = array();
+		while (count($DirectoriesToScan) > 0) {
+			foreach ($DirectoriesToScan as $DirectoryKey => $startingdir) {
+				if ($dir = opendir($startingdir)) {
+					set_time_limit(30);
+					echo '<b>'.str_replace('\\', '/', $startingdir).'</b><br>';
+					flush();
+					while (($file = readdir($dir)) !== false) {
+						if (($file != '.') && ($file != '..')) {
+							$RealPathName = realpath($startingdir.'/'.$file);
+							if (is_dir($RealPathName)) {
+								if (!in_array($RealPathName, $DirectoriesScanned) && !in_array($RealPathName, $DirectoriesToScan)) {
+									$DirectoriesToScan[] = $RealPathName;
+								}
+							} else if (is_file($RealPathName)) {
+								if (!empty($_REQUEST['newscan'])) {
+									if (!in_array(str_replace('\\', '/', $RealPathName), $AlreadyInDatabase)) {
+										$FilesInDir[] = $RealPathName;
+									}
+								} elseif (!empty($_REQUEST['scan'])) {
+									$FilesInDir[] = $RealPathName;
+								}
+							}
+						}
+					}
+					closedir($dir);
+				} else {
+					echo '<FONT COLOR="RED">Failed to open directory "<b>'.$startingdir.'</b>"</FONT><br><br>';
+				}
+				$DirectoriesScanned[] = $startingdir;
+				unset($DirectoriesToScan[$DirectoryKey]);
+			}
+		}
+		echo '<i>List of files to scan complete (added '.number_format(count($FilesInDir)).' files to scan)</i><hr>';
+		flush();
+	}
+
+	$FilesInDir = array_unique($FilesInDir);
+	sort($FilesInDir);
+
+	$starttime = time();
+	$rowcounter = 0;
+	$totaltoprocess = count($FilesInDir);
+
+	foreach ($FilesInDir as $filename) {
+		set_time_limit(300);
+
+		echo '<br>'.date('H:i:s').' ['.number_format(++$rowcounter).' / '.number_format($totaltoprocess).'] '.str_replace('\\', '/', $filename);
+
+		$ThisFileInfo = $getID3->analyze($filename);
+		getid3_lib::CopyTagsToComments($ThisFileInfo);
+
+		if (file_exists($filename)) {
+			$ThisFileInfo['file_modified_time'] = filemtime($filename);
+			$ThisFileInfo['md5_file']           = ($getid3_demo_mysql_md5_file ? md5_file($filename) : '');
+		}
+
+		if (empty($ThisFileInfo['fileformat'])) {
+
+			echo ' (<span style="color: #990099;">unknown file type</span>)';
+
+		} else {
+
+			if (!empty($ThisFileInfo['error'])) {
+				echo ' (<span style="color: #FF0000;">errors</span>)';
+			} elseif (!empty($ThisFileInfo['warning'])) {
+				echo ' (<span style="color: #FF9999;">warnings</span>)';
+			} else {
+				echo ' (<span style="color: #009900;">OK</span>)';
+			}
+
+			$this_track_track = '';
+			if (!empty($ThisFileInfo['comments']['track'])) {
+				foreach ($ThisFileInfo['comments']['track'] as $key => $value) {
+					if (strlen($value) > strlen($this_track_track)) {
+						$this_track_track = str_pad($value, 2, '0', STR_PAD_LEFT);
+					}
+				}
+				if (ereg('^([0-9]+)/([0-9]+)$', $this_track_track, $matches)) {
+					// change "1/5"->"01/05", "3/12"->"03/12", etc
+					$this_track_track = str_pad($matches[1], 2, '0', STR_PAD_LEFT).'/'.str_pad($matches[2], 2, '0', STR_PAD_LEFT);
+				}
+			}
+
+			$this_track_remix = '';
+			$this_track_title = '';
+			if (!empty($ThisFileInfo['comments']['title'])) {
+				foreach ($ThisFileInfo['comments']['title'] as $possible_title) {
+					if (strlen($possible_title) > strlen($this_track_title)) {
+						$this_track_title = $possible_title;
+					}
+				}
+			}
+
+			$ParenthesesPairs = array('()', '[]', '{}');
+			foreach ($ParenthesesPairs as $pair) {
+				if (preg_match_all('/(.*) '.preg_quote($pair{0}).'(([^'.preg_quote($pair).']*[\- '.preg_quote($pair{0}).'])?(cut|dub|edit|version|live|reprise|[a-z]*mix))'.preg_quote($pair{1}).'/iU', $this_track_title, $matches)) {
+					$this_track_title = $matches[1][0];
+					$this_track_remix = implode("\t", $matches[2]);
+				}
+			}
+
+
+
+			if (!empty($_REQUEST['rescanerrors'])) {
+
+				$SQLquery  = 'UPDATE `'.GETID3_DB_TABLE.'` SET ';
+				$SQLquery .= '`LastModified` = "'.mysql_escape_string(@$ThisFileInfo['file_modified_time']).'", ';
+				$SQLquery .= '`md5_file` = "'.mysql_escape_string(@$ThisFileInfo['md5_file']).'", ';
+				$SQLquery .= '`md5_data` = "'.mysql_escape_string(@$ThisFileInfo['md5_data']).'", ';
+				$SQLquery .= '`md5_data_source` = "'.mysql_escape_string(@$ThisFileInfo['md5_data_source']).'", ';
+				$SQLquery .= '`filesize` = "'.mysql_escape_string(@$ThisFileInfo['filesize']).'", ';
+				$SQLquery .= '`fileformat` = "'.mysql_escape_string(@$ThisFileInfo['fileformat']).'", ';
+				$SQLquery .= '`audio_dataformat` = "'.mysql_escape_string(@$ThisFileInfo['audio']['dataformat']).'", ';
+				$SQLquery .= '`video_dataformat` = "'.mysql_escape_string(@$ThisFileInfo['video']['dataformat']).'", ';
+				$SQLquery .= '`audio_bitrate` = "'.mysql_escape_string(floatval(@$ThisFileInfo['audio']['bitrate'])).'", ';
+				$SQLquery .= '`video_bitrate` = "'.mysql_escape_string(floatval(@$ThisFileInfo['video']['bitrate'])).'", ';
+				$SQLquery .= '`playtime_seconds` = "'.mysql_escape_string(floatval(@$ThisFileInfo['playtime_seconds'])).'", ';
+				$SQLquery .= '`tags` = "'.mysql_escape_string(@implode("\t", @array_keys(@$ThisFileInfo['tags']))).'", ';
+				$SQLquery .= '`artist` = "'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['artist'])).'", ';
+
+				$SQLquery .= '`title` = "'.mysql_escape_string($this_track_title).'", ';
+				$SQLquery .= '`remix` = "'.mysql_escape_string($this_track_remix).'", ';
+
+				$SQLquery .= '`album` = "'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['album'])).'", ';
+				$SQLquery .= '`genre` = "'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['genre'])).'", ';
+				$SQLquery .= '`comment` = "'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['comment'])).'", ';
+
+				$SQLquery .= '`track` = "'.mysql_escape_string($this_track_track).'", ';
+
+				$SQLquery .= '`comments_all` = "'.mysql_escape_string(@serialize(@$ThisFileInfo['comments'])).'", ';
+				$SQLquery .= '`comments_id3v2` = "'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['id3v2'])).'", ';
+				$SQLquery .= '`comments_ape` = "'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['ape'])).'", ';
+				$SQLquery .= '`comments_lyrics3` = "'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['lyrics3'])).'", ';
+				$SQLquery .= '`comments_id3v1` = "'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['id3v1'])).'", ';
+				$SQLquery .= '`warning` = "'.mysql_escape_string(@implode("\t", @$ThisFileInfo['warning'])).'", ';
+				$SQLquery .= '`error` = "'.mysql_escape_string(@implode("\t", @$ThisFileInfo['error'])).'", ';
+				$SQLquery .= '`encoder_options` = "'.mysql_escape_string(trim(@$ThisFileInfo['audio']['encoder'].' '.@$ThisFileInfo['audio']['encoder_options'])).'", ';
+				$SQLquery .= '`vbr_method` = "'.mysql_escape_string(@$ThisFileInfo['mpeg']['audio']['VBR_method']).'", ';
+				$SQLquery .= '`track_volume` = "'.mysql_escape_string(floatval(@$ThisFileInfo['replay_gain']['track']['volume'])).'" ';
+				$SQLquery .= 'WHERE (`filename` = "'.mysql_escape_string(@$ThisFileInfo['filenamepath']).'")';
+
+			} elseif (!empty($_REQUEST['scan']) || !empty($_REQUEST['newscan'])) {
+
+				$SQLquery  = 'INSERT INTO `'.GETID3_DB_TABLE.'` (`filename`, `LastModified`, `md5_file`, `md5_data`, `md5_data_source`, `filesize`, `fileformat`, `audio_dataformat`, `video_dataformat`, `audio_bitrate`, `video_bitrate`, `playtime_seconds`, `tags`, `artist`, `title`, `remix`, `album`, `genre`, `comment`, `track`, `comments_all`, `comments_id3v2`, `comments_ape`, `comments_lyrics3`, `comments_id3v1`, `warning`, `error`, `encoder_options`, `vbr_method`, `track_volume`) VALUES (';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['filenamepath']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['file_modified_time']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['md5_file']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['md5_data']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['md5_data_source']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['filesize']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['fileformat']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['audio']['dataformat']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@$ThisFileInfo['video']['dataformat']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(floatval(@$ThisFileInfo['audio']['bitrate'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(floatval(@$ThisFileInfo['video']['bitrate'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(floatval(@$ThisFileInfo['playtime_seconds'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @array_keys(@$ThisFileInfo['tags']))).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['artist'])).'", ';
+
+				$SQLquery .= '"'.mysql_escape_string($this_track_title).'", ';
+				$SQLquery .= '"'.mysql_escape_string($this_track_remix).'", ';
+
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['album'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['genre'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @$ThisFileInfo['comments']['comment'])).'", ';
+
+				$SQLquery .= '"'.mysql_escape_string($this_track_track).'", ';
+
+				$SQLquery .= '"'.mysql_escape_string(@serialize(@$ThisFileInfo['comments'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['id3v2'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['ape'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['lyrics3'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@serialize(@$ThisFileInfo['tags']['id3v1'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @$ThisFileInfo['warning'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(@implode("\t", @$ThisFileInfo['error'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(trim(@$ThisFileInfo['audio']['encoder'].' '.@$ThisFileInfo['audio']['encoder_options'])).'", ';
+				$SQLquery .= '"'.mysql_escape_string(!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' : @$ThisFileInfo['mpeg']['audio']['VBR_method']).'", ';
+				$SQLquery .= '"'.mysql_escape_string(floatval(@$ThisFileInfo['replay_gain']['track']['volume'])).'")';
+
+			}
+			flush();
+			safe_mysql_query($SQLquery);
+		}
+
+	}
+
+	$SQLquery = 'OPTIMIZE TABLE `'.GETID3_DB_TABLE.'`';
+	safe_mysql_query($SQLquery);
+
+	echo '<hr>Done scanning!<hr>';
+
+} elseif (!empty($_REQUEST['missingtrackvolume'])) {
+
+	$MissingTrackVolumeFilesScanned  = 0;
+	$MissingTrackVolumeFilesAdjusted = 0;
+	$MissingTrackVolumeFilesDeleted  = 0;
+	$SQLquery  = 'SELECT `filename`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`track_volume` = "0")';
+	$SQLquery .= ' AND (`audio_bitrate` > "0")';
+	$result = safe_mysql_query($SQLquery);
+	echo 'Scanning <span ID="missingtrackvolumeNowScanning">0</span> / '.number_format(mysql_num_rows($result)).' files for track volume information:<hr>';
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(30);
+		echo '<script type="text/javascript">if (document.getElementById("missingtrackvolumeNowScanning")) document.getElementById("missingtrackvolumeNowScanning").innerHTML = "'.number_format($MissingTrackVolumeFilesScanned++).'";</script>. ';
+		flush();
+		if (file_exists($row['filename'])) {
+
+			$ThisFileInfo = $getID3->analyze($row['filename']);
+			if (!empty($ThisFileInfo['replay_gain']['track']['volume'])) {
+				$MissingTrackVolumeFilesAdjusted++;
+				$SQLquery  = 'UPDATE `'.GETID3_DB_TABLE.'`';
+				$SQLquery .= ' SET `track_volume` = "'.$ThisFileInfo['replay_gain']['track']['volume'].'"';
+				$SQLquery .= ' WHERE (`filename` = "'.mysql_escape_string($row['filename']).'")';
+				safe_mysql_query($SQLquery);
+			}
+
+		} else {
+
+			$MissingTrackVolumeFilesDeleted++;
+			$SQLquery  = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE (`filename` = "'.mysql_escape_string($row['filename']).'")';
+			safe_mysql_query($SQLquery);
+
+		}
+	}
+	echo '<hr>Scanned '.number_format($MissingTrackVolumeFilesScanned).' files with no track volume information.<br>';
+	echo 'Found track volume information for '.number_format($MissingTrackVolumeFilesAdjusted).' of them (could not find info for '.number_format($MissingTrackVolumeFilesScanned - $MissingTrackVolumeFilesAdjusted).' files; deleted '.number_format($MissingTrackVolumeFilesDeleted).' records of missing files)<hr>';
+
+} elseif (!empty($_REQUEST['deadfilescheck'])) {
+
+	$SQLquery  = 'SELECT COUNT(*) AS `num`, `filename`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' GROUP BY `filename`';
+	$SQLquery .= ' ORDER BY `num` DESC';
+	$result = safe_mysql_query($SQLquery);
+	$DupesDeleted = 0;
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(30);
+		if ($row['num'] <= 1) {
+			break;
+		}
+		echo '<br>'.FixTextFields($row['filename']).' (<font color="#FF9999">duplicate</font>)';
+		$SQLquery  = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE `filename` LIKE "'.mysql_escape_string($row['filename']).'"';
+		safe_mysql_query($SQLquery);
+		$DupesDeleted++;
+	}
+	if ($DupesDeleted > 0) {
+		echo '<hr>Deleted <b>'.number_format($DupesDeleted).'</b> duplicate filenames<hr>';
+	}
+
+	$SQLquery  = 'SELECT `filename`, `filesize`, `LastModified`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	$totalchecked = 0;
+	$totalremoved = 0;
+	$previousdir = '';
+	while ($row = mysql_fetch_array($result)) {
+		$totalchecked++;
+		set_time_limit(30);
+		$reason = '';
+		if (!file_exists($row['filename'])) {
+			$reason = 'deleted';
+		} elseif (filesize($row['filename']) != $row['filesize']) {
+			$reason = 'filesize changed';
+		} elseif (filemtime($row['filename']) != $row['LastModified']) {
+			if (abs(filemtime($row['filename']) - $row['LastModified']) != 3600) {
+				// off by exactly one hour == daylight savings time
+				$reason = 'last-modified time changed';
+			}
+		}
+
+		$thisdir = dirname($row['filename']);
+		if ($reason) {
+
+			$totalremoved++;
+			echo '<br>'.FixTextFields($row['filename']).' (<font color="#FF9999">'.$reason.'</font>)';
+			flush();
+			$SQLquery  = 'DELETE FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE (`filename` = "'.mysql_escape_string($row['filename']).'")';
+			safe_mysql_query($SQLquery);
+
+		} elseif ($thisdir != $previousdir) {
+
+			echo '. ';
+			flush();
+
+		}
+		$previousdir = $thisdir;
+	}
+
+	echo '<hr><b>'.number_format($totalremoved).' of '.number_format($totalchecked).' files in database no longer exist, or have been altered since last scan. Removed from database.</b><hr>';
+
+} elseif (!empty($_REQUEST['encodedbydistribution'])) {
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+
+		$SQLquery  = 'SELECT `filename`, `comments_id3v2`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`encoder_options` = "'.mysql_escape_string($_REQUEST['encodedbydistribution']).'")';
+		$result = mysql_query($SQLquery);
+		$NonBlankEncodedBy = '';
+		$BlankEncodedBy = '';
+		while ($row = mysql_fetch_array($result)) {
+			set_time_limit(30);
+			$CommentArray = unserialize($row['comments_id3v2']);
+			if (isset($CommentArray['encoded_by'][0])) {
+				$NonBlankEncodedBy .= WindowsShareSlashTranslate($row['filename'])."\n";
+			} else {
+				$BlankEncodedBy    .= WindowsShareSlashTranslate($row['filename'])."\n";
+			}
+		}
+		echo $NonBlankEncodedBy;
+		echo $BlankEncodedBy;
+		exit;
+
+	} elseif (!empty($_REQUEST['showfiles'])) {
+
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?encodedbydistribution='.urlencode('%')).'">show all</a><br>';
+		echo '<table border="1">';
+
+		$SQLquery  = 'SELECT `filename`, `comments_id3v2`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$result = mysql_query($SQLquery);
+		while ($row = mysql_fetch_array($result)) {
+			set_time_limit(30);
+			$CommentArray = unserialize($row['comments_id3v2']);
+			if (($_REQUEST['encodedbydistribution'] == '%') || (!empty($CommentArray['encoded_by'][0]) && ($_REQUEST['encodedbydistribution'] == $CommentArray['encoded_by'][0]))) {
+				echo '<tr><td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+				echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td></tr>';
+			}
+		}
+		echo '</table>';
+
+	} else {
+
+		$SQLquery  = 'SELECT `encoder_options`, `comments_id3v2`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' ORDER BY (`encoder_options` LIKE "LAME%") DESC, (`encoder_options` LIKE "CBR%") DESC';
+		$result = mysql_query($SQLquery);
+		$EncodedBy = array();
+		while ($row = mysql_fetch_array($result)) {
+			set_time_limit(30);
+			$CommentArray = unserialize($row['comments_id3v2']);
+			if (isset($EncodedBy[$row['encoder_options']][@$CommentArray['encoded_by'][0]])) {
+				$EncodedBy[$row['encoder_options']][@$CommentArray['encoded_by'][0]]++;
+			} else {
+				$EncodedBy[$row['encoder_options']][@$CommentArray['encoded_by'][0]] = 1;
+			}
+		}
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?encodedbydistribution='.urlencode('%').'&m3u=1').'">.m3u version</a><br>';
+		echo '<table border="1"><tr><th>m3u</th><th>Encoder Options</th><th>Encoded By (ID3v2)</th></tr>';
+		foreach ($EncodedBy as $key => $value) {
+			echo '<tr><TD VALIGN="TOP"><a href="'.htmlentities($_SERVER['PHP_SELF'].'?encodedbydistribution='.urlencode($key).'&showfiles=1&m3u=1').'">m3u</a></td>';
+			echo '<TD VALIGN="TOP"><b>'.$key.'</b></td>';
+			echo '<td><table border="0" WIDTH="100%">';
+			arsort($value);
+			foreach ($value as $string => $count) {
+				echo '<tr><TD ALIGN="RIGHT" WIDTH="50"><i>'.number_format($count).'</i></td><td>&nbsp;</td>';
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?encodedbydistribution='.urlencode($string).'&showfiles=1').'">'.$string.'</a></td></tr>';
+			}
+			echo '</table></td></tr>';
+		}
+		echo '</table>';
+
+	}
+
+} elseif (!empty($_REQUEST['audiobitrates'])) {
+
+	getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
+	$BitrateDistribution = array();
+	$SQLquery  = 'SELECT ROUND(audio_bitrate / 1000) AS `RoundBitrate`, COUNT(*) AS `num`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`audio_bitrate` > 0)';
+	$SQLquery .= ' GROUP BY `RoundBitrate`';
+	$result = safe_mysql_query($SQLquery);
+	while ($row = mysql_fetch_array($result)) {
+		@$BitrateDistribution[getid3_mp3::ClosestStandardMP3Bitrate($row['RoundBitrate'] * 1000)] += $row['num'];  // safe_inc
+	}
+
+	echo '<table border="1" cellspacing="0" cellpadding="3">';
+	echo '<tr><th>Bitrate</th><th>Count</th></tr>';
+	foreach ($BitrateDistribution as $Bitrate => $Count) {
+		echo '<tr>';
+		echo '<TD ALIGN="RIGHT">'.round($Bitrate / 1000).' kbps</td>';
+		echo '<TD ALIGN="RIGHT">'.number_format($Count).'</td>';
+		echo '</tr>';
+	}
+	echo '</table>';
+
+
+} elseif (!empty($_REQUEST['emptygenres'])) {
+
+	$SQLquery  = 'SELECT `fileformat`, `filename`, `genre`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`genre` = "")';
+	$SQLquery .= ' OR (`genre` = "Unknown")';
+	$SQLquery .= ' OR (`genre` = "Other")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			if (!in_array($row['fileformat'], $IgnoreNoTagFormats)) {
+				echo WindowsShareSlashTranslate($row['filename'])."\n";
+			}
+		}
+		exit;
+
+	} else {
+
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?emptygenres='.urlencode($_REQUEST['emptygenres']).'&m3u=1').'">.m3u version</a><br>';
+		$EmptyGenreCounter = 0;
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>m3u</th><th>filename</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			if (!in_array($row['fileformat'], $IgnoreNoTagFormats)) {
+				$EmptyGenreCounter++;
+				echo '<tr>';
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+				echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+				echo '</tr>';
+			}
+		}
+		echo '</table>';
+		echo '<b>'.number_format($EmptyGenreCounter).'</b> files with empty genres';
+
+	}
+
+} elseif (!empty($_REQUEST['nonemptycomments'])) {
+
+	$SQLquery  = 'SELECT `filename`, `comment`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`comment` <> "")';
+	$SQLquery .= ' ORDER BY `comment` ASC';
+	$result = safe_mysql_query($SQLquery);
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	} else {
+
+		$NonEmptyCommentsCounter = 0;
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?nonemptycomments='.urlencode($_REQUEST['nonemptycomments']).'&m3u=1').'">.m3u version</a><br>';
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>m3u</th><th>filename</th><th>comments</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			$NonEmptyCommentsCounter++;
+			echo '<tr>';
+			echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+			echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+			if (strlen(trim($row['comment'])) > 0) {
+				echo '<td>'.FixTextFields($row['comment']).'</td>';
+			} else {
+				echo '<td><i>space</i></td>';
+			}
+			echo '</tr>';
+		}
+		echo '</table>';
+		echo '<b>'.number_format($NonEmptyCommentsCounter).'</b> files with non-empty comments';
+
+	}
+
+} elseif (!empty($_REQUEST['trackzero'])) {
+
+	$SQLquery  = 'SELECT `filename`, `track`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`track` <> "")';
+	$SQLquery .= ' AND ((`track` < "1")';
+	$SQLquery .= ' OR (`track` > "99"))';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			if ((strlen($row['track']) > 0) && ($row['track'] < 1) || ($row['track'] > 99)) {
+				echo WindowsShareSlashTranslate($row['filename'])."\n";
+			}
+		}
+		exit;
+
+	} else {
+
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?trackzero='.urlencode($_REQUEST['trackzero']).'&m3u=1').'">.m3u version</a><br>';
+		$TrackZeroCounter = 0;
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>m3u</th><th>filename</th><th>track</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			if ((strlen($row['track']) > 0) && ($row['track'] < 1) || ($row['track'] > 99)) {
+				$TrackZeroCounter++;
+				echo '<tr>';
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+				echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+				echo '<td>'.FixTextFields($row['track']).'</td>';
+				echo '</tr>';
+			}
+		}
+		echo '</table>';
+		echo '<b>'.number_format($TrackZeroCounter).'</b> files with track "zero"';
+
+	}
+
+
+} elseif (!empty($_REQUEST['titlefeat'])) {
+
+	$SQLquery  = 'SELECT `filename`, `title`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`title` LIKE "%feat.%")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	} else {
+
+		echo '<b>'.number_format(mysql_num_rows($result)).'</b> files with "feat." in the title (instead of the artist)<br><br>';
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?titlefeat='.urlencode($_REQUEST['titlefeat']).'&m3u=1').'">.m3u version</a><br>';
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>m3u</th><th>filename</th><th>title</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr>';
+			echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+			echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+			echo '<td>'.eregi_replace('(feat\. .*)', '<b>\\1</b>', FixTextFields($row['title'])).'</td>';
+			echo '</tr>';
+		}
+		echo '</table>';
+
+	}
+
+
+} elseif (!empty($_REQUEST['tracknoalbum'])) {
+
+	$SQLquery  = 'SELECT `filename`, `track`, `album`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`track` <> "")';
+	$SQLquery .= ' AND (`album` = "")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	} else {
+
+		echo '<b>'.number_format(mysql_num_rows($result)).'</b> files with a track number, but no album<br><br>';
+		echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?tracknoalbum='.urlencode($_REQUEST['tracknoalbum']).'&m3u=1').'">.m3u version</a><br>';
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>m3u</th><th>filename</th><th>track</th><th>album</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr>';
+			echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+			echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+			echo '<td>'.FixTextFields($row['track']).'</td>';
+			echo '<td>'.FixTextFields($row['album']).'</td>';
+			echo '</tr>';
+		}
+		echo '</table>';
+
+	}
+
+
+} elseif (!empty($_REQUEST['synchronizetagsfrom']) && !empty($_REQUEST['filename'])) {
+
+	echo 'Applying new tags from <b>'.$_REQUEST['synchronizetagsfrom'].'</b> in <b>'.FixTextFields($_REQUEST['filename']).'</b><ul>';
+	$errors = array();
+	if (SynchronizeAllTags($_REQUEST['filename'], $_REQUEST['synchronizetagsfrom'], 'A12', $errors)) {
+		echo '<li>Sucessfully wrote tags</li>';
+	} else {
+		echo '<li>Tag writing had errors: <ul><li>'.implode('</li><li>', $errors).'</li></ul></li>';
+	}
+	echo '</ul>';
+
+
+} elseif (!empty($_REQUEST['unsynchronizedtags'])) {
+
+	$NotOKfiles        = 0;
+	$Autofixedfiles    = 0;
+	$FieldsToCompare   = array('title', 'artist', 'album', 'year', 'genre', 'comment', 'track');
+	$TagsToCompare     = array('id3v2'=>false, 'ape'=>false, 'lyrics3'=>false, 'id3v1'=>false);
+	$ID3v1FieldLengths = array('title'=>30, 'artist'=>30, 'album'=>30, 'year'=>4, 'genre'=>99, 'comment'=>28);
+	if (strpos($_REQUEST['unsynchronizedtags'], '2') !== false) {
+		$TagsToCompare['id3v2'] = true;
+	}
+	if (strpos($_REQUEST['unsynchronizedtags'], 'A') !== false) {
+		$TagsToCompare['ape'] = true;
+	}
+	if (strpos($_REQUEST['unsynchronizedtags'], 'L') !== false) {
+		$TagsToCompare['lyrics3'] = true;
+	}
+	if (strpos($_REQUEST['unsynchronizedtags'], '1') !== false) {
+		$TagsToCompare['id3v1'] = true;
+	}
+
+	echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?unsynchronizedtags='.urlencode($_REQUEST['unsynchronizedtags']).'&autofix=1').'">Auto-fix empty tags</a><br><br>';
+	echo '<div id="Autofixing"></div>';
+	echo '<table border="1" cellspacing="0" cellpadding="3">';
+	echo '<tr>';
+	echo '<th>View</th>';
+	echo '<th>Filename</th>';
+	echo '<th>Combined</th>';
+	if ($TagsToCompare['id3v2']) {
+		echo '<th><a href="'.htmlentities($_SERVER['PHP_SELF'].'?unsynchronizedtags='.urlencode($_REQUEST['unsynchronizedtags']).'&autofix=1&autofixforcesource=id3v2&autofixforcedest=A1').'" title="Auto-fix all tags to match ID3v2 contents" onClick="return confirm(\'Are you SURE you want to synchronize all tags to match ID3v2?\');">ID3v2</a></th>';
+	}
+	if ($TagsToCompare['ape']) {
+		echo '<th><a href="'.htmlentities($_SERVER['PHP_SELF'].'?unsynchronizedtags='.urlencode($_REQUEST['unsynchronizedtags']).'&autofix=1&autofixforcesource=ape&autofixforcedest=21').'" title="Auto-fix all tags to match APE contents" onClick="return confirm(\'Are you SURE you want to synchronize all tags to match APE?\');">APE</a></th>';
+	}
+	if ($TagsToCompare['lyrics3']) {
+		echo '<th>Lyrics3</th>';
+	}
+	if ($TagsToCompare['id3v1']) {
+		echo '<th><a href="'.htmlentities($_SERVER['PHP_SELF'].'?unsynchronizedtags='.urlencode($_REQUEST['unsynchronizedtags']).'&autofix=1&autofixforcesource=ape&autofixforcedest=2A').'" title="Auto-fix all tags to match ID3v1 contents" onClick="return confirm(\'Are you SURE you want to synchronize all tags to match ID3v1?\');">ID3v1</a></th>';
+	}
+	echo '</tr>';
+
+	$SQLquery  = 'SELECT `filename`, `comments_all`, `comments_id3v2`, `comments_ape`, `comments_lyrics3`, `comments_id3v1`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`fileformat` = "mp3")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	$lastdir = '';
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(30);
+		if ($lastdir != dirname($row['filename'])) {
+			echo '<script type="text/javascript">if (document.getElementById("Autofixing")) document.getElementById("Autofixing").innerHTML = "'.htmlentities($lastdir, ENT_QUOTES).'";</script>';
+			flush();
+		}
+
+		$FileOK      = true;
+		$Mismatched  = array('id3v2'=>false, 'ape'=>false, 'lyrics3'=>false, 'id3v1'=>false);
+		$SemiMatched = array('id3v2'=>false, 'ape'=>false, 'lyrics3'=>false, 'id3v1'=>false);
+		$EmptyTags   = array('id3v2'=>true,  'ape'=>true,  'lyrics3'=>true,  'id3v1'=>true);
+
+		$Comments['all']     = @unserialize($row['comments_all']);
+		$Comments['id3v2']   = @unserialize($row['comments_id3v2']);
+		$Comments['ape']     = @unserialize($row['comments_ape']);
+		$Comments['lyrics3'] = @unserialize($row['comments_lyrics3']);
+		$Comments['id3v1']   = @unserialize($row['comments_id3v1']);
+
+		if (isset($Comments['ape']['tracknumber'])) {
+			$Comments['ape']['track'] = $Comments['ape']['tracknumber'];
+			unset($Comments['ape']['tracknumber']);
+		}
+		if (isset($Comments['ape']['track_number'])) {
+			$Comments['ape']['track'] = $Comments['ape']['track_number'];
+			unset($Comments['ape']['track_number']);
+		}
+		if (isset($Comments['id3v2']['track_number'])) {
+			$Comments['id3v2']['track'] = $Comments['id3v2']['track_number'];
+			unset($Comments['id3v2']['track_number']);
+		}
+		if (!empty($Comments['all']['track'])) {
+			$besttrack = '';
+			foreach ($Comments['all']['track'] as $key => $value) {
+				if (strlen($value) > strlen($besttrack)) {
+					$besttrack = $value;
+				}
+			}
+			$Comments['all']['track'] = array(0=>$besttrack);
+		}
+
+		$ThisLine  = '<tr>';
+		$ThisLine .= '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">view</a></td>';
+		$ThisLine .= '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+		$tagvalues = '';
+		foreach ($FieldsToCompare as $fieldname) {
+			$tagvalues .= $fieldname.' = '.@implode(" \n", @$Comments['all'][$fieldname])." \n";
+		}
+		$ThisLine .= '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?synchronizetagsfrom=all&filename='.urlencode($row['filename'])).'" title="'.htmlentities(rtrim($tagvalues, "\n"), ENT_QUOTES).'" target="retagwindow">all</a></td>';
+		foreach ($TagsToCompare as $tagtype => $CompareThisTagType) {
+			if ($CompareThisTagType) {
+				$tagvalues = '';
+				foreach ($FieldsToCompare as $fieldname) {
+
+					if ($tagtype == 'id3v1') {
+
+						getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
+						if (($fieldname == 'genre') && !getid3_id3v1::LookupGenreID(@$Comments['all'][$fieldname][0])) {
+
+							// non-standard genres can never match, so just ignore
+							$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+
+						} elseif ($fieldname == 'comment') {
+
+							if (rtrim(substr(@$Comments[$tagtype][$fieldname][0], 0, 28)) != rtrim(substr(@$Comments['all'][$fieldname][0], 0, 28))) {
+//echo __LINE__.'<br>';
+//echo '<pre>';
+//var_dump($tagtype);
+//var_dump($fieldname);
+//echo '<pre>';
+//exit;
+								$tagvalues .= $fieldname.' = [['.@$Comments[$tagtype][$fieldname][0].']]'."\n";
+								if (trim(strtolower(RemoveAccents(substr(@$Comments[$tagtype][$fieldname][0], 0, 28)))) == trim(strtolower(RemoveAccents(substr(@$Comments['all'][$fieldname][0], 0, 28))))) {
+									$SemiMatched[$tagtype] = true;
+								} else {
+									$Mismatched[$tagtype]  = true;
+								}
+								$FileOK = false;
+							} else {
+								$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+							}
+
+						} elseif ($fieldname == 'track') {
+
+							// intval('01/20') == intval('1')
+							if (intval(@$Comments[$tagtype][$fieldname][0]) != intval(@$Comments['all'][$fieldname][0])) {
+//echo __LINE__.'<br>';
+//echo '<pre>';
+//var_dump($tagtype);
+//var_dump($fieldname);
+//echo '<pre>';
+//exit;
+								$tagvalues .= $fieldname.' = [['.@$Comments[$tagtype][$fieldname][0].']]'."\n";
+								$Mismatched[$tagtype]  = true;
+								$FileOK = false;
+							} else {
+								$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+							}
+
+						} elseif (rtrim(substr(@$Comments[$tagtype][$fieldname][0], 0, 30)) != rtrim(substr(@$Comments['all'][$fieldname][0], 0, 30))) {
+
+//echo __LINE__.'<br>';
+//echo '<pre>';
+//var_dump($tagtype);
+//var_dump($fieldname);
+//echo '<pre>';
+//exit;
+							$tagvalues .= $fieldname.' = [['.@$Comments[$tagtype][$fieldname][0].']]'."\n";
+							if (strtolower(RemoveAccents(trim(substr(@$Comments[$tagtype][$fieldname][0], 0, 30)))) == strtolower(RemoveAccents(trim(substr(@$Comments['all'][$fieldname][0], 0, 30))))) {
+								$SemiMatched[$tagtype] = true;
+							} else {
+								$Mismatched[$tagtype]  = true;
+							}
+							$FileOK = false;
+							if (strlen(trim(@$Comments[$tagtype][$fieldname][0])) > 0) {
+								$EmptyTags[$tagtype] = false;
+							}
+
+						} else {
+
+							$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+							if (strlen(trim(@$Comments[$tagtype][$fieldname][0])) > 0) {
+								$EmptyTags[$tagtype] = false;
+							}
+
+						}
+
+					} elseif (($tagtype == 'ape') && ($fieldname == 'year')) {
+
+						if ((@$Comments['ape']['date'][0] != @$Comments['all']['year'][0]) && (@$Comments['ape']['year'][0] != @$Comments['all']['year'][0])) {
+
+							$tagvalues .= $fieldname.' = [['.@$Comments['ape']['date'][0].']]'."\n";
+							$Mismatched[$tagtype]  = true;
+							$FileOK = false;
+							if (strlen(trim(@$Comments['ape']['date'][0])) > 0) {
+								$EmptyTags[$tagtype] = false;
+							}
+
+						} else {
+
+							$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+							if (strlen(trim(@$Comments[$tagtype][$fieldname][0])) > 0) {
+								$EmptyTags[$tagtype] = false;
+							}
+
+						}
+
+					} elseif (($fieldname == 'genre') && !empty($Comments['all'][$fieldname]) && !empty($Comments[$tagtype][$fieldname]) && in_array($Comments[$tagtype][$fieldname][0], $Comments['all'][$fieldname])) {
+
+						$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+						if (strlen(trim(@$Comments[$tagtype][$fieldname][0])) > 0) {
+							$EmptyTags[$tagtype] = false;
+						}
+
+					} elseif (@$Comments[$tagtype][$fieldname][0] != @$Comments['all'][$fieldname][0]) {
+
+//echo __LINE__.'<br>';
+//echo '<pre>';
+//var_dump($tagtype);
+//var_dump($fieldname);
+//var_dump($Comments[$tagtype][$fieldname][0]);
+//var_dump($Comments['all'][$fieldname][0]);
+//echo '<pre>';
+//exit;
+						$skiptracknumberfield = false;
+						switch ($fieldname) {
+							case 'track':
+							case 'tracknumber':
+							case 'track_number':
+								if (intval(@$Comments[$tagtype][$fieldname][0]) == intval(@$Comments['all'][$fieldname][0])) {
+									$skiptracknumberfield = true;
+								}
+								break;
+						}
+						if (!$skiptracknumberfield) {
+							$tagvalues .= $fieldname.' = [['.@$Comments[$tagtype][$fieldname][0].']]'."\n";
+							if (trim(strtolower(RemoveAccents(@$Comments[$tagtype][$fieldname][0]))) == trim(strtolower(RemoveAccents(@$Comments['all'][$fieldname][0])))) {
+								$SemiMatched[$tagtype] = true;
+							} else {
+								$Mismatched[$tagtype]  = true;
+							}
+							$FileOK = false;
+							if (strlen(trim(@$Comments[$tagtype][$fieldname][0])) > 0) {
+								$EmptyTags[$tagtype] = false;
+							}
+						}
+
+					} else {
+
+						$tagvalues .= $fieldname.' = '.@$Comments[$tagtype][$fieldname][0]."\n";
+						if (strlen(trim(@$Comments[$tagtype][$fieldname][0])) > 0) {
+							$EmptyTags[$tagtype] = false;
+						}
+
+					}
+				}
+
+				if ($EmptyTags[$tagtype]) {
+					$FileOK = false;
+					$ThisLine .= '<td bgcolor="#0099cc">';
+				} elseif ($SemiMatched[$tagtype]) {
+					$ThisLine .= '<td bgcolor="#ff9999">';
+				} elseif ($Mismatched[$tagtype]) {
+					$ThisLine .= '<td bgcolor="#ff0000">';
+				} else {
+					$ThisLine .= '<td bgcolor="#00cc00">';
+				}
+				$ThisLine .= '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?synchronizetagsfrom='.$tagtype.'&filename='.urlencode($row['filename'])).'" title="'.htmlentities(rtrim($tagvalues, "\n"), ENT_QUOTES).'" TARGET="retagwindow">'.$tagtype.'</a>';
+				$ThisLine .= '</td>';
+			}
+		}
+		$ThisLine .= '</tr>';
+
+		if (!$FileOK) {
+			$NotOKfiles++;
+
+			echo '<script type="text/javascript">if (document.getElementById("Autofixing")) document.getElementById("Autofixing").innerHTML = "'.htmlentities($row['filename'], ENT_QUOTES).'";</script>';
+			flush();
+
+			if (!empty($_REQUEST['autofix'])) {
+
+				$AnyMismatched = false;
+				foreach ($Mismatched as $key => $value) {
+					if ($value && ($EmptyTags["$key"] === false)) {
+						$AnyMismatched = true;
+					}
+				}
+				if ($AnyMismatched && empty($_REQUEST['autofixforcesource'])) {
+
+					echo $ThisLine;
+
+				} else {
+
+					$TagsToSynch = '';
+					foreach ($EmptyTags as $key => $value) {
+						if ($value) {
+							switch ($key) {
+								case 'id3v1':
+									$TagsToSynch .= '1';
+									break;
+								case 'id3v2':
+									$TagsToSynch .= '2';
+									break;
+								case 'ape':
+									$TagsToSynch .= 'A';
+									break;
+							}
+						}
+					}
+
+					$autofixforcesource = (@$_REQUEST['autofixforcesource'] ? $_REQUEST['autofixforcesource'] : 'all');
+					$TagsToSynch        = (@$_REQUEST['autofixforcedest']   ? $_REQUEST['autofixforcedest']   : $TagsToSynch);
+
+					$errors = array();
+					if (SynchronizeAllTags($row['filename'], $autofixforcesource, $TagsToSynch, $errors)) {
+						$Autofixedfiles++;
+						echo '<tr bgcolor="#00CC00">';
+					} else {
+						echo '<tr bgcolor="#FF0000">';
+					}
+					echo '<td>&nbsp;</th>';
+					echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'" title="'.FixTextFields(implode("\n", $errors)).'">'.FixTextFields($row['filename']).'</a></td>';
+					echo '<td><table border="0">';
+					echo '<tr><td><b>'.$TagsToSynch.'</b></td></tr>';
+					echo '</table></td></tr>';
+				}
+
+			} else {
+
+				echo $ThisLine;
+
+			}
+		}
+	}
+
+	echo '</table><br>';
+	echo '<script type="text/javascript">if (document.getElementById("Autofixing")) document.getElementById("Autofixing").innerHTML = "";</script>';
+	echo 'Found <b>'.number_format($NotOKfiles).'</b> files with unsynchronized tags, and auto-fixed '.number_format($Autofixedfiles).' of them.';
+
+} elseif (!empty($_REQUEST['filenamepattern'])) {
+
+	$patterns['A'] = 'artist';
+	$patterns['T'] = 'title';
+	$patterns['M'] = 'album';
+	$patterns['N'] = 'track';
+	$patterns['G'] = 'genre';
+	$patterns['R'] = 'remix';
+
+	$FieldsToUse = explode(' ', wordwrap(eregi_replace('[^A-Z]', '', $_REQUEST['filenamepattern']), 1, ' ', 1));
+	//$FieldsToUse = explode(' ', wordwrap($_REQUEST['filenamepattern'], 1, ' ', 1));
+	foreach ($FieldsToUse as $FieldID) {
+		$FieldNames[] = $patterns["$FieldID"];
+	}
+
+	$SQLquery  = 'SELECT `filename`, `fileformat`, '.implode(', ', $FieldNames);
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	echo 'Files that do not match naming pattern: (<a href="'.htmlentities($_SERVER['PHP_SELF'].'?filenamepattern='.urlencode($_REQUEST['filenamepattern']).'&autofix=1').'">auto-fix</a>)<br>';
+	echo '<table border="1" cellspacing="0" cellpadding="3">';
+	echo '<tr><th>view</th><th>Why</th><td><b>Actual filename</b><br>(click to play/edit file)</td><td><b>Correct filename (based on tags)</b>'.(!@$_REQUEST['autofix'] ? '<br>(click to rename file to this)' : '').'</td></tr>';
+	$nonmatchingfilenames = 0;
+	$Pattern = $_REQUEST['filenamepattern'];
+	$PatternLength = strlen($Pattern);
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(10);
+		$PatternFilename = '';
+		for ($i = 0; $i < $PatternLength; $i++) {
+			if (isset($patterns[$Pattern{$i}])) {
+				$PatternFilename .= trim(strtr($row[$patterns[$Pattern{$i}]], ':\\*<>|', ';-¤«»¦'), ' ');
+			} else {
+				$PatternFilename .= $Pattern{$i};
+			}
+		}
+
+		// Replace "~" with "-" if characters immediately before and after are both numbers,
+		// "/" has been replaced with "~" above which is good for multi-song medley dividers,
+		// but for things like 24/7, 7/8ths, etc it looks better if it's 24-7, 7-8ths, etc.
+		$PatternFilename = eregi_replace('([ a-z]+)/([ a-z]+)', '\\1~\\2', $PatternFilename);
+		$PatternFilename = str_replace('/',  '×',  $PatternFilename);
+
+		$PatternFilename = str_replace('?',  '¿',  $PatternFilename);
+		$PatternFilename = str_replace(' "', ' “', $PatternFilename);
+		$PatternFilename = str_replace('("', '(“', $PatternFilename);
+		$PatternFilename = str_replace('-"', '-“', $PatternFilename);
+		$PatternFilename = str_replace('" ', '” ', $PatternFilename.' ');
+		$PatternFilename = str_replace('"',  '”',  $PatternFilename);
+		$PatternFilename = str_replace('  ', ' ',  $PatternFilename);
+
+
+		$ParenthesesPairs = array('()', '[]', '{}');
+		foreach ($ParenthesesPairs as $pair) {
+
+			// multiple remixes are stored tab-seperated in the database.
+			// change "{2000 Version\tSomebody Remix}" into "{2000 Version} {Somebody Remix}"
+			while (ereg('^(.*)'.preg_quote($pair{0}).'([^'.preg_quote($pair{1}).']*)('."\t".')([^'.preg_quote($pair{0}).']*)'.preg_quote($pair{1}), $PatternFilename, $matches)) {
+				$PatternFilename = $matches[1].$pair{0}.$matches[2].$pair{1}.' '.$pair{0}.$matches[4].$pair{1};
+			}
+
+			// remove empty parenthesized pairs (probably where no track numbers, remix version, etc)
+			$PatternFilename = ereg_replace(preg_quote($pair), '', $PatternFilename);
+
+			// "[01]  - Title With No Artist.mp3"  ==>  "[01] Title With No Artist.mp3"
+			$PatternFilename = ereg_replace(preg_quote($pair{1}).' +\- ', $pair{1}.' ', $PatternFilename);
+
+		}
+
+		// get rid of leading & trailing spaces if end items (artist or title for example) are missing
+		$PatternFilename  = trim($PatternFilename, ' -');
+
+		if (!$PatternFilename) {
+			// no tags to create a filename from -- skip this file
+			continue;
+		}
+		$PatternFilename .= '.'.$row['fileformat'];
+
+		$ActualFilename = basename($row['filename']);
+		if ($ActualFilename != $PatternFilename) {
+
+			$NotMatchedReasons = '';
+			if (strtolower($ActualFilename) === strtolower($PatternFilename)) {
+				$NotMatchedReasons .= 'Aa ';
+			} elseif (RemoveAccents($ActualFilename) === RemoveAccents($PatternFilename)) {
+				$NotMatchedReasons .= 'ée ';
+			}
+
+
+			$actualExt  = '.'.fileextension($ActualFilename);
+			$patternExt = '.'.fileextension($PatternFilename);
+			$ActualFilenameNoExt  = (($actualExt  != '.') ? substr($ActualFilename,   0, 0 - strlen($actualExt))  : $ActualFilename);
+			$PatternFilenameNoExt = (($patternExt != '.') ? substr($PatternFilename,  0, 0 - strlen($patternExt)) : $PatternFilename);
+
+			if (strpos($PatternFilenameNoExt, $ActualFilenameNoExt) !== false) {
+				$DifferenceBoldedName  = str_replace($ActualFilenameNoExt, '</b>'.$ActualFilenameNoExt.'<b>', $PatternFilenameNoExt);
+			} else {
+				$ShortestNameLength = min(strlen($ActualFilenameNoExt), strlen($PatternFilenameNoExt));
+				for ($DifferenceOffset = 0; $DifferenceOffset < $ShortestNameLength; $DifferenceOffset++) {
+					if ($ActualFilenameNoExt{$DifferenceOffset} !== $PatternFilenameNoExt{$DifferenceOffset}) {
+						break;
+					}
+				}
+				$DifferenceBoldedName  = '</b>'.substr($PatternFilenameNoExt, 0, $DifferenceOffset).'<b>'.substr($PatternFilenameNoExt, $DifferenceOffset);
+			}
+			$DifferenceBoldedName .= (($actualExt == $patternExt) ? '</b>'.$patternExt.'<b>' : $patternExt);
+
+
+			echo '<tr>';
+			echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">view</a></td>';
+			echo '<td>&nbsp;'.$NotMatchedReasons.'</td>';
+			echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">'.FixTextFields($ActualFilename).'</a></td>';
+
+			if (@$_REQUEST['autofix']) {
+
+				$results = '';
+				if (RenameFileFromTo($row['filename'], dirname($row['filename']).'/'.$PatternFilename, $results)) {
+					echo '<TD BGCOLOR="#009900">';
+				} else {
+					echo '<TD BGCOLOR="#FF0000">';
+				}
+				echo '<b>'.$DifferenceBoldedName.'</b></td>';
+
+
+			} else {
+
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?filenamepattern='.urlencode($_REQUEST['filenamepattern']).'&renamefilefrom='.urlencode($row['filename']).'&renamefileto='.urlencode(dirname($row['filename']).'/'.$PatternFilename)).'" title="'.FixTextFields(basename($row['filename']))."\n".FixTextFields(basename($PatternFilename)).'" TARGET="renamewindow">';
+				echo '<b>'.$DifferenceBoldedName.'</b></a></td>';
+
+			}
+			echo '</tr>';
+
+			$nonmatchingfilenames++;
+		}
+	}
+	echo '</table><br>';
+	echo 'Found '.number_format($nonmatchingfilenames).' files that do not match naming pattern<br>';
+
+
+} elseif (!empty($_REQUEST['encoderoptionsdistribution'])) {
+
+	if (isset($_REQUEST['showtagfiles'])) {
+		$SQLquery  = 'SELECT `filename`, `encoder_options` FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`encoder_options` LIKE "'.mysql_escape_string($_REQUEST['showtagfiles']).'")';
+		$SQLquery .= ' AND (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+		$SQLquery .= ' ORDER BY `filename` ASC';
+		$result = safe_mysql_query($SQLquery);
+
+		if (!empty($_REQUEST['m3u'])) {
+
+			header('Content-type: audio/x-mpegurl');
+			echo '#EXTM3U'."\n";
+			while ($row = mysql_fetch_array($result)) {
+				echo WindowsShareSlashTranslate($row['filename'])."\n";
+			}
+			exit;
+
+		} else {
+
+			echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?encoderoptionsdistribution=1').'">Show all Encoder Options</a><hr>';
+			echo 'Files with Encoder Options <b>'.$_REQUEST['showtagfiles'].'</b>:<br>';
+			echo '<table border="1" cellspacing="0" cellpadding="3">';
+			while ($row = mysql_fetch_array($result)) {
+				echo '<tr>';
+				echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+				echo '<td>'.$row['encoder_options'].'</td>';
+				echo '</tr>';
+			}
+			echo '</table>';
+
+		}
+
+	} elseif (!isset($_REQUEST['m3u'])) {
+
+		$SQLquery  = 'SELECT `encoder_options`, COUNT(*) AS `num` FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+		$SQLquery .= ' GROUP BY `encoder_options`';
+		$SQLquery .= ' ORDER BY (`encoder_options` LIKE "LAME%") DESC, (`encoder_options` LIKE "CBR%") DESC, `num` DESC, `encoder_options` ASC';
+		$result = safe_mysql_query($SQLquery);
+		echo 'Files with Encoder Options:<br>';
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>Encoder Options</th><th>Count</th><th>M3U</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr>';
+			echo '<td>'.$row['encoder_options'].'</td>';
+			echo '<TD ALIGN="RIGHT"><a href="'.htmlentities($_SERVER['PHP_SELF'].'?encoderoptionsdistribution=1&showtagfiles='.($row['encoder_options'] ? urlencode($row['encoder_options']) : '')).'">'.number_format($row['num']).'</a></td>';
+			echo '<TD ALIGN="RIGHT"><a href="'.htmlentities($_SERVER['PHP_SELF'].'?encoderoptionsdistribution=1&showtagfiles='.($row['encoder_options'] ? urlencode($row['encoder_options']) : '').'&m3u=.m3u').'">m3u</a></td>';
+			echo '</tr>';
+		}
+		echo '</table><hr>';
+
+	}
+
+} elseif (!empty($_REQUEST['tagtypes'])) {
+
+	if (!isset($_REQUEST['m3u'])) {
+		$SQLquery  = 'SELECT `tags`, COUNT(*) AS `num` FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+		$SQLquery .= ' GROUP BY `tags`';
+		$SQLquery .= ' ORDER BY `num` DESC';
+		$result = safe_mysql_query($SQLquery);
+		echo 'Files with tags:<br>';
+		echo '<table border="1" cellspacing="0" cellpadding="3">';
+		echo '<tr><th>Tags</th><th>Count</th><th>M3U</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr>';
+			echo '<td>'.$row['tags'].'</td>';
+			echo '<TD ALIGN="RIGHT"><a href="'.htmlentities($_SERVER['PHP_SELF'].'?tagtypes=1&showtagfiles='.($row['tags'] ? urlencode($row['tags']) : '')).'">'.number_format($row['num']).'</a></td>';
+			echo '<TD ALIGN="RIGHT"><a href="'.htmlentities($_SERVER['PHP_SELF'].'?tagtypes=1&showtagfiles='.($row['tags'] ? urlencode($row['tags']) : '').'&m3u=.m3u').'">m3u</a></td>';
+			echo '</tr>';
+		}
+		echo '</table><hr>';
+	}
+
+	if (isset($_REQUEST['showtagfiles'])) {
+		$SQLquery  = 'SELECT `filename`, `tags` FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`tags` LIKE "'.mysql_escape_string($_REQUEST['showtagfiles']).'")';
+		$SQLquery .= ' AND (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+		$SQLquery .= ' ORDER BY `filename` ASC';
+		$result = safe_mysql_query($SQLquery);
+
+		if (!empty($_REQUEST['m3u'])) {
+
+			header('Content-type: audio/x-mpegurl');
+			echo '#EXTM3U'."\n";
+			while ($row = mysql_fetch_array($result)) {
+				echo WindowsShareSlashTranslate($row['filename'])."\n";
+			}
+			exit;
+
+		} else {
+
+			echo '<table border="1" cellspacing="0" cellpadding="3">';
+			while ($row = mysql_fetch_array($result)) {
+				echo '<tr>';
+				echo '<td><a href="demo.browse.php?filename='.rawurlencode($row['filename']).'">'.FixTextFields($row['filename']).'</a></td>';
+				echo '<td>'.$row['tags'].'</td>';
+				echo '</tr>';
+			}
+			echo '</table>';
+
+		}
+	}
+
+
+} elseif (!empty($_REQUEST['md5datadupes'])) {
+
+	$OtherFormats = '';
+	$AVFormats    = '';
+
+	$SQLquery  = 'SELECT `md5_data`, `filename`, COUNT(*) AS `num`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`md5_data` <> "")';
+	$SQLquery .= ' GROUP BY `md5_data`';
+	$SQLquery .= ' ORDER BY `num` DESC';
+	$result = safe_mysql_query($SQLquery);
+	while (($row = mysql_fetch_array($result)) && ($row['num'] > 1)) {
+		set_time_limit(30);
+
+		$filenames = array();
+		$tags      = array();
+		$md5_data  = array();
+		$SQLquery  = 'SELECT `fileformat`, `filename`, `tags`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`md5_data` = "'.mysql_escape_string($row['md5_data']).'")';
+		$SQLquery .= ' ORDER BY `filename` ASC';
+		$result2 = safe_mysql_query($SQLquery);
+		while ($row2 = mysql_fetch_array($result2)) {
+			$thisfileformat = $row2['fileformat'];
+			$filenames[] = $row2['filename'];
+			$tags[]      = $row2['tags'];
+			$md5_data[]  = $row['md5_data'];
+		}
+
+		$thisline  = '<tr>';
+		$thisline .= '<TD VALIGN="TOP" style="font-family: monospace;">'.implode('<br>', $md5_data).'</td>';
+		$thisline .= '<TD VALIGN="TOP" NOWRAP>'.implode('<br>', $tags).'</td>';
+		$thisline .= '<TD VALIGN="TOP">'.implode('<br>', $filenames).'</td>';
+		$thisline .= '</tr>';
+
+		if (in_array($thisfileformat, $IgnoreNoTagFormats)) {
+			$OtherFormats .= $thisline;
+		} else {
+			$AVFormats .= $thisline;
+		}
+	}
+	echo 'Duplicated MD5_DATA (Audio/Video files):<table border="1" cellspacing="0" cellpadding="2">';
+	echo $AVFormats.'</table><hr>';
+	echo 'Duplicated MD5_DATA (Other files):<table border="1" cellspacing="0" cellpadding="2">';
+	echo $OtherFormats.'</table><hr>';
+
+
+} elseif (!empty($_REQUEST['artisttitledupes'])) {
+
+	if (isset($_REQUEST['m3uartist']) && isset($_REQUEST['m3utitle'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		$SQLquery  = 'SELECT `filename`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`artist` = "'.mysql_escape_string($_REQUEST['m3uartist']).'")';
+		$SQLquery .= ' AND (`title` = "'.mysql_escape_string($_REQUEST['m3utitle']).'")';
+		$SQLquery .= ' ORDER BY `playtime_seconds` ASC, `remix` ASC, `filename` ASC';
+		$result = safe_mysql_query($SQLquery);
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	}
+
+	$SQLquery  = 'SELECT `artist`, `title`, `filename`, COUNT(*) AS `num`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`artist` <> "")';
+	$SQLquery .= ' AND (`title` <> "")';
+	$SQLquery .= ' GROUP BY `artist`, `title`'.(@$_REQUEST['samemix'] ? ', `remix`' : '');
+	$SQLquery .= ' ORDER BY `num` DESC, `artist` ASC, `title` ASC, `playtime_seconds` ASC, `remix` ASC';
+	$result = safe_mysql_query($SQLquery);
+	$uniquetitles = 0;
+	$uniquefiles  = 0;
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while (($row = mysql_fetch_array($result)) && ($row['num'] > 1)) {
+			$SQLquery  = 'SELECT `filename`';
+			$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE (`artist` = "'.mysql_escape_string($row['artist']).'")';
+			$SQLquery .= ' AND (`title` = "'.mysql_escape_string($row['title']).'")';
+			if (@$_REQUEST['samemix']) {
+				$SQLquery .= ' AND (`remix` = "'.mysql_escape_string($row['remix']).'")';
+			}
+			$SQLquery .= ' ORDER BY `playtime_seconds` ASC, `remix` ASC, `filename` ASC';
+			$result2 = safe_mysql_query($SQLquery);
+			while ($row2 = mysql_fetch_array($result2)) {
+				echo WindowsShareSlashTranslate($row2['filename'])."\n";
+			}
+		}
+		exit;
+
+	} else {
+
+		echo 'Duplicated aritst + title: (<a href="'.htmlentities($_SERVER['PHP_SELF'].'?artisttitledupes=1&samemix=1').'">Identical Mix/Version only</a>)<br>';
+		echo '(<a href="'.htmlentities($_SERVER['PHP_SELF'].'?artisttitledupes=1&m3u=.m3u').'">.m3u version</a>)<br>';
+		echo '<table border="1" cellspacing="0" cellpadding="2">';
+		echo '<tr><th colspan="3">&nbsp;</th><th>Artist</th><th>Title</th><th>Version</th><th>&nbsp;</th><th>&nbsp;</th><th>Filename</th></tr>';
+
+		while (($row = mysql_fetch_array($result)) && ($row['num'] > 1)) {
+			$uniquetitles++;
+			set_time_limit(30);
+
+			$filenames = array();
+			$artists   = array();
+			$titles    = array();
+			$remixes   = array();
+			$bitrates  = array();
+			$playtimes = array();
+			$SQLquery  = 'SELECT `filename`, `artist`, `title`, `remix`, `audio_bitrate`, `vbr_method`, `playtime_seconds`, `encoder_options`';
+			$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE (`artist` = "'.mysql_escape_string($row['artist']).'")';
+			$SQLquery .= ' AND (`title` = "'.mysql_escape_string($row['title']).'")';
+			$SQLquery .= ' ORDER BY `playtime_seconds` ASC, `remix` ASC, `filename` ASC';
+			$result2 = safe_mysql_query($SQLquery);
+			while ($row2 = mysql_fetch_array($result2)) {
+				$uniquefiles++;
+				$filenames[] = $row2['filename'];
+				$artists[]   = $row2['artist'];
+				$titles[]    = $row2['title'];
+				$remixes[]   = $row2['remix'];
+				if ($row2['vbr_method']) {
+					$bitrates[]  = '<B'.($row2['encoder_options'] ? ' style="text-decoration: underline; cursor: help;" title="'.$row2['encoder_options'] : '').'">'.BitrateText($row2['audio_bitrate'] / 1000).'</b>';
+				} else {
+					$bitrates[]  = BitrateText($row2['audio_bitrate'] / 1000);
+				}
+				$playtimes[] = getid3_lib::PlaytimeString($row2['playtime_seconds']);
+			}
+
+			echo '<tr>';
+			echo '<TD NOWRAP VALIGN="TOP">';
+			foreach ($filenames as $file) {
+				echo '<a href="'.htmlentities('demo.browse.php?deletefile='.urlencode($file).'&noalert=1').'" onClick="return confirm(\'Are you sure you want to delete '.addslashes($file).'? \n(this action cannot be un-done)\');" title="Permanently delete '."\n".FixTextFields($file)."\n".'" TARGET="deletedupewindow">delete</a><br>';
+			}
+			echo '</td>';
+			echo '<TD NOWRAP VALIGN="TOP">';
+			foreach ($filenames as $file) {
+				echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($file)).'">play</a><br>';
+			}
+			echo '</td>';
+			echo '<TD VALIGN="MIDDLE" ALIGN="CENTER" ><a href="'.htmlentities($_SERVER['PHP_SELF'].'?artisttitledupes=1&m3uartist='.urlencode($artists[0]).'&m3utitle='.urlencode($titles[0])).'">play all</a></td>';
+			echo '<TD VALIGN="TOP" NOWRAP>'.implode('<br>', $artists).'</td>';
+			echo '<TD VALIGN="TOP" NOWRAP>'.implode('<br>', $titles).'</td>';
+			echo '<TD VALIGN="TOP" NOWRAP>'.implode('<br>', $remixes).'</td>';
+			echo '<TD VALIGN="TOP" NOWRAP ALIGN="RIGHT">'.implode('<br>', $bitrates).'</td>';
+			echo '<TD VALIGN="TOP" NOWRAP ALIGN="RIGHT">'.implode('<br>', $playtimes).'</td>';
+
+			echo '<TD VALIGN="TOP" NOWRAP ALIGN="LEFT"><table border="0" cellspacing="0" cellpadding="0">';
+			foreach ($filenames as $file) {
+				echo '<tr><TD NOWRAP ALIGN="RIGHT"><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($file)).'"><span style="color: #339966;">'.dirname($file).'/</span>'.basename($file).'</a></td></tr>';
+			}
+			echo '</table></td>';
+
+			echo '</tr>';
+		}
+
+	}
+	echo '</table>';
+	echo number_format($uniquefiles).' files with '.number_format($uniquetitles).' unique <i>aritst + title</i><br>';
+	echo '<hr>';
+
+} elseif (!empty($_REQUEST['filetypelist'])) {
+
+	list($fileformat, $audioformat) = explode('|', $_REQUEST['filetypelist']);
+	$SQLquery  = 'SELECT `filename`, `fileformat`, `audio_dataformat`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`fileformat` = "'.mysql_escape_string($fileformat).'")';
+	$SQLquery .= ' AND (`audio_dataformat` = "'.mysql_escape_string($audioformat).'")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	echo 'Files of format <b>'.$fileformat.'.'.$audioformat.'</b>:<table border="1" cellspacing="0" cellpadding="4">';
+	echo '<tr><th>file</th><th>audio</th><th>filename</th></tr>';
+	while ($row = mysql_fetch_array($result)) {
+		echo '<tr>';
+		echo '<td>'.$row['fileformat'].'</td>';
+		echo '<td>'.$row['audio_dataformat'].'</td>';
+		echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+		echo '</tr>';
+	}
+	echo '</table><hr>';
+
+} elseif (!empty($_REQUEST['trackinalbum'])) {
+
+	$SQLquery  = 'SELECT `filename`, `album`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`album` LIKE "% [%")';
+	$SQLquery .= ' ORDER BY `album` ASC, `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	} elseif (!empty($_REQUEST['autofix'])) {
+
+		getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
+		getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
+
+		while ($row = mysql_fetch_array($result)) {
+			set_time_limit(30);
+			$ThisFileInfo = $getID3->analyze($filename);
+			getid3_lib::CopyTagsToComments($ThisFileInfo);
+
+			if (!empty($ThisFileInfo['tags'])) {
+
+				$Album = trim(str_replace(strstr($ThisFileInfo['comments']['album'][0], ' ['), '', $ThisFileInfo['comments']['album'][0]));
+				$Track = (string) intval(str_replace(' [', '', str_replace(']', '', strstr($ThisFileInfo['comments']['album'][0], ' ['))));
+				if ($Track == '0') {
+					$Track = '';
+				}
+				if ($Album && $Track) {
+					echo '<hr>'.FixTextFields($row['filename']).'<br>';
+					echo '<i>'.$Album.'</i> (track #'.$Track.')<br>';
+					echo '<b>ID3v2:</b> '.(RemoveID3v2($row['filename'], false) ? 'removed' : 'REMOVAL FAILED!').', ';
+					echo '<b>ID3v1:</b> '.(WriteID3v1($row['filename'], @$ThisFileInfo['comments']['title'][0], @$ThisFileInfo['comments']['artist'][0], $Album, @$ThisFileInfo['comments']['year'][0], @$ThisFileInfo['comments']['comment'][0], @$ThisFileInfo['comments']['genreid'][0], $Track, false) ? 'updated' : 'UPDATE FAILED').'<br>';
+				} else {
+					echo ' . ';
+				}
+
+			} else {
+
+				echo '<hr>FAILED<br>'.FixTextFields($row['filename']).'<hr>';
+
+			}
+			flush();
+		}
+
+	} else {
+
+		echo '<b>'.number_format(mysql_num_rows($result)).'</b> files with <b>[??]</b>-format track numbers in album field:<br>';
+		if (mysql_num_rows($result) > 0) {
+			echo '(<a href="'.htmlentities($_SERVER['PHP_SELF'].'?trackinalbum=1&m3u=.m3u').'">.m3u version</a>)<br>';
+			echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?trackinalbum=1&autofix=1').'">Try to auto-fix</a><br>';
+			echo '<table border="1" cellspacing="0" cellpadding="4">';
+			while ($row = mysql_fetch_array($result)) {
+				echo '<tr>';
+				echo '<td>'.$row['album'].'</td>';
+				echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+				echo '</tr>';
+			}
+			echo '</table>';
+		}
+		echo '<hr>';
+
+	}
+
+} elseif (!empty($_REQUEST['fileextensions'])) {
+
+	$SQLquery  = 'SELECT `filename`, `fileformat`, `audio_dataformat`, `video_dataformat`, `tags`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	$invalidextensionfiles = 0;
+	$invalidextensionline  = '<table border="1" cellspacing="0" cellpadding="4">';
+	$invalidextensionline .= '<tr><th>file</th><th>audio</th><th>video</th><th>tags</th><th>actual</th><th>correct</th><th>filename</th></tr>';
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(30);
+
+		$acceptableextensions = AcceptableExtensions($row['fileformat'], $row['audio_dataformat'], $row['video_dataformat']);
+		$actualextension      = strtolower(fileextension($row['filename']));
+		if ($acceptableextensions && !in_array($actualextension, $acceptableextensions)) {
+			$invalidextensionfiles++;
+
+			$invalidextensionline .= '<tr>';
+			$invalidextensionline .= '<td>'.$row['fileformat'].'</td>';
+			$invalidextensionline .= '<td>'.$row['audio_dataformat'].'</td>';
+			$invalidextensionline .= '<td>'.$row['video_dataformat'].'</td>';
+			$invalidextensionline .= '<td>'.$row['tags'].'</td>';
+			$invalidextensionline .= '<td>'.$actualextension.'</td>';
+			$invalidextensionline .= '<td>'.implode('; ', $acceptableextensions).'</td>';
+			$invalidextensionline .= '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+			$invalidextensionline .= '</tr>';
+		}
+	}
+	$invalidextensionline .= '</table><hr>';
+	echo number_format($invalidextensionfiles).' files with incorrect filename extension:<br>';
+	echo $invalidextensionline;
+
+} elseif (isset($_REQUEST['genredistribution'])) {
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		$SQLquery  = 'SELECT `filename`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (BINARY `genre` = "'.$_REQUEST['genredistribution'].'")';
+		$SQLquery .= ' AND (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+		$SQLquery .= ' ORDER BY `filename` ASC';
+		$result = safe_mysql_query($SQLquery);
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	} else {
+
+		if ($_REQUEST['genredistribution'] == '%') {
+
+			$SQLquery  = 'SELECT COUNT(*) AS `num`, `genre`';
+			$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE (`fileformat` NOT LIKE "'.implode('") AND (`fileformat` NOT LIKE "', $IgnoreNoTagFormats).'")';
+			$SQLquery .= ' GROUP BY `genre`';
+			$SQLquery .= ' ORDER BY `num` DESC';
+			$result = safe_mysql_query($SQLquery);
+			getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
+			echo '<table border="1" cellspacing="0" cellpadding="4">';
+			echo '<tr><th>Count</th><th>Genre</th><th>m3u</th></tr>';
+			while ($row = mysql_fetch_array($result)) {
+				$GenreID = getid3_id3v1::LookupGenreID($row['genre']);
+				if (is_numeric($GenreID)) {
+					echo '<tr bgcolor="#00FF00;">';
+				} else {
+					echo '<tr bgcolor="#FF9999;">';
+				}
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?genredistribution='.urlencode($row['genre'])).'">'.number_format($row['num']).'</a></td>';
+				echo '<td nowrap>'.str_replace("\t", '<br>', $row['genre']).'</td>';
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3u=.m3u&genredistribution='.urlencode($row['genre'])).'">.m3u</a></td>';
+				echo '</tr>';
+			}
+			echo '</table><hr>';
+
+		} else {
+
+			$SQLquery  = 'SELECT `filename`, `genre`';
+			$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+			$SQLquery .= ' WHERE (`genre` LIKE "'.mysql_escape_string($_REQUEST['genredistribution']).'")';
+			$SQLquery .= ' ORDER BY `filename` ASC';
+			$result = safe_mysql_query($SQLquery);
+			echo '<a href="'.htmlentities($_SERVER['PHP_SELF'].'?genredistribution='.urlencode('%')).'">All Genres</a><br>';
+			echo '<table border="1" cellspacing="0" cellpadding="4">';
+			echo '<tr><th>Genre</th><th>m3u</th><th>Filename</th></tr>';
+			while ($row = mysql_fetch_array($result)) {
+				echo '<tr>';
+				echo '<TD NOWRAP>'.str_replace("\t", '<br>', $row['genre']).'</td>';
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+				echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+				echo '</tr>';
+			}
+			echo '</table><hr>';
+
+		}
+
+
+	}
+
+} elseif (!empty($_REQUEST['formatdistribution'])) {
+
+	$SQLquery  = 'SELECT `fileformat`, `audio_dataformat`, COUNT(*) AS `num`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' GROUP BY `fileformat`, `audio_dataformat`';
+	$SQLquery .= ' ORDER BY `num` DESC';
+	$result = safe_mysql_query($SQLquery);
+	echo 'File format distribution:<table border="1" cellspacing="0" cellpadding="4">';
+	echo '<tr><th>Number</th><th>Format</th></tr>';
+	while ($row = mysql_fetch_array($result)) {
+		echo '<tr>';
+		echo '<TD ALIGN="RIGHT">'.number_format($row['num']).'</td>';
+		echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?filetypelist='.$row['fileformat'].'|'.$row['audio_dataformat']).'">'.($row['fileformat'] ? $row['fileformat'] : '<i>unknown</i>').(($row['audio_dataformat'] && ($row['audio_dataformat'] != $row['fileformat'])) ? '.'.$row['audio_dataformat'] : '').'</a></td>';
+		echo '</tr>';
+	}
+	echo '</table><hr>';
+
+} elseif (!empty($_REQUEST['errorswarnings'])) {
+
+	$SQLquery  = 'SELECT `filename`, `error`, `warning`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`error` <> "")';
+	$SQLquery .= ' OR (`warning` <> "")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+
+	if (!empty($_REQUEST['m3u'])) {
+
+		header('Content-type: audio/x-mpegurl');
+		echo '#EXTM3U'."\n";
+		while ($row = mysql_fetch_array($result)) {
+			echo WindowsShareSlashTranslate($row['filename'])."\n";
+		}
+		exit;
+
+	} else {
+
+		echo number_format(mysql_num_rows($result)).' files with errors or warnings:<br>';
+		echo '(<a href="'.htmlentities($_SERVER['PHP_SELF'].'?errorswarnings=1&m3u=.m3u').'">.m3u version</a>)<br>';
+		echo '<table border="1" cellspacing="0" cellpadding="4">';
+		echo '<tr><th>Filename</th><th>Error</th><th>Warning</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr>';
+			echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td>';
+			echo '<td>'.(!empty($row['error'])   ? '<li>'.str_replace("\t", '<li>', FixTextFields($row['error'])).'</li>' : '&nbsp;').'</td>';
+			echo '<td>'.(!empty($row['warning']) ? '<li>'.str_replace("\t", '<li>', FixTextFields($row['warning'])).'</li>' : '&nbsp;').'</td>';
+			echo '</tr>';
+		}
+	}
+	echo '</table><hr>';
+
+} elseif (!empty($_REQUEST['fixid3v1padding'])) {
+
+	getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v1.php', __FILE__, true);
+	$id3v1_writer = new getid3_write_id3v1;
+
+	$SQLquery  = 'SELECT `filename`, `error`, `warning`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`fileformat` = "mp3")';
+	$SQLquery .= ' AND (`warning` <> "")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	$totaltofix = mysql_num_rows($result);
+	$rowcounter = 0;
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(30);
+		if (strpos($row['warning'], 'Some ID3v1 fields do not use NULL characters for padding') !== false) {
+			set_time_limit(30);
+			$id3v1_writer->filename = $row['filename'];
+			echo ($id3v1_writer->FixID3v1Padding() ? '<span style="color: #009900;">fixed - ' : '<span style="color: #FF0000;">error - ');
+		} else {
+			echo '<span style="color: #0000FF;">No error? - ';
+		}
+		echo '['.++$rowcounter.' / '.$totaltofix.'] ';
+		echo FixTextFields($row['filename']).'</span><br>';
+		flush();
+	}
+
+} elseif (!empty($_REQUEST['vbrmethod'])) {
+
+	if ($_REQUEST['vbrmethod'] == '1') {
+
+		$SQLquery  = 'SELECT COUNT(*) AS `num`, `vbr_method`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' GROUP BY `vbr_method`';
+		$SQLquery .= ' ORDER BY `vbr_method`';
+		$result = safe_mysql_query($SQLquery);
+		echo 'VBR methods:<table border="1" cellspacing="0" cellpadding="4">';
+		echo '<tr><th>Count</th><th>VBR Method</th></tr>';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr>';
+			echo '<TD ALIGN="RIGHT">'.FixTextFields(number_format($row['num'])).'</td>';
+			if ($row['vbr_method']) {
+				echo '<td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?vbrmethod='.$row['vbr_method']).'">'.FixTextFields($row['vbr_method']).'</a></td>';
+			} else {
+				echo '<td><i>CBR</i></td>';
+			}
+			echo '</tr>';
+		}
+		echo '</table>';
+
+	} else {
+
+		$SQLquery  = 'SELECT `filename`';
+		$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+		$SQLquery .= ' WHERE (`vbr_method` = "'.mysql_escape_string($_REQUEST['vbrmethod']).'")';
+		$result = safe_mysql_query($SQLquery);
+		echo number_format(mysql_num_rows($result)).' files with VBR_method of "'.$_REQUEST['vbrmethod'].'":<table border="1" cellspacing="0" cellpadding="3">';
+		while ($row = mysql_fetch_array($result)) {
+			echo '<tr><td><a href="'.htmlentities($_SERVER['PHP_SELF'].'?m3ufilename='.urlencode($row['filename'])).'">m3u</a></td>';
+			echo '<td><a href="'.htmlentities('demo.browse.php?filename='.rawurlencode($row['filename'])).'">'.FixTextFields($row['filename']).'</a></td></tr>';
+		}
+		echo '</table>';
+
+	}
+	echo '<hr>';
+
+} elseif (!empty($_REQUEST['correctcase'])) {
+
+	$SQLquery  = 'SELECT `filename`, `fileformat`';
+	$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+	$SQLquery .= ' WHERE (`fileformat` <> "")';
+	$SQLquery .= ' ORDER BY `filename` ASC';
+	$result = safe_mysql_query($SQLquery);
+	echo 'Copy and paste the following into a DOS batch file. You may have to run this script more than once to catch all the changes (remember to scan for deleted/changed files and rescan directory between scans)<hr>';
+	echo '<PRE>';
+	$lastdir = '';
+	while ($row = mysql_fetch_array($result)) {
+		set_time_limit(30);
+		$CleanedFilename = CleanUpFileName($row['filename']);
+		if ($row['filename'] != $CleanedFilename) {
+			if (strtolower($lastdir) != strtolower(str_replace('/', '\\', dirname($row['filename'])))) {
+				$lastdir = str_replace('/', '\\', dirname($row['filename']));
+				echo 'cd "'.$lastdir.'"'."\n";
+			}
+			echo 'ren "'.basename($row['filename']).'" "'.basename(CleanUpFileName($row['filename'])).'"'."\n";
+		}
+	}
+	echo '</PRE>';
+	echo '<hr>';
+
+}
+
+function CleanUpFileName($filename) {
+	$DirectoryName = dirname($filename);
+	$FileExtension = fileextension(basename($filename));
+	$BaseFilename  = basename($filename, '.'.$FileExtension);
+
+	$BaseFilename = strtolower($BaseFilename);
+	$BaseFilename = str_replace('_', ' ', $BaseFilename);
+	//$BaseFilename = str_replace('-', ' - ', $BaseFilename);
+	$BaseFilename = str_replace('(', ' (', $BaseFilename);
+	$BaseFilename = str_replace('( ', '(', $BaseFilename);
+	$BaseFilename = str_replace(')', ') ', $BaseFilename);
+	$BaseFilename = str_replace(' )', ')', $BaseFilename);
+	$BaseFilename = str_replace(' \'\'', ' “', $BaseFilename);
+	$BaseFilename = str_replace('\'\' ', '” ', $BaseFilename);
+	$BaseFilename = str_replace(' vs ', ' vs. ', $BaseFilename);
+	while (strstr($BaseFilename, '  ') !== false) {
+		$BaseFilename = str_replace('  ', ' ', $BaseFilename);
+	}
+	$BaseFilename = trim($BaseFilename);
+
+	return $DirectoryName.'/'.BetterUCwords($BaseFilename).'.'.strtolower($FileExtension);
+}
+
+function BetterUCwords($string) {
+	$stringlength = strlen($string);
+
+	$string{0} = strtoupper($string{0});
+	for ($i = 1; $i < $stringlength; $i++) {
+		if (($string{$i - 1} == '\'') && ($i > 1) && (($string{$i - 2} == 'O') || ($string{$i - 2} == ' '))) {
+			// O'Clock, 'Em
+			$string{$i} = strtoupper($string{$i});
+		} elseif (ereg('^[\'A-Za-z0-9À-ÿ]$', $string{$i - 1})) {
+			$string{$i} = strtolower($string{$i});
+		} else {
+			$string{$i} = strtoupper($string{$i});
+		}
+	}
+
+	static $LowerCaseWords = array('vs.', 'feat.');
+	static $UpperCaseWords = array('DJ', 'USA', 'II', 'MC', 'CD', 'TV', '\'N\'');
+
+	$OutputListOfWords = array();
+	$ListOfWords = explode(' ', $string);
+	foreach ($ListOfWords as $ThisWord) {
+		if (in_array(strtolower(str_replace('(', '', $ThisWord)), $LowerCaseWords)) {
+			$ThisWord = strtolower($ThisWord);
+		} elseif (in_array(strtoupper(str_replace('(', '', $ThisWord)), $UpperCaseWords)) {
+			$ThisWord = strtoupper($ThisWord);
+		} elseif ((substr($ThisWord, 0, 2) == 'Mc') && (strlen($ThisWord) > 2)) {
+			$ThisWord{2} = strtoupper($ThisWord{2});
+		} elseif ((substr($ThisWord, 0, 3) == 'Mac') && (strlen($ThisWord) > 3)) {
+			$ThisWord{3} = strtoupper($ThisWord{3});
+		}
+		$OutputListOfWords[] = $ThisWord;
+	}
+	$UCstring = implode(' ', $OutputListOfWords);
+	$UCstring = str_replace(' From “', ' from “', $UCstring);
+	$UCstring = str_replace(' \'n\' ', ' \'N\' ', $UCstring);
+
+	return $UCstring;
+}
+
+
+
+echo '<hr><form action="'.FixTextFields($_SERVER['PHP_SELF']).'">';
+echo '<b>Warning:</b> Scanning a new directory will erase all previous entries in the database!<br>';
+echo 'Directory: <input type="text" name="scan" size="50" value="'.FixTextFields(!empty($_REQUEST['scan']) ? $_REQUEST['scan'] : '').'"> ';
+echo '<input type="submit" value="Go" onClick="return confirm(\'Are you sure you want to erase all entries in the database and start scanning again?\');">';
+echo '</form>';
+echo '<hr><form action="'.FixTextFields($_SERVER['PHP_SELF']).'">';
+echo 'Re-scanning a new directory will only add new, previously unscanned files into the list (and not erase the database).<br>';
+echo 'Directory: <input type="text" name="newscan" size="50" value="'.FixTextFields(!empty($_REQUEST['newscan']) ? $_REQUEST['newscan'] : '').'"> ';
+echo '<input type="SUBMIT" value="Go">';
+echo '</form><hr>';
+echo '<ul>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?deadfilescheck=1').'">Remove deleted or changed files from database</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?md5datadupes=1').'">List files with identical MD5_DATA values</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?artisttitledupes=1').'">List files with identical artist + title</a> (<a href="'.$_SERVER['PHP_SELF'].'?artisttitledupes=1&samemix=1">same mix only</a>)</li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?fileextensions=1').'">File with incorrect file extension</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?formatdistribution=1').'">File Format Distribution</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?audiobitrates=1').'">Audio Bitrate Distribution</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?vbrmethod=1').'">VBR_Method Distribution</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?tagtypes=1').'">Tag Type Distribution</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?genredistribution='.urlencode('%')).'">Genre Distribution</a></li>';
+//echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?missingtrackvolume=1').'">Scan for missing track volume information (update database from pre-v1.7.0b5)</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?encoderoptionsdistribution=1').'">Encoder Options Distribution</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?encodedbydistribution='.urlencode('%')).'">Encoded By (ID3v2) Distribution</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?trackinalbum=1').'">Track number in Album field</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?tracknoalbum=1').'">Track number, but no Album</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?titlefeat=1').'">"feat." in Title field</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?emptygenres=1').'">Blank genres</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?trackzero=1').'">Track "zero"</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?nonemptycomments=1').'">non-empty comments</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?unsynchronizedtags=2A1').'">Tags that are not synchronized</a> (<a href="'.$_SERVER['PHP_SELF'].'?unsynchronizedtags=2A1&autofix=1">autofix</a>)</li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?filenamepattern='.urlencode('[N] A - T {R}')).'">Filenames that don\'t match pattern</a> (<a href="?filenamepattern='.urlencode('[N] A - T {R}').'&autofix=1">auto-fix</a>)</li>';
+//echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?filenamepattern='.urlencode('A - T')).'">Filenames that don\'t match pattern</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?correctcase=1').'">Correct filename case (Win/DOS)</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?fixid3v1padding=1').'">Fix ID3v1 invalid padding</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?errorswarnings=1').'">Files with Errors and/or Warnings</a></li>';
+echo '<li><a href="'.htmlentities($_SERVER['PHP_SELF'].'?rescanerrors=1').'">Re-scan only files with Errors and/or Warnings</a></li>';
+echo '</ul>';
+
+$SQLquery  = 'SELECT COUNT(*) AS `TotalFiles`, SUM(`playtime_seconds`) AS `TotalPlaytime`, SUM(`filesize`) AS `TotalFilesize`, AVG(`playtime_seconds`) AS `AvgPlaytime`, AVG(`filesize`) AS `AvgFilesize`, AVG(`audio_bitrate` + `video_bitrate`) AS `AvgBitrate`';
+$SQLquery .= ' FROM `'.GETID3_DB_TABLE.'`';
+$result = mysql_query($SQLquery);
+if ($row = mysql_fetch_array($result)) {
+	echo '<hr><b>Currently in the database:</b><TABLE>';
+	echo '<tr><TH ALIGN="LEFT">Total Files</th><td>'.number_format($row['TotalFiles']).'</td></tr>';
+	echo '<tr><TH ALIGN="LEFT">Total Filesize</th><td>'.number_format($row['TotalFilesize'] / 1048576).' MB</td></tr>';
+	echo '<tr><TH ALIGN="LEFT">Total Playtime</th><td>'.number_format($row['TotalPlaytime'] / 3600, 1).' hours</td></tr>';
+	echo '<tr><TH ALIGN="LEFT">Average Filesize</th><td>'.number_format($row['AvgFilesize'] / 1048576, 1).' MB</td></tr>';
+	echo '<tr><TH ALIGN="LEFT">Average Playtime</th><td>'.getid3_lib::PlaytimeString($row['AvgPlaytime']).'</td></tr>';
+	echo '<tr><TH ALIGN="LEFT">Average Bitrate</th><td>'.BitrateText($row['AvgBitrate'] / 1000, 1).'</td></tr>';
+	echo '</table>';
+}
+
+?>
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.simple.php b/apps/media/getID3/demos/demo.simple.php
new file mode 100644
index 0000000000000000000000000000000000000000..db937f1e2da8819960d13df7871990382f070757
--- /dev/null
+++ b/apps/media/getID3/demos/demo.simple.php
@@ -0,0 +1,53 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.simple.php - part of getID3()                    //
+// Sample script for scanning a single directory and           //
+// displaying a few pieces of information for each file        //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+echo '<HTML><HEAD>';
+echo '<TITLE>getID3() - /demo/demo.simple.php (sample script)</TITLE>';
+echo '<STYLE>BODY,TD,TH { font-family: sans-serif; font-size: 9pt; }</STYLE>';
+echo '</HEAD><BODY>';
+
+
+// include getID3() library (can be in a different directory if full path is specified)
+require_once('../getid3/getid3.php');
+
+// Initialize getID3 engine
+$getID3 = new getID3;
+
+$DirectoryToScan = '/change/to/directory/you/want/to/scan'; // change to whatever directory you want to scan
+$dir = opendir($DirectoryToScan);
+echo '<TABLE BORDER="1" CELLSPACING="0" CELLPADDING="3">';
+echo '<TR><TH>Filename</TH><TH>Artist</TH><TH>Title</TH><TH>Bitrate</TH><TH>Playtime</TH></TR>';
+while (($file = readdir($dir)) !== false) {
+	$FullFileName = realpath($DirectoryToScan.'/'.$file);
+	if (is_file($FullFileName)) {
+		set_time_limit(30);
+
+		$ThisFileInfo = $getID3->analyze($FullFileName);
+
+		getid3_lib::CopyTagsToComments($ThisFileInfo);
+
+		// output desired information in whatever format you want
+		echo '<TR>';
+		echo '<TD>'.$ThisFileInfo['filenamepath'].'</TD>';
+		echo '<TD>'.(!empty($ThisFileInfo['comments_html']['artist']) ? implode('<BR>', $ThisFileInfo['comments_html']['artist']) : '&nbsp;').'</TD>';
+		echo '<TD>'.(!empty($ThisFileInfo['comments_html']['title'])  ? implode('<BR>', $ThisFileInfo['comments_html']['title'])  : '&nbsp;').'</TD>';
+		echo '<TD ALIGN="RIGHT">'.(!empty($ThisFileInfo['audio']['bitrate'])        ? round($ThisFileInfo['audio']['bitrate'] / 1000).' kbps'   : '&nbsp;').'</TD>';
+		echo '<TD ALIGN="RIGHT">'.(!empty($ThisFileInfo['playtime_string'])         ? $ThisFileInfo['playtime_string']                          : '&nbsp;').'</TD>';
+		echo '</TR>';
+	}
+}
+
+?>
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.simple.write.php b/apps/media/getID3/demos/demo.simple.write.php
new file mode 100644
index 0000000000000000000000000000000000000000..468984e39e0d9a910c4af31581e6f6ad5914d926
--- /dev/null
+++ b/apps/media/getID3/demos/demo.simple.write.php
@@ -0,0 +1,54 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.simple.write.php - part of getID3()              //
+// Sample script showing basic syntax for writing tags         //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+$TaggingFormat = 'UTF-8';
+
+require_once('../getid3/getid3.php');
+// Initialize getID3 engine
+$getID3 = new getID3;
+$getID3->setOption(array('encoding'=>$TaggingFormat));
+
+require_once('../getid3/write.php');
+// Initialize getID3 tag-writing module
+$tagwriter = new getid3_writetags;
+//$tagwriter->filename       = '/path/to/file.mp3';
+$tagwriter->filename       = 'd:/file.mp3';
+$tagwriter->tagformats     = array('id3v1', 'id3v2.3');
+
+// set various options (optional)
+$tagwriter->overwrite_tags = true;
+$tagwriter->tag_encoding   = $TaggingFormat;
+$tagwriter->remove_other_tags = true;
+
+// populate data array
+$TagData['title'][]   = 'My Song';
+$TagData['artist'][]  = 'The Artist';
+$TagData['album'][]   = 'Greatest Hits';
+$TagData['year'][]    = '2004';
+$TagData['genre'][]   = 'Rock';
+$TagData['comment'][] = 'excellent!';
+$TagData['track'][]   = '04/16';
+
+$tagwriter->tag_data = $TagData;
+
+// write tags
+if ($tagwriter->WriteTags()) {
+	echo 'Successfully wrote tags<br>';
+	if (!empty($tagwriter->warnings)) {
+		echo 'There were some warnings:<br>'.implode('<br><br>', $tagwriter->warnings);
+	}
+} else {
+	echo 'Failed to write tags!<br>'.implode('<br><br>', $tagwriter->errors);
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/demo.write.php b/apps/media/getID3/demos/demo.write.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f03b4783834cfe9d422a38a588f7fecd345a983
--- /dev/null
+++ b/apps/media/getID3/demos/demo.write.php
@@ -0,0 +1,271 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /demo/demo.write.php - part of getID3()                     //
+// sample script for demonstrating writing ID3v1 and ID3v2     //
+// tags for MP3, or Ogg comment tags for Ogg Vorbis            //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+die('Due to a security issue, this demo has been disabled. It can be enabled by removing line 16 in demos/demo.write.php');
+
+
+$TaggingFormat = 'UTF-8';
+
+header('Content-Type: text/html; charset='.$TaggingFormat);
+echo '<HTML><HEAD><TITLE>getID3() - Sample tag writer</TITLE></HEAD><BODY>';
+
+require_once('../getid3/getid3.php');
+// Initialize getID3 engine
+$getID3 = new getID3;
+$getID3->setOption(array('encoding'=>$TaggingFormat));
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.php', __FILE__, true);
+
+$browsescriptfilename = 'demo.browse.php';
+
+function FixTextFields($text) {
+	return htmlentities(getid3_lib::SafeStripSlashes($text), ENT_QUOTES);
+}
+
+$Filename = (isset($_REQUEST['Filename']) ? getid3_lib::SafeStripSlashes($_REQUEST['Filename']) : '');
+
+
+
+if (isset($_POST['WriteTags'])) {
+
+	$TagFormatsToWrite = (isset($_POST['TagFormatsToWrite']) ? $_POST['TagFormatsToWrite'] : array());
+	if (!empty($TagFormatsToWrite)) {
+		echo 'starting to write tag(s)<BR>';
+
+		$tagwriter = new getid3_writetags;
+		$tagwriter->filename       = $Filename;
+		$tagwriter->tagformats     = $TagFormatsToWrite;
+		$tagwriter->overwrite_tags = true;
+		$tagwriter->tag_encoding   = $TaggingFormat;
+		if (!empty($_POST['remove_other_tags'])) {
+			$tagwriter->remove_other_tags = true;
+		}
+
+		$commonkeysarray = array('Title', 'Artist', 'Album', 'Year', 'Comment');
+		foreach ($commonkeysarray as $key) {
+			if (!empty($_POST[$key])) {
+				$TagData[strtolower($key)][] = getid3_lib::SafeStripSlashes($_POST[$key]);
+			}
+		}
+		if (!empty($_POST['Genre'])) {
+			$TagData['genre'][] = getid3_lib::SafeStripSlashes($_POST['Genre']);
+		}
+		if (!empty($_POST['GenreOther'])) {
+			$TagData['genre'][] = getid3_lib::SafeStripSlashes($_POST['GenreOther']);
+		}
+		if (!empty($_POST['Track'])) {
+			$TagData['track'][] = getid3_lib::SafeStripSlashes($_POST['Track'].(!empty($_POST['TracksTotal']) ? '/'.$_POST['TracksTotal'] : ''));
+		}
+
+		if (!empty($_FILES['userfile']['tmp_name'])) {
+			if (in_array('id3v2.4', $tagwriter->tagformats) || in_array('id3v2.3', $tagwriter->tagformats) || in_array('id3v2.2', $tagwriter->tagformats)) {
+				if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
+					if ($fd = @fopen($_FILES['userfile']['tmp_name'], 'rb')) {
+						$APICdata = fread($fd, filesize($_FILES['userfile']['tmp_name']));
+						fclose ($fd);
+
+						list($APIC_width, $APIC_height, $APIC_imageTypeID) = GetImageSize($_FILES['userfile']['tmp_name']);
+						$imagetypes = array(1=>'gif', 2=>'jpeg', 3=>'png');
+						if (isset($imagetypes[$APIC_imageTypeID])) {
+
+							$TagData['attached_picture'][0]['data']          = $APICdata;
+							$TagData['attached_picture'][0]['picturetypeid'] = $_POST['APICpictureType'];
+							$TagData['attached_picture'][0]['description']   = $_FILES['userfile']['name'];
+							$TagData['attached_picture'][0]['mime']          = 'image/'.$imagetypes[$APIC_imageTypeID];
+
+						} else {
+							echo '<B>invalid image format (only GIF, JPEG, PNG)</B><BR>';
+						}
+					} else {
+						echo '<B>cannot open '.$_FILES['userfile']['tmp_name'].'</B><BR>';
+					}
+				} else {
+					echo '<B>!is_uploaded_file('.$_FILES['userfile']['tmp_name'].')</B><BR>';
+				}
+			} else {
+				echo '<B>WARNING:</B> Can only embed images for ID3v2<BR>';
+			}
+		}
+
+		$tagwriter->tag_data = $TagData;
+		if ($tagwriter->WriteTags()) {
+			echo 'Successfully wrote tags<BR>';
+			if (!empty($tagwriter->warnings)) {
+				echo 'There were some warnings:<BLOCKQUOTE STYLE="background-color:#FFCC33; padding: 10px;">'.implode('<BR><BR>', $tagwriter->warnings).'</BLOCKQUOTE>';
+			}
+		} else {
+			echo 'Failed to write tags!<BLOCKQUOTE STYLE="background-color:#FF9999; padding: 10px;">'.implode('<BR><BR>', $tagwriter->errors).'</BLOCKQUOTE>';
+		}
+
+	} else {
+
+		echo 'WARNING: no tag formats selected for writing - nothing written';
+
+	}
+	echo '<HR>';
+
+}
+
+
+echo '<H4>Sample tag editor/writer</H4>';
+echo '<A HREF="'.$browsescriptfilename.'?listdirectory='.rawurlencode(realpath(dirname($Filename))).'">Browse current directory</A><BR>';
+if (!empty($Filename)) {
+	echo '<A HREF="'.$_SERVER['PHP_SELF'].'">Start Over</A><BR><BR>';
+	echo '<TABLE BORDER="3" CELLSPACING="0" CELLPADDING="4"><FORM ACTION="'.$_SERVER['PHP_SELF'].'" METHOD="POST" ENCTYPE="multipart/form-data">';
+	echo '<TR><TD ALIGN="RIGHT"><B>Filename: </B></TD><TD><INPUT TYPE="HIDDEN" NAME="Filename" VALUE="'.FixTextFields($Filename).'"><A HREF="'.$browsescriptfilename.'?filename='.rawurlencode($Filename).'" TARGET="_blank">'.$Filename.'</A></TD></TR>';
+	if (file_exists($Filename)) {
+
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+		$OldThisFileInfo = $getID3->analyze($Filename);
+		getid3_lib::CopyTagsToComments($OldThisFileInfo);
+
+		switch ($OldThisFileInfo['fileformat']) {
+			case 'mp3':
+			case 'mp2':
+			case 'mp1':
+				$ValidTagTypes = array('id3v1', 'id3v2.3', 'ape');
+				break;
+
+			case 'mpc':
+				$ValidTagTypes = array('ape');
+				break;
+
+			case 'ogg':
+				if (@$OldThisFileInfo['audio']['dataformat'] == 'flac') {
+					//$ValidTagTypes = array('metaflac');
+					// metaflac doesn't (yet) work with OggFLAC files
+					$ValidTagTypes = array();
+				} else {
+					$ValidTagTypes = array('vorbiscomment');
+				}
+				break;
+
+			case 'flac':
+				$ValidTagTypes = array('metaflac');
+				break;
+
+			case 'real':
+				$ValidTagTypes = array('real');
+				break;
+
+			default:
+				$ValidTagTypes = array();
+				break;
+		}
+		echo '<TR><TD ALIGN="RIGHT"><B>Title</B></TD> <TD><INPUT TYPE="TEXT" SIZE="40" NAME="Title"  VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['title'])).'"></TD></TR>';
+		echo '<TR><TD ALIGN="RIGHT"><B>Artist</B></TD><TD><INPUT TYPE="TEXT" SIZE="40" NAME="Artist" VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['artist'])).'"></TD></TR>';
+		echo '<TR><TD ALIGN="RIGHT"><B>Album</B></TD> <TD><INPUT TYPE="TEXT" SIZE="40" NAME="Album"  VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['album'])).'"></TD></TR>';
+		echo '<TR><TD ALIGN="RIGHT"><B>Year</B></TD>  <TD><INPUT TYPE="TEXT" SIZE="4"  NAME="Year"   VALUE="'.FixTextFields(@implode(', ', @$OldThisFileInfo['comments']['year'])).'"></TD></TR>';
+
+		$TracksTotal = '';
+		$TrackNumber = '';
+		if (!empty($OldThisFileInfo['comments']['tracknumber']) && is_array($OldThisFileInfo['comments']['tracknumber'])) {
+			$RawTrackNumberArray = $OldThisFileInfo['comments']['tracknumber'];
+		} elseif (!empty($OldThisFileInfo['comments']['track']) && is_array($OldThisFileInfo['comments']['track'])) {
+			$RawTrackNumberArray = $OldThisFileInfo['comments']['track'];
+		} else {
+			$RawTrackNumberArray = array();
+		}
+		foreach ($RawTrackNumberArray as $key => $value) {
+			if (strlen($value) > strlen($TrackNumber)) {
+				// ID3v1 may store track as "3" but ID3v2/APE would store as "03/16"
+				$TrackNumber = $value;
+			}
+		}
+		if (strstr($TrackNumber, '/')) {
+			list($TrackNumber, $TracksTotal) = explode('/', $TrackNumber);
+		}
+		echo '<TR><TD ALIGN="RIGHT"><B>Track</B></TD><TD><INPUT TYPE="TEXT" SIZE="2"  NAME="Track"  VALUE="'.FixTextFields($TrackNumber).'"> of <INPUT TYPE="TEXT" SIZE="2" NAME="TracksTotal"  VALUE="'.FixTextFields($TracksTotal).'"></TD></TR>';
+
+		$ArrayOfGenresTemp = getid3_id3v1::ArrayOfGenres();   // get the array of genres
+		foreach ($ArrayOfGenresTemp as $key => $value) {      // change keys to match displayed value
+			$ArrayOfGenres[$value] = $value;
+		}
+		unset($ArrayOfGenresTemp);                            // remove temporary array
+		unset($ArrayOfGenres['Cover']);                       // take off these special cases
+		unset($ArrayOfGenres['Remix']);
+		unset($ArrayOfGenres['Unknown']);
+		$ArrayOfGenres['']      = '- Unknown -';              // Add special cases back in with renamed key/value
+		$ArrayOfGenres['Cover'] = '-Cover-';
+		$ArrayOfGenres['Remix'] = '-Remix-';
+		asort($ArrayOfGenres);                                // sort into alphabetical order
+		echo '<TR><TD ALIGN="RIGHT"><B>Genre</B></TD><TD><SELECT NAME="Genre">';
+		$AllGenresArray = (!empty($OldThisFileInfo['comments']['genre']) ? $OldThisFileInfo['comments']['genre'] : array());
+		foreach ($ArrayOfGenres as $key => $value) {
+			echo '<OPTION VALUE="'.$key.'"';
+			if (in_array($key, $AllGenresArray)) {
+				echo ' SELECTED';
+				unset($AllGenresArray[array_search($key, $AllGenresArray)]);
+				sort($AllGenresArray);
+			}
+			echo '>'.$value.'</OPTION>';
+			//echo '<OPTION VALUE="'.FixTextFields($value).'"'.((@$OldThisFileInfo['comments']['genre'][0] == $value) ? ' SELECTED' : '').'>'.$value.'</OPTION>';
+		}
+		echo '</SELECT><INPUT TYPE="TEXT" NAME="GenreOther" SIZE="10" VALUE="'.FixTextFields(@$AllGenresArray[0]).'"></TD></TR>';
+
+		echo '<TR><TD ALIGN="RIGHT"><B>Write Tags</B></TD><TD>';
+		foreach ($ValidTagTypes as $ValidTagType) {
+			echo '<INPUT TYPE="CHECKBOX" NAME="TagFormatsToWrite[]" VALUE="'.$ValidTagType.'"';
+			if (count($ValidTagTypes) == 1) {
+				echo ' CHECKED';
+			} else {
+				switch ($ValidTagType) {
+					case 'id3v2.2':
+					case 'id3v2.3':
+					case 'id3v2.4':
+						if (isset($OldThisFileInfo['tags']['id3v2'])) {
+							echo ' CHECKED';
+						}
+						break;
+
+					default:
+						if (isset($OldThisFileInfo['tags'][$ValidTagType])) {
+							echo ' CHECKED';
+						}
+						break;
+				}
+			}
+			echo '>'.$ValidTagType.'<BR>';
+		}
+		if (count($ValidTagTypes) > 1) {
+			echo '<hr><input type="checkbox" name="remove_other_tags" value="1"> Remove non-selected tag formats when writing new tag<br>';
+		}
+		echo '</TD></TR>';
+
+		echo '<TR><TD ALIGN="RIGHT"><B>Comment</B></TD><TD><TEXTAREA COLS="30" ROWS="3" NAME="Comment" WRAP="VIRTUAL">'.(isset($OldThisFileInfo['comments']['comment']) ? @implode("\n", $OldThisFileInfo['comments']['comment']) : '').'</TEXTAREA></TD></TR>';
+
+		echo '<TR><TD ALIGN="RIGHT"><B>Picture</B><BR>(ID3v2 only)</TD><TD><INPUT TYPE="FILE" NAME="userfile" ACCEPT="image/jpeg, image/gif, image/png"><BR>';
+		echo '<SELECT NAME="APICpictureType">';
+		$APICtypes = getid3_id3v2::APICPictureTypeLookup('', true);
+		foreach ($APICtypes as $key => $value) {
+			echo '<OPTION VALUE="'.FixTextFields($key).'">'.FixTextFields($value).'</OPTION>';
+		}
+		echo '</SELECT></TD></TR>';
+		echo '<TR><TD ALIGN="CENTER" COLSPAN="2"><INPUT TYPE="SUBMIT" NAME="WriteTags" VALUE="Save Changes"> ';
+		echo '<INPUT TYPE="RESET" VALUE="Reset"></TD></TR>';
+
+	} else {
+
+		echo '<TR><TD ALIGN="RIGHT"><B>Error</B></TD><TD>'.FixTextFields($Filename).' does not exist</TD></TR>';
+
+	}
+	echo '</FORM></TABLE>';
+
+}
+
+?>
+</BODY>
+</HTML>
\ No newline at end of file
diff --git a/apps/media/getID3/demos/getid3.css b/apps/media/getID3/demos/getid3.css
new file mode 100644
index 0000000000000000000000000000000000000000..0694eff2ad147e463d33d1e606870094bdee9a3b
--- /dev/null
+++ b/apps/media/getID3/demos/getid3.css
@@ -0,0 +1,195 @@
+
+/**
+* Common elements
+*/
+
+body {
+	font: 12px Verdana, sans-serif;
+	background-color: white;
+	color:            black;
+	margin-top:     6px;
+	margin-bottom:  30px;
+	margin-left:    12px;
+	margin-right:   12px;
+}
+
+
+h1 {
+	font: bold 18px Verdana, sans-serif;
+	line-height:   26px;
+	margin-top:    12px;
+	margin-bottom: 15px;
+	margin-left:    0px;
+	margin-right:   7px;
+	background-color: #e6eaf6;
+	padding-left:   10px;
+	padding-top:    2px;
+	padding-bottom: 4px;
+}
+
+
+h3 {
+	font: bold 13px Verdana, sans-serif;
+	line-height:   26px;
+	margin-top:    12px;
+	margin-bottom:  0px;
+	margin-left:    0px;
+	margin-right:   7px;
+	padding-left:   4px;
+}
+
+
+ul {
+	margin-top: 0px;
+}
+
+
+p, li {
+	font: 9pt/135% sans-serif;
+	margin-top:    1x;
+	margin-bottom: 0x;
+}
+
+
+a, a:link, a:visited {
+	color: #0000cc;
+}
+
+
+hr {
+	height: 0;
+	border: solid gray 0;
+	border-top-width: thin;
+	width: 700px;
+}
+
+
+table.table td {
+	font: 9pt sans-serif;
+	padding-top:    1px;
+	padding-bottom: 1px;
+	padding-left:   5px;
+	padding-right:  5px;
+}
+
+
+table.table td.header {
+	background-color: #cccccc;
+	padding-top:    2px;
+	padding-bottom: 2px;
+	font-weight: bold;
+}
+
+
+table.table tr.even_files {
+	background-color: #fefefe;
+}
+
+
+table.table tr.odd_files {
+	background-color: #e9e9e9;
+}
+
+
+table.dump {
+	border-top:  solid 1px #cccccc;
+	border-left: solid 1px #cccccc;
+	margin: 2px;
+}
+
+
+table.dump td {
+	font: 9pt sans-serif;
+	padding-top:    1px;
+	padding-bottom: 1px;
+	padding-left:   5px;
+	padding-right:  5px;
+	border-right:  solid 1px #cccccc;
+	border-bottom: solid 1px #cccccc;
+}
+
+
+td.dump_string {
+	font-weight: bold;
+	color: #0000cc;
+}
+
+
+td.dump_integer {
+	color: #cc0000;
+	font-weight: bold;
+}
+
+
+td.dump_double {
+	color: orange;
+	font-weight: bold;
+}
+
+
+td.dump_boolean {
+	color: #333333;
+	font-weight: bold;
+}
+
+
+.error {
+	color: red
+}
+
+
+
+
+/**
+* Tool Tips
+*/
+
+.tooltip {
+    font: 9pt sans-serif;
+    background: #ffffe1;
+    color: 	black;
+    border: 	black 1px solid;
+    margin: 	2px;
+    padding: 	7px;
+    position: 	absolute;
+    top: 	10px;
+    left: 	10px;
+    z-index: 	10000;
+    visibility: hidden;
+}
+
+
+.tooltip p {
+     margin-top:   -2px;
+     margin-bottom: 4px;
+}
+
+
+
+
+ /**
+ * Forms
+ */
+
+table.form td {
+ 	font: 9pt/135% sans-serif;
+ 	padding: 2px;
+}
+
+
+select, input {
+	font: 9pt/135% sans-serif;
+}
+
+.select, .field {
+	width: 260px;
+}
+
+#sel_field {
+	width: 85px;
+}
+
+
+.button {
+	margin-top: 10px;
+}
diff --git a/apps/media/getID3/demos/index.php b/apps/media/getID3/demos/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..13783f0daf44dfa87fe1c09825957d8bf787bd7f
--- /dev/null
+++ b/apps/media/getID3/demos/index.php
@@ -0,0 +1 @@
+In this directory are a number of examples of how to use <A HREF="http://www.getid3.org">getID3()</A> - if you don't know what to run, take a look at <A HREF="demo.browse.php">demo.browse.php</A>
\ No newline at end of file
diff --git a/apps/media/getID3/dependencies.txt b/apps/media/getID3/dependencies.txt
new file mode 100644
index 0000000000000000000000000000000000000000..34a72ba86526d4104ccb1a7051308961423ba9d5
--- /dev/null
+++ b/apps/media/getID3/dependencies.txt
@@ -0,0 +1,24 @@
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// dependencies.txt - part of getID3()                         //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+lyrics3     depends on   apetag (optional)
+ogg         depends on   flac
+id3v2       depends on   id3v1
+apetag      depends on   id3v1 (optional, writing only)
+bonk        depends on   id3v2 (optional)
+riff        depends on   mp3
+mpeg        depends on   mp3
+quicktime   depends on   mp3
+flac        depends on   ogg
+optimfrog   depends on   riff
+la          depends on   riff
+lpac        depends on   riff
+asf         depends on   riff, id3v1 (optional)
diff --git a/apps/media/getID3/getid3/extension.cache.dbm.php b/apps/media/getID3/getid3/extension.cache.dbm.php
new file mode 100644
index 0000000000000000000000000000000000000000..051bb1f0d7a0e828751249a25037d5b89df70a7e
--- /dev/null
+++ b/apps/media/getID3/getid3/extension.cache.dbm.php
@@ -0,0 +1,222 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// extension.cache.dbm.php - part of getID3()                  //
+// Please see readme.txt for more information                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// This extension written by Allan Hansen <ahØartemis*dk>      //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+/**
+* This is a caching extension for getID3(). It works the exact same
+* way as the getID3 class, but return cached information very fast
+*
+* Example:
+*
+*    Normal getID3 usage (example):
+*
+*       require_once 'getid3/getid3.php';
+*       $getID3 = new getID3;
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*    getID3_cached usage:
+*
+*       require_once 'getid3/getid3.php';
+*       require_once 'getid3/getid3/extension.cache.dbm.php';
+*       $getID3 = new getID3_cached('db3', '/tmp/getid3_cache.dbm',
+*                                          '/tmp/getid3_cache.lock');
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*
+* Supported Cache Types
+*
+*   SQL Databases:          (use extension.cache.mysql)
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   mysql               host, database, username, password
+*
+*
+*   DBM-Style Databases:    (this extension)
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   gdbm                dbm_filename, lock_filename
+*   ndbm                dbm_filename, lock_filename
+*   db2                 dbm_filename, lock_filename
+*   db3                 dbm_filename, lock_filename
+*   db4                 dbm_filename, lock_filename  (PHP5 required)
+*
+*   PHP must have write access to both dbm_filename and lock_filename.
+*
+*
+* Recommended Cache Types
+*
+*   Infrequent updates, many reads      any DBM
+*   Frequent updates                    mysql
+*/
+
+
+class getID3_cached_dbm extends getID3
+{
+
+	// public: constructor - see top of this file for cache type and cache_options
+	function getID3_cached_dbm($cache_type, $dbm_filename, $lock_filename) {
+
+		// Check for dba extension
+		if (!extension_loaded('dba')) {
+			die('PHP is not compiled with dba support, required to use DBM style cache.');
+		}
+
+		// Check for specific dba driver
+		if (function_exists('dba_handlers')) {  // PHP 4.3.0+
+			if (!in_array('db3', dba_handlers())) {
+				die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
+			}
+		}
+		else { // PHP <= 4.2.3
+			ob_start(); // nasty, buy the only way to check...
+			phpinfo();
+			$contents = ob_get_contents();
+			ob_end_clean();
+			if (!strstr($contents, $cache_type)) {
+				die('PHP is not compiled --with '.$cache_type.' support, required to use DBM style cache.');
+			}
+		}
+
+		// Create lock file if needed
+		if (!file_exists($lock_filename)) {
+			if (!touch($lock_filename)) {
+				die('failed to create lock file: ' . $lock_filename);
+			}
+		}
+
+		// Open lock file for writing
+		if (!is_writeable($lock_filename)) {
+			die('lock file: ' . $lock_filename . ' is not writable');
+		}
+		$this->lock = fopen($lock_filename, 'w');
+
+		// Acquire exclusive write lock to lock file
+		flock($this->lock, LOCK_EX);
+
+		// Create dbm-file if needed
+		if (!file_exists($dbm_filename)) {
+			if (!touch($dbm_filename)) {
+				die('failed to create dbm file: ' . $dbm_filename);
+			}
+		}
+
+		// Try to open dbm file for writing
+		$this->dba = @dba_open($dbm_filename, 'w', $cache_type);
+		if (!$this->dba) {
+
+			// Failed - create new dbm file
+			$this->dba = dba_open($dbm_filename, 'n', $cache_type);
+
+			if (!$this->dba) {
+				die('failed to create dbm file: ' . $dbm_filename);
+			}
+
+			// Insert getID3 version number
+			dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
+		}
+
+		// Init misc values
+		$this->cache_type   = $cache_type;
+		$this->dbm_filename = $dbm_filename;
+
+		// Register destructor
+		register_shutdown_function(array($this, '__destruct'));
+
+		// Check version number and clear cache if changed
+		if (dba_fetch(GETID3_VERSION, $this->dba) != GETID3_VERSION) {
+			$this->clear_cache();
+		}
+
+		parent::getID3();
+	}
+
+
+
+	// public: destuctor
+	function __destruct() {
+
+		// Close dbm file
+		@dba_close($this->dba);
+
+		// Release exclusive lock
+		@flock($this->lock, LOCK_UN);
+
+		// Close lock file
+		@fclose($this->lock);
+	}
+
+
+
+	// public: clear cache
+	function clear_cache() {
+
+		// Close dbm file
+		dba_close($this->dba);
+
+		// Create new dbm file
+		$this->dba = dba_open($this->dbm_filename, 'n', $this->cache_type);
+
+		if (!$this->dba) {
+			die('failed to clear cache/recreate dbm file: ' . $this->dbm_filename);
+		}
+
+		// Insert getID3 version number
+		dba_insert(GETID3_VERSION, GETID3_VERSION, $this->dba);
+
+		// Reregister shutdown function
+		register_shutdown_function(array($this, '__destruct'));
+	}
+
+
+
+	// public: analyze file
+	function analyze($filename) {
+
+		if (file_exists($filename)) {
+
+			// Calc key     filename::mod_time::size    - should be unique
+			$key = $filename . '::' . filemtime($filename) . '::' . filesize($filename);
+
+			// Loopup key
+			$result = dba_fetch($key, $this->dba);
+
+			// Hit
+			if ($result !== false) {
+				return unserialize($result);
+			}
+		}
+
+		// Miss
+		$result = parent::analyze($filename);
+
+		// Save result
+		if (file_exists($filename)) {
+			dba_insert($key, serialize($result), $this->dba);
+		}
+
+		return $result;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/extension.cache.mysql.php b/apps/media/getID3/getid3/extension.cache.mysql.php
new file mode 100644
index 0000000000000000000000000000000000000000..40ea6883eaab6d2047e687a481ccfbe54c9fbc9b
--- /dev/null
+++ b/apps/media/getID3/getid3/extension.cache.mysql.php
@@ -0,0 +1,171 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// extension.cache.mysql.php - part of getID3()                //
+// Please see readme.txt for more information                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// This extension written by Allan Hansen <ahØartemis*dk>      //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+/**
+* This is a caching extension for getID3(). It works the exact same
+* way as the getID3 class, but return cached information very fast
+*
+* Example:  (see also demo.cache.mysql.php in /demo/)
+*
+*    Normal getID3 usage (example):
+*
+*       require_once 'getid3/getid3.php';
+*       $getID3 = new getID3;
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*    getID3_cached usage:
+*
+*       require_once 'getid3/getid3.php';
+*       require_once 'getid3/getid3/extension.cache.mysql.php';
+*       $getID3 = new getID3_cached_mysql('localhost', 'database',
+*                                         'username', 'password');
+*       $getID3->encoding = 'UTF-8';
+*       $info1 = $getID3->analyze('file1.flac');
+*       $info2 = $getID3->analyze('file2.wv');
+*
+*
+* Supported Cache Types    (this extension)
+*
+*   SQL Databases:
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   mysql               host, database, username, password
+*
+*
+*   DBM-Style Databases:    (use extension.cache.dbm)
+*
+*   cache_type          cache_options
+*   -------------------------------------------------------------------
+*   gdbm                dbm_filename, lock_filename
+*   ndbm                dbm_filename, lock_filename
+*   db2                 dbm_filename, lock_filename
+*   db3                 dbm_filename, lock_filename
+*   db4                 dbm_filename, lock_filename  (PHP5 required)
+*
+*   PHP must have write access to both dbm_filename and lock_filename.
+*
+*
+* Recommended Cache Types
+*
+*   Infrequent updates, many reads      any DBM
+*   Frequent updates                    mysql
+*/
+
+
+class getID3_cached_mysql extends getID3
+{
+
+	// private vars
+	var $cursor;
+	var $connection;
+
+
+	// public: constructor - see top of this file for cache type and cache_options
+	function getID3_cached_mysql($host, $database, $username, $password) {
+
+		// Check for mysql support
+		if (!function_exists('mysql_pconnect')) {
+			die('PHP not compiled with mysql support.');
+		}
+
+		// Connect to database
+		$this->connection = mysql_pconnect($host, $username, $password);
+		if (!$this->connection) {
+			die('mysql_pconnect() failed - check permissions and spelling.');
+		}
+
+		// Select database
+		if (!mysql_select_db($database, $this->connection)) {
+			die('Cannot use database '.$database);
+		}
+
+		// Create cache table if not exists
+		$this->create_table();
+
+		// Check version number and clear cache if changed
+		$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename` = '".GETID3_VERSION."') AND (`filesize` = '-1') AND (`filetime` = '-1') AND (`analyzetime` = '-1')", $this->connection);
+		list($version) = @mysql_fetch_array($this->cursor);
+		if ($version != GETID3_VERSION) {
+			$this->clear_cache();
+		}
+
+		parent::getID3();
+	}
+
+
+
+	// public: clear cache
+	function clear_cache() {
+
+		$this->cursor = mysql_query("DELETE FROM `getid3_cache`", $this->connection);
+		$this->cursor = mysql_query("INSERT INTO `getid3_cache` VALUES ('".GETID3_VERSION."', -1, -1, -1, '".GETID3_VERSION."')", $this->connection);
+	}
+
+
+
+	// public: analyze file
+	function analyze($filename) {
+
+		if (file_exists($filename)) {
+
+			// Short-hands
+			$filetime = filemtime($filename);
+			$filesize = filesize($filename);
+			$filenam2 = mysql_escape_string($filename);
+
+			// Loopup file
+			$this->cursor = mysql_query("SELECT `value` FROM `getid3_cache` WHERE (`filename`='".$filenam2."') AND (`filesize`='".$filesize."') AND (`filetime`='".$filetime."')", $this->connection);
+			list($result) = @mysql_fetch_array($this->cursor);
+
+			// Hit
+			if ($result) {
+				return unserialize($result);
+			}
+		}
+
+		// Miss
+		$result = parent::analyze($filename);
+
+		// Save result
+		if (file_exists($filename)) {
+			$res2 = mysql_escape_string(serialize($result));
+			$this->cursor = mysql_query("INSERT INTO `getid3_cache` (`filename`, `filesize`, `filetime`, `analyzetime`, `value`) VALUES ('".$filenam2."', '".$filesize."', '".$filetime."', '".time()."', '".$res2."')", $this->connection);
+		}
+		return $result;
+	}
+
+
+
+	// private: (re)create sql table
+	function create_table($drop = false) {
+
+		$this->cursor = mysql_query("CREATE TABLE IF NOT EXISTS `getid3_cache` (
+			`filename` VARCHAR(255) NOT NULL DEFAULT '',
+			`filesize` INT(11) NOT NULL DEFAULT '0',
+			`filetime` INT(11) NOT NULL DEFAULT '0',
+			`analyzetime` INT(11) NOT NULL DEFAULT '0',
+			`value` TEXT NOT NULL,
+			PRIMARY KEY (`filename`,`filesize`,`filetime`)) TYPE=MyISAM", $this->connection);
+		echo mysql_error($this->connection);
+	}
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/getid3.lib.php b/apps/media/getID3/getid3/getid3.lib.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ed5e361f50339fdf913df89ba4af472e4c835b0
--- /dev/null
+++ b/apps/media/getID3/getid3/getid3.lib.php
@@ -0,0 +1,1309 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// getid3.lib.php - part of getID3()                           //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_lib
+{
+
+	function PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true) {
+		$returnstring = '';
+		for ($i = 0; $i < strlen($string); $i++) {
+			if ($hex) {
+				$returnstring .= str_pad(dechex(ord($string{$i})), 2, '0', STR_PAD_LEFT);
+			} else {
+				$returnstring .= ' '.(ereg("[\x20-\x7E]", $string{$i}) ? $string{$i} : '¤');
+			}
+			if ($spaces) {
+				$returnstring .= ' ';
+			}
+		}
+		if ($htmlsafe) {
+			$returnstring = htmlentities($returnstring);
+		}
+		return $returnstring;
+	}
+
+	function SafeStripSlashes($text) {
+		if (get_magic_quotes_gpc()) {
+			return stripslashes($text);
+		}
+		return $text;
+	}
+
+
+	function trunc($floatnumber) {
+		// truncates a floating-point number at the decimal point
+		// returns int (if possible, otherwise float)
+		if ($floatnumber >= 1) {
+			$truncatednumber = floor($floatnumber);
+		} elseif ($floatnumber <= -1) {
+			$truncatednumber = ceil($floatnumber);
+		} else {
+			$truncatednumber = 0;
+		}
+		if ($truncatednumber <= 1073741824) { // 2^30
+			$truncatednumber = (int) $truncatednumber;
+		}
+		return $truncatednumber;
+	}
+
+
+	function CastAsInt($floatnum) {
+		// convert to float if not already
+		$floatnum = (float) $floatnum;
+
+		// convert a float to type int, only if possible
+		if (getid3_lib::trunc($floatnum) == $floatnum) {
+			// it's not floating point
+			if ($floatnum <= 2147483647) { // 2^31
+				// it's within int range
+				$floatnum = (int) $floatnum;
+			}
+		}
+		return $floatnum;
+	}
+
+
+	function DecimalBinary2Float($binarynumerator) {
+		$numerator   = getid3_lib::Bin2Dec($binarynumerator);
+		$denominator = getid3_lib::Bin2Dec('1'.str_repeat('0', strlen($binarynumerator)));
+		return ($numerator / $denominator);
+	}
+
+
+	function NormalizeBinaryPoint($binarypointnumber, $maxbits=52) {
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
+		if (strpos($binarypointnumber, '.') === false) {
+			$binarypointnumber = '0.'.$binarypointnumber;
+		} elseif ($binarypointnumber{0} == '.') {
+			$binarypointnumber = '0'.$binarypointnumber;
+		}
+		$exponent = 0;
+		while (($binarypointnumber{0} != '1') || (substr($binarypointnumber, 1, 1) != '.')) {
+			if (substr($binarypointnumber, 1, 1) == '.') {
+				$exponent--;
+				$binarypointnumber = substr($binarypointnumber, 2, 1).'.'.substr($binarypointnumber, 3);
+			} else {
+				$pointpos = strpos($binarypointnumber, '.');
+				$exponent += ($pointpos - 1);
+				$binarypointnumber = str_replace('.', '', $binarypointnumber);
+				$binarypointnumber = $binarypointnumber{0}.'.'.substr($binarypointnumber, 1);
+			}
+		}
+		$binarypointnumber = str_pad(substr($binarypointnumber, 0, $maxbits + 2), $maxbits + 2, '0', STR_PAD_RIGHT);
+		return array('normalized'=>$binarypointnumber, 'exponent'=>(int) $exponent);
+	}
+
+
+	function Float2BinaryDecimal($floatvalue) {
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
+		$maxbits = 128; // to how many bits of precision should the calculations be taken?
+		$intpart   = getid3_lib::trunc($floatvalue);
+		$floatpart = abs($floatvalue - $intpart);
+		$pointbitstring = '';
+		while (($floatpart != 0) && (strlen($pointbitstring) < $maxbits)) {
+			$floatpart *= 2;
+			$pointbitstring .= (string) getid3_lib::trunc($floatpart);
+			$floatpart -= getid3_lib::trunc($floatpart);
+		}
+		$binarypointnumber = decbin($intpart).'.'.$pointbitstring;
+		return $binarypointnumber;
+	}
+
+
+	function Float2String($floatvalue, $bits) {
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
+		switch ($bits) {
+			case 32:
+				$exponentbits = 8;
+				$fractionbits = 23;
+				break;
+
+			case 64:
+				$exponentbits = 11;
+				$fractionbits = 52;
+				break;
+
+			default:
+				return false;
+				break;
+		}
+		if ($floatvalue >= 0) {
+			$signbit = '0';
+		} else {
+			$signbit = '1';
+		}
+		$normalizedbinary  = getid3_lib::NormalizeBinaryPoint(getid3_lib::Float2BinaryDecimal($floatvalue), $fractionbits);
+		$biasedexponent    = pow(2, $exponentbits - 1) - 1 + $normalizedbinary['exponent']; // (127 or 1023) +/- exponent
+		$exponentbitstring = str_pad(decbin($biasedexponent), $exponentbits, '0', STR_PAD_LEFT);
+		$fractionbitstring = str_pad(substr($normalizedbinary['normalized'], 2), $fractionbits, '0', STR_PAD_RIGHT);
+
+		return getid3_lib::BigEndian2String(getid3_lib::Bin2Dec($signbit.$exponentbitstring.$fractionbitstring), $bits % 8, false);
+	}
+
+
+	function LittleEndian2Float($byteword) {
+		return getid3_lib::BigEndian2Float(strrev($byteword));
+	}
+
+
+	function BigEndian2Float($byteword) {
+		// ANSI/IEEE Standard 754-1985, Standard for Binary Floating Point Arithmetic
+		// http://www.psc.edu/general/software/packages/ieee/ieee.html
+		// http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee.html
+
+		$bitword = getid3_lib::BigEndian2Bin($byteword);
+		if (!$bitword) {
+            return 0;
+        }
+		$signbit = $bitword{0};
+
+		switch (strlen($byteword) * 8) {
+			case 32:
+				$exponentbits = 8;
+				$fractionbits = 23;
+				break;
+
+			case 64:
+				$exponentbits = 11;
+				$fractionbits = 52;
+				break;
+
+			case 80:
+				// 80-bit Apple SANE format
+				// http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
+				$exponentstring = substr($bitword, 1, 15);
+				$isnormalized = intval($bitword{16});
+				$fractionstring = substr($bitword, 17, 63);
+				$exponent = pow(2, getid3_lib::Bin2Dec($exponentstring) - 16383);
+				$fraction = $isnormalized + getid3_lib::DecimalBinary2Float($fractionstring);
+				$floatvalue = $exponent * $fraction;
+				if ($signbit == '1') {
+					$floatvalue *= -1;
+				}
+				return $floatvalue;
+				break;
+
+			default:
+				return false;
+				break;
+		}
+		$exponentstring = substr($bitword, 1, $exponentbits);
+		$fractionstring = substr($bitword, $exponentbits + 1, $fractionbits);
+		$exponent = getid3_lib::Bin2Dec($exponentstring);
+		$fraction = getid3_lib::Bin2Dec($fractionstring);
+
+		if (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction != 0)) {
+			// Not a Number
+			$floatvalue = false;
+		} elseif (($exponent == (pow(2, $exponentbits) - 1)) && ($fraction == 0)) {
+			if ($signbit == '1') {
+				$floatvalue = '-infinity';
+			} else {
+				$floatvalue = '+infinity';
+			}
+		} elseif (($exponent == 0) && ($fraction == 0)) {
+			if ($signbit == '1') {
+				$floatvalue = -0;
+			} else {
+				$floatvalue = 0;
+			}
+			$floatvalue = ($signbit ? 0 : -0);
+		} elseif (($exponent == 0) && ($fraction != 0)) {
+			// These are 'unnormalized' values
+			$floatvalue = pow(2, (-1 * (pow(2, $exponentbits - 1) - 2))) * getid3_lib::DecimalBinary2Float($fractionstring);
+			if ($signbit == '1') {
+				$floatvalue *= -1;
+			}
+		} elseif ($exponent != 0) {
+			$floatvalue = pow(2, ($exponent - (pow(2, $exponentbits - 1) - 1))) * (1 + getid3_lib::DecimalBinary2Float($fractionstring));
+			if ($signbit == '1') {
+				$floatvalue *= -1;
+			}
+		}
+		return (float) $floatvalue;
+	}
+
+
+	function BigEndian2Int($byteword, $synchsafe=false, $signed=false) {
+		$intvalue = 0;
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			if ($synchsafe) { // disregard MSB, effectively 7-bit bytes
+				$intvalue = $intvalue | (ord($byteword{$i}) & 0x7F) << (($bytewordlen - 1 - $i) * 7);
+			} else {
+				$intvalue += ord($byteword{$i}) * pow(256, ($bytewordlen - 1 - $i));
+			}
+		}
+		if ($signed && !$synchsafe) {
+			// synchsafe ints are not allowed to be signed
+			switch ($bytewordlen) {
+				case 1:
+				case 2:
+				case 3:
+				case 4:
+					$signmaskbit = 0x80 << (8 * ($bytewordlen - 1));
+					if ($intvalue & $signmaskbit) {
+						$intvalue = 0 - ($intvalue & ($signmaskbit - 1));
+					}
+					break;
+
+				default:
+					die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2Int()');
+					break;
+			}
+		}
+		return getid3_lib::CastAsInt($intvalue);
+	}
+
+
+	function LittleEndian2Int($byteword, $signed=false) {
+		return getid3_lib::BigEndian2Int(strrev($byteword), false, $signed);
+	}
+
+
+	function BigEndian2Bin($byteword) {
+		$binvalue = '';
+		$bytewordlen = strlen($byteword);
+		for ($i = 0; $i < $bytewordlen; $i++) {
+			$binvalue .= str_pad(decbin(ord($byteword{$i})), 8, '0', STR_PAD_LEFT);
+		}
+		return $binvalue;
+	}
+
+
+	function BigEndian2String($number, $minbytes=1, $synchsafe=false, $signed=false) {
+		if ($number < 0) {
+			return false;
+		}
+		$maskbyte = (($synchsafe || $signed) ? 0x7F : 0xFF);
+		$intstring = '';
+		if ($signed) {
+			if ($minbytes > 4) {
+				die('ERROR: Cannot have signed integers larger than 32-bits in getid3_lib::BigEndian2String()');
+			}
+			$number = $number & (0x80 << (8 * ($minbytes - 1)));
+		}
+		while ($number != 0) {
+			$quotient = ($number / ($maskbyte + 1));
+			$intstring = chr(ceil(($quotient - floor($quotient)) * $maskbyte)).$intstring;
+			$number = floor($quotient);
+		}
+		return str_pad($intstring, $minbytes, "\x00", STR_PAD_LEFT);
+	}
+
+
+	function Dec2Bin($number) {
+		while ($number >= 256) {
+			$bytes[] = (($number / 256) - (floor($number / 256))) * 256;
+			$number = floor($number / 256);
+		}
+		$bytes[] = $number;
+		$binstring = '';
+		for ($i = 0; $i < count($bytes); $i++) {
+			$binstring = (($i == count($bytes) - 1) ? decbin($bytes[$i]) : str_pad(decbin($bytes[$i]), 8, '0', STR_PAD_LEFT)).$binstring;
+		}
+		return $binstring;
+	}
+
+
+	function Bin2Dec($binstring, $signed=false) {
+		$signmult = 1;
+		if ($signed) {
+			if ($binstring{0} == '1') {
+				$signmult = -1;
+			}
+			$binstring = substr($binstring, 1);
+		}
+		$decvalue = 0;
+		for ($i = 0; $i < strlen($binstring); $i++) {
+			$decvalue += ((int) substr($binstring, strlen($binstring) - $i - 1, 1)) * pow(2, $i);
+		}
+		return getid3_lib::CastAsInt($decvalue * $signmult);
+	}
+
+
+	function Bin2String($binstring) {
+		// return 'hi' for input of '0110100001101001'
+		$string = '';
+		$binstringreversed = strrev($binstring);
+		for ($i = 0; $i < strlen($binstringreversed); $i += 8) {
+			$string = chr(getid3_lib::Bin2Dec(strrev(substr($binstringreversed, $i, 8)))).$string;
+		}
+		return $string;
+	}
+
+
+	function LittleEndian2String($number, $minbytes=1, $synchsafe=false) {
+		$intstring = '';
+		while ($number > 0) {
+			if ($synchsafe) {
+				$intstring = $intstring.chr($number & 127);
+				$number >>= 7;
+			} else {
+				$intstring = $intstring.chr($number & 255);
+				$number >>= 8;
+			}
+		}
+		return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT);
+	}
+
+
+	function array_merge_clobber($array1, $array2) {
+		// written by kcØhireability*com
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (!is_array($array1) || !is_array($array2)) {
+			return false;
+		}
+		$newarray = $array1;
+		foreach ($array2 as $key => $val) {
+			if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
+				$newarray[$key] = getid3_lib::array_merge_clobber($newarray[$key], $val);
+			} else {
+				$newarray[$key] = $val;
+			}
+		}
+		return $newarray;
+	}
+
+
+	function array_merge_noclobber($array1, $array2) {
+		if (!is_array($array1) || !is_array($array2)) {
+			return false;
+		}
+		$newarray = $array1;
+		foreach ($array2 as $key => $val) {
+			if (is_array($val) && isset($newarray[$key]) && is_array($newarray[$key])) {
+				$newarray[$key] = getid3_lib::array_merge_noclobber($newarray[$key], $val);
+			} elseif (!isset($newarray[$key])) {
+				$newarray[$key] = $val;
+			}
+		}
+		return $newarray;
+	}
+
+
+	function fileextension($filename, $numextensions=1) {
+		if (strstr($filename, '.')) {
+			$reversedfilename = strrev($filename);
+			$offset = 0;
+			for ($i = 0; $i < $numextensions; $i++) {
+				$offset = strpos($reversedfilename, '.', $offset + 1);
+				if ($offset === false) {
+					return '';
+				}
+			}
+			return strrev(substr($reversedfilename, 0, $offset));
+		}
+		return '';
+	}
+
+
+	function PlaytimeString($playtimeseconds) {
+		$sign = (($playtimeseconds < 0) ? '-' : '');
+		$playtimeseconds = abs($playtimeseconds);
+		$contentseconds = round((($playtimeseconds / 60) - floor($playtimeseconds / 60)) * 60);
+		$contentminutes = floor($playtimeseconds / 60);
+		if ($contentseconds >= 60) {
+			$contentseconds -= 60;
+			$contentminutes++;
+		}
+		return $sign.intval($contentminutes).':'.str_pad($contentseconds, 2, 0, STR_PAD_LEFT);
+	}
+
+
+	function image_type_to_mime_type($imagetypeid) {
+		// only available in PHP v4.3.0+
+		static $image_type_to_mime_type = array();
+		if (empty($image_type_to_mime_type)) {
+			$image_type_to_mime_type[1]  = 'image/gif';                     // GIF
+			$image_type_to_mime_type[2]  = 'image/jpeg';                    // JPEG
+			$image_type_to_mime_type[3]  = 'image/png';                     // PNG
+			$image_type_to_mime_type[4]  = 'application/x-shockwave-flash'; // Flash
+			$image_type_to_mime_type[5]  = 'image/psd';                     // PSD
+			$image_type_to_mime_type[6]  = 'image/bmp';                     // BMP
+			$image_type_to_mime_type[7]  = 'image/tiff';                    // TIFF: little-endian (Intel)
+			$image_type_to_mime_type[8]  = 'image/tiff';                    // TIFF: big-endian (Motorola)
+			//$image_type_to_mime_type[9]  = 'image/jpc';                   // JPC
+			//$image_type_to_mime_type[10] = 'image/jp2';                   // JPC
+			//$image_type_to_mime_type[11] = 'image/jpx';                   // JPC
+			//$image_type_to_mime_type[12] = 'image/jb2';                   // JPC
+			$image_type_to_mime_type[13] = 'application/x-shockwave-flash'; // Shockwave
+			$image_type_to_mime_type[14] = 'image/iff';                     // IFF
+		}
+		return (isset($image_type_to_mime_type[$imagetypeid]) ? $image_type_to_mime_type[$imagetypeid] : 'application/octet-stream');
+	}
+
+
+	function DateMac2Unix($macdate) {
+		// Macintosh timestamp: seconds since 00:00h January 1, 1904
+		// UNIX timestamp:      seconds since 00:00h January 1, 1970
+		return getid3_lib::CastAsInt($macdate - 2082844800);
+	}
+
+
+	function FixedPoint8_8($rawdata) {
+		return getid3_lib::BigEndian2Int(substr($rawdata, 0, 1)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 1, 1)) / pow(2, 8));
+	}
+
+
+	function FixedPoint16_16($rawdata) {
+		return getid3_lib::BigEndian2Int(substr($rawdata, 0, 2)) + (float) (getid3_lib::BigEndian2Int(substr($rawdata, 2, 2)) / pow(2, 16));
+	}
+
+
+	function FixedPoint2_30($rawdata) {
+		$binarystring = getid3_lib::BigEndian2Bin($rawdata);
+		return getid3_lib::Bin2Dec(substr($binarystring, 0, 2)) + (float) (getid3_lib::Bin2Dec(substr($binarystring, 2, 30)) / 1073741824);
+	}
+
+
+	function CreateDeepArray($ArrayPath, $Separator, $Value) {
+		// assigns $Value to a nested array path:
+		//   $foo = getid3_lib::CreateDeepArray('/path/to/my', '/', 'file.txt')
+		// is the same as:
+		//   $foo = array('path'=>array('to'=>'array('my'=>array('file.txt'))));
+		// or
+		//   $foo['path']['to']['my'] = 'file.txt';
+		while ($ArrayPath && ($ArrayPath{0} == $Separator)) {
+			$ArrayPath = substr($ArrayPath, 1);
+		}
+		if (($pos = strpos($ArrayPath, $Separator)) !== false) {
+			$ReturnedArray[substr($ArrayPath, 0, $pos)] = getid3_lib::CreateDeepArray(substr($ArrayPath, $pos + 1), $Separator, $Value);
+		} else {
+			$ReturnedArray[$ArrayPath] = $Value;
+		}
+		return $ReturnedArray;
+	}
+
+	function array_max($arraydata, $returnkey=false) {
+		$maxvalue = false;
+		$maxkey = false;
+		foreach ($arraydata as $key => $value) {
+			if (!is_array($value)) {
+				if ($value > $maxvalue) {
+					$maxvalue = $value;
+					$maxkey = $key;
+				}
+			}
+		}
+		return ($returnkey ? $maxkey : $maxvalue);
+	}
+
+	function array_min($arraydata, $returnkey=false) {
+		$minvalue = false;
+		$minkey = false;
+		foreach ($arraydata as $key => $value) {
+			if (!is_array($value)) {
+				if ($value > $minvalue) {
+					$minvalue = $value;
+					$minkey = $key;
+				}
+			}
+		}
+		return ($returnkey ? $minkey : $minvalue);
+	}
+
+
+	function md5_file($file) {
+
+		// md5_file() exists in PHP 4.2.0+.
+		if (function_exists('md5_file')) {
+			return md5_file($file);
+		}
+
+		if (GETID3_OS_ISWINDOWS) {
+
+			$RequiredFiles = array('cygwin1.dll', 'md5sum.exe');
+			foreach ($RequiredFiles as $required_file) {
+				if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
+					die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::md5_file() to function under Windows in PHP < v4.2.0');
+				}
+			}
+			$commandline = GETID3_HELPERAPPSDIR.'md5sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
+			if (ereg("^[\\]?([0-9a-f]{32})", strtolower(`$commandline`), $r)) {
+				return $r[1];
+			}
+
+		} else {
+
+			// The following works under UNIX only
+			$file = str_replace('`', '\\`', $file);
+			if (ereg("^([0-9a-f]{32})[ \t\n\r]", `md5sum "$file"`, $r)) {
+				return $r[1];
+			}
+
+		}
+		return false;
+	}
+
+
+	function sha1_file($file) {
+
+		// sha1_file() exists in PHP 4.3.0+.
+		if (function_exists('sha1_file')) {
+			return sha1_file($file);
+		}
+
+		$file = str_replace('`', '\\`', $file);
+
+		if (GETID3_OS_ISWINDOWS) {
+
+			$RequiredFiles = array('cygwin1.dll', 'sha1sum.exe');
+			foreach ($RequiredFiles as $required_file) {
+				if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
+					die(implode(' and ', $RequiredFiles).' are required in '.GETID3_HELPERAPPSDIR.' for getid3_lib::sha1_file() to function under Windows in PHP < v4.3.0');
+				}
+			}
+			$commandline = GETID3_HELPERAPPSDIR.'sha1sum.exe "'.str_replace('/', DIRECTORY_SEPARATOR, $file).'"';
+			if (ereg("^sha1=([0-9a-f]{40})", strtolower(`$commandline`), $r)) {
+				return $r[1];
+			}
+
+		} else {
+
+			$commandline = 'sha1sum '.escapeshellarg($file).'';
+			if (ereg("^([0-9a-f]{40})[ \t\n\r]", strtolower(`$commandline`), $r)) {
+				return $r[1];
+			}
+
+		}
+
+		return false;
+	}
+
+
+	// Allan Hansen <ahØartemis*dk>
+	// getid3_lib::md5_data() - returns md5sum for a file from startuing position to absolute end position
+	function hash_data($file, $offset, $end, $algorithm) {
+		if ($end >= pow(2, 31)) {
+			return false;
+		}
+
+		switch ($algorithm) {
+			case 'md5':
+				$hash_function = 'md5_file';
+				$unix_call     = 'md5sum';
+				$windows_call  = 'md5sum.exe';
+				$hash_length   = 32;
+				break;
+
+			case 'sha1':
+				$hash_function = 'sha1_file';
+				$unix_call     = 'sha1sum';
+				$windows_call  = 'sha1sum.exe';
+				$hash_length   = 40;
+				break;
+
+			default:
+				die('Invalid algorithm ('.$algorithm.') in getid3_lib::hash_data()');
+				break;
+		}
+		$size = $end - $offset;
+		while (true) {
+			if (GETID3_OS_ISWINDOWS) {
+
+				// It seems that sha1sum.exe for Windows only works on physical files, does not accept piped data
+				// Fall back to create-temp-file method:
+				if ($algorithm == 'sha1') {
+					break;
+				}
+
+				$RequiredFiles = array('cygwin1.dll', 'head.exe', 'tail.exe', $windows_call);
+				foreach ($RequiredFiles as $required_file) {
+					if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
+						// helper apps not available - fall back to old method
+						break;
+					}
+				}
+				$commandline  = GETID3_HELPERAPPSDIR.'head.exe -c '.$end.' "'.escapeshellarg(str_replace('/', DIRECTORY_SEPARATOR, $file)).'" | ';
+				$commandline .= GETID3_HELPERAPPSDIR.'tail.exe -c '.$size.' | ';
+				$commandline .= GETID3_HELPERAPPSDIR.$windows_call;
+
+			} else {
+
+				$commandline  = 'head -c'.$end.' '.escapeshellarg($file).' | ';
+				$commandline .= 'tail -c'.$size.' | ';
+				$commandline .= $unix_call;
+
+			}
+			if ((bool) ini_get('safe_mode')) {
+				$ThisFileInfo['warning'][] = 'PHP running in Safe Mode - backtick operator not available, using slower non-system-call '.$algorithm.' algorithm';
+				break;
+			}
+			return substr(`$commandline`, 0, $hash_length);
+		}
+
+		// try to create a temporary file in the system temp directory - invalid dirname should force to system temp dir
+		if (($data_filename = tempnam('*', 'getID3')) === false) {
+			// can't find anywhere to create a temp file, just die
+			return false;
+		}
+
+		// Init
+		$result = false;
+
+		// copy parts of file
+		if ($fp = @fopen($file, 'rb')) {
+
+			if ($fp_data = @fopen($data_filename, 'wb')) {
+
+				fseek($fp, $offset, SEEK_SET);
+				$byteslefttowrite = $end - $offset;
+				while (($byteslefttowrite > 0) && ($buffer = fread($fp, GETID3_FREAD_BUFFER_SIZE))) {
+					$byteswritten = fwrite($fp_data, $buffer, $byteslefttowrite);
+					$byteslefttowrite -= $byteswritten;
+				}
+				fclose($fp_data);
+				$result = getid3_lib::$hash_function($data_filename);
+
+			}
+			fclose($fp);
+		}
+		unlink($data_filename);
+		return $result;
+	}
+
+
+	function iconv_fallback_int_utf8($charval) {
+		if ($charval < 128) {
+			// 0bbbbbbb
+			$newcharstring = chr($charval);
+		} elseif ($charval < 2048) {
+			// 110bbbbb 10bbbbbb
+			$newcharstring  = chr(($charval >> 6) | 0xC0);
+			$newcharstring .= chr(($charval & 0x3F) | 0x80);
+		} elseif ($charval < 65536) {
+			// 1110bbbb 10bbbbbb 10bbbbbb
+			$newcharstring  = chr(($charval >> 12) | 0xE0);
+			$newcharstring .= chr(($charval >>  6) | 0xC0);
+			$newcharstring .= chr(($charval & 0x3F) | 0x80);
+		} else {
+			// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
+			$newcharstring  = chr(($charval >> 18) | 0xF0);
+			$newcharstring .= chr(($charval >> 12) | 0xC0);
+			$newcharstring .= chr(($charval >>  6) | 0xC0);
+			$newcharstring .= chr(($charval & 0x3F) | 0x80);
+		}
+		return $newcharstring;
+	}
+
+	// ISO-8859-1 => UTF-8
+	function iconv_fallback_iso88591_utf8($string, $bom=false) {
+		if (function_exists('utf8_encode')) {
+			return utf8_encode($string);
+		}
+		// utf8_encode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
+		$newcharstring = '';
+		if ($bom) {
+			$newcharstring .= "\xEF\xBB\xBF";
+		}
+		for ($i = 0; $i < strlen($string); $i++) {
+			$charval = ord($string{$i});
+			$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
+		}
+		return $newcharstring;
+	}
+
+	// ISO-8859-1 => UTF-16BE
+	function iconv_fallback_iso88591_utf16be($string, $bom=false) {
+		$newcharstring = '';
+		if ($bom) {
+			$newcharstring .= "\xFE\xFF";
+		}
+		for ($i = 0; $i < strlen($string); $i++) {
+			$newcharstring .= "\x00".$string{$i};
+		}
+		return $newcharstring;
+	}
+
+	// ISO-8859-1 => UTF-16LE
+	function iconv_fallback_iso88591_utf16le($string, $bom=false) {
+		$newcharstring = '';
+		if ($bom) {
+			$newcharstring .= "\xFF\xFE";
+		}
+		for ($i = 0; $i < strlen($string); $i++) {
+			$newcharstring .= $string{$i}."\x00";
+		}
+		return $newcharstring;
+	}
+
+	// ISO-8859-1 => UTF-16LE (BOM)
+	function iconv_fallback_iso88591_utf16($string) {
+		return getid3_lib::iconv_fallback_iso88591_utf16le($string, true);
+	}
+
+	// UTF-8 => ISO-8859-1
+	function iconv_fallback_utf8_iso88591($string) {
+		if (function_exists('utf8_decode')) {
+			return utf8_decode($string);
+		}
+		// utf8_decode() unavailable, use getID3()'s iconv_fallback() conversions (possibly PHP is compiled without XML support)
+		$newcharstring = '';
+		$offset = 0;
+		$stringlength = strlen($string);
+		while ($offset < $stringlength) {
+			if ((ord($string{$offset}) | 0x07) == 0xF7) {
+				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
+				           ((ord($string{($offset + 1)}) & 0x3F) << 12) &
+				           ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
+				            (ord($string{($offset + 3)}) & 0x3F);
+				$offset += 4;
+			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+				// 1110bbbb 10bbbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
+				           ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
+				            (ord($string{($offset + 2)}) & 0x3F);
+				$offset += 3;
+			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+				// 110bbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
+				            (ord($string{($offset + 1)}) & 0x3F);
+				$offset += 2;
+			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+				// 0bbbbbbb
+				$charval = ord($string{$offset});
+				$offset += 1;
+			} else {
+				// error? throw some kind of warning here?
+				$charval = false;
+				$offset += 1;
+			}
+			if ($charval !== false) {
+				$newcharstring .= (($charval < 256) ? chr($charval) : '?');
+			}
+		}
+		return $newcharstring;
+	}
+
+	// UTF-8 => UTF-16BE
+	function iconv_fallback_utf8_utf16be($string, $bom=false) {
+		$newcharstring = '';
+		if ($bom) {
+			$newcharstring .= "\xFE\xFF";
+		}
+		$offset = 0;
+		$stringlength = strlen($string);
+		while ($offset < $stringlength) {
+			if ((ord($string{$offset}) | 0x07) == 0xF7) {
+				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
+				           ((ord($string{($offset + 1)}) & 0x3F) << 12) &
+				           ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
+				            (ord($string{($offset + 3)}) & 0x3F);
+				$offset += 4;
+			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+				// 1110bbbb 10bbbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
+				           ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
+				            (ord($string{($offset + 2)}) & 0x3F);
+				$offset += 3;
+			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+				// 110bbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
+				            (ord($string{($offset + 1)}) & 0x3F);
+				$offset += 2;
+			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+				// 0bbbbbbb
+				$charval = ord($string{$offset});
+				$offset += 1;
+			} else {
+				// error? throw some kind of warning here?
+				$charval = false;
+				$offset += 1;
+			}
+			if ($charval !== false) {
+				$newcharstring .= (($charval < 65536) ? getid3_lib::BigEndian2String($charval, 2) : "\x00".'?');
+			}
+		}
+		return $newcharstring;
+	}
+
+	// UTF-8 => UTF-16LE
+	function iconv_fallback_utf8_utf16le($string, $bom=false) {
+		$newcharstring = '';
+		if ($bom) {
+			$newcharstring .= "\xFF\xFE";
+		}
+		$offset = 0;
+		$stringlength = strlen($string);
+		while ($offset < $stringlength) {
+			if ((ord($string{$offset}) | 0x07) == 0xF7) {
+				// 11110bbb 10bbbbbb 10bbbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x07) << 18) &
+				           ((ord($string{($offset + 1)}) & 0x3F) << 12) &
+				           ((ord($string{($offset + 2)}) & 0x3F) <<  6) &
+				            (ord($string{($offset + 3)}) & 0x3F);
+				$offset += 4;
+			} elseif ((ord($string{$offset}) | 0x0F) == 0xEF) {
+				// 1110bbbb 10bbbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x0F) << 12) &
+				           ((ord($string{($offset + 1)}) & 0x3F) <<  6) &
+				            (ord($string{($offset + 2)}) & 0x3F);
+				$offset += 3;
+			} elseif ((ord($string{$offset}) | 0x1F) == 0xDF) {
+				// 110bbbbb 10bbbbbb
+				$charval = ((ord($string{($offset + 0)}) & 0x1F) <<  6) &
+				            (ord($string{($offset + 1)}) & 0x3F);
+				$offset += 2;
+			} elseif ((ord($string{$offset}) | 0x7F) == 0x7F) {
+				// 0bbbbbbb
+				$charval = ord($string{$offset});
+				$offset += 1;
+			} else {
+				// error? maybe throw some warning here?
+				$charval = false;
+				$offset += 1;
+			}
+			if ($charval !== false) {
+				$newcharstring .= (($charval < 65536) ? getid3_lib::LittleEndian2String($charval, 2) : '?'."\x00");
+			}
+		}
+		return $newcharstring;
+	}
+
+	// UTF-8 => UTF-16LE (BOM)
+	function iconv_fallback_utf8_utf16($string) {
+		return getid3_lib::iconv_fallback_utf8_utf16le($string, true);
+	}
+
+	// UTF-16BE => UTF-8
+	function iconv_fallback_utf16be_utf8($string) {
+		if (substr($string, 0, 2) == "\xFE\xFF") {
+			// strip BOM
+			$string = substr($string, 2);
+		}
+		$newcharstring = '';
+		for ($i = 0; $i < strlen($string); $i += 2) {
+			$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
+			$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
+		}
+		return $newcharstring;
+	}
+
+	// UTF-16LE => UTF-8
+	function iconv_fallback_utf16le_utf8($string) {
+		if (substr($string, 0, 2) == "\xFF\xFE") {
+			// strip BOM
+			$string = substr($string, 2);
+		}
+		$newcharstring = '';
+		for ($i = 0; $i < strlen($string); $i += 2) {
+			$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
+			$newcharstring .= getid3_lib::iconv_fallback_int_utf8($charval);
+		}
+		return $newcharstring;
+	}
+
+	// UTF-16BE => ISO-8859-1
+	function iconv_fallback_utf16be_iso88591($string) {
+		if (substr($string, 0, 2) == "\xFE\xFF") {
+			// strip BOM
+			$string = substr($string, 2);
+		}
+		$newcharstring = '';
+		for ($i = 0; $i < strlen($string); $i += 2) {
+			$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
+			$newcharstring .= (($charval < 256) ? chr($charval) : '?');
+		}
+		return $newcharstring;
+	}
+
+	// UTF-16LE => ISO-8859-1
+	function iconv_fallback_utf16le_iso88591($string) {
+		if (substr($string, 0, 2) == "\xFF\xFE") {
+			// strip BOM
+			$string = substr($string, 2);
+		}
+		$newcharstring = '';
+		for ($i = 0; $i < strlen($string); $i += 2) {
+			$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
+			$newcharstring .= (($charval < 256) ? chr($charval) : '?');
+		}
+		return $newcharstring;
+	}
+
+	// UTF-16 (BOM) => ISO-8859-1
+	function iconv_fallback_utf16_iso88591($string) {
+		$bom = substr($string, 0, 2);
+		if ($bom == "\xFE\xFF") {
+			return getid3_lib::iconv_fallback_utf16be_iso88591(substr($string, 2));
+		} elseif ($bom == "\xFF\xFE") {
+			return getid3_lib::iconv_fallback_utf16le_iso88591(substr($string, 2));
+		}
+		return $string;
+	}
+
+	// UTF-16 (BOM) => UTF-8
+	function iconv_fallback_utf16_utf8($string) {
+		$bom = substr($string, 0, 2);
+		if ($bom == "\xFE\xFF") {
+			return getid3_lib::iconv_fallback_utf16be_utf8(substr($string, 2));
+		} elseif ($bom == "\xFF\xFE") {
+			return getid3_lib::iconv_fallback_utf16le_utf8(substr($string, 2));
+		}
+		return $string;
+	}
+
+	function iconv_fallback($in_charset, $out_charset, $string) {
+
+		if ($in_charset == $out_charset) {
+			return $string;
+		}
+
+		// iconv() availble
+		if (function_exists('iconv')) {
+
+		    if ($converted_string = @iconv($in_charset, $out_charset.'//TRANSLIT', $string)) {
+    			switch ($out_charset) {
+    				case 'ISO-8859-1':
+    					$converted_string = rtrim($converted_string, "\x00");
+    					break;
+    			}
+    			return $converted_string;
+    		}
+
+    		// iconv() may sometimes fail with "illegal character in input string" error message
+    		// and return an empty string, but returning the unconverted string is more useful
+    		return $string;
+    	}
+
+
+        // iconv() not available
+		static $ConversionFunctionList = array();
+		if (empty($ConversionFunctionList)) {
+			$ConversionFunctionList['ISO-8859-1']['UTF-8']    = 'iconv_fallback_iso88591_utf8';
+			$ConversionFunctionList['ISO-8859-1']['UTF-16']   = 'iconv_fallback_iso88591_utf16';
+			$ConversionFunctionList['ISO-8859-1']['UTF-16BE'] = 'iconv_fallback_iso88591_utf16be';
+			$ConversionFunctionList['ISO-8859-1']['UTF-16LE'] = 'iconv_fallback_iso88591_utf16le';
+			$ConversionFunctionList['UTF-8']['ISO-8859-1']    = 'iconv_fallback_utf8_iso88591';
+			$ConversionFunctionList['UTF-8']['UTF-16']        = 'iconv_fallback_utf8_utf16';
+			$ConversionFunctionList['UTF-8']['UTF-16BE']      = 'iconv_fallback_utf8_utf16be';
+			$ConversionFunctionList['UTF-8']['UTF-16LE']      = 'iconv_fallback_utf8_utf16le';
+			$ConversionFunctionList['UTF-16']['ISO-8859-1']   = 'iconv_fallback_utf16_iso88591';
+			$ConversionFunctionList['UTF-16']['UTF-8']        = 'iconv_fallback_utf16_utf8';
+			$ConversionFunctionList['UTF-16LE']['ISO-8859-1'] = 'iconv_fallback_utf16le_iso88591';
+			$ConversionFunctionList['UTF-16LE']['UTF-8']      = 'iconv_fallback_utf16le_utf8';
+			$ConversionFunctionList['UTF-16BE']['ISO-8859-1'] = 'iconv_fallback_utf16be_iso88591';
+			$ConversionFunctionList['UTF-16BE']['UTF-8']      = 'iconv_fallback_utf16be_utf8';
+		}
+		if (isset($ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)])) {
+			$ConversionFunction = $ConversionFunctionList[strtoupper($in_charset)][strtoupper($out_charset)];
+			return getid3_lib::$ConversionFunction($string);
+		}
+		die('PHP does not have iconv() support - cannot convert from '.$in_charset.' to '.$out_charset);
+	}
+
+
+	function MultiByteCharString2HTML($string, $charset='ISO-8859-1') {
+		$HTMLstring = '';
+
+		switch ($charset) {
+			case 'ISO-8859-1':
+			case 'ISO8859-1':
+			case 'ISO-8859-15':
+			case 'ISO8859-15':
+			case 'cp866':
+			case 'ibm866':
+			case '866':
+			case 'cp1251':
+			case 'Windows-1251':
+			case 'win-1251':
+			case '1251':
+			case 'cp1252':
+			case 'Windows-1252':
+			case '1252':
+			case 'KOI8-R':
+			case 'koi8-ru':
+			case 'koi8r':
+			case 'BIG5':
+			case '950':
+			case 'GB2312':
+			case '936':
+			case 'BIG5-HKSCS':
+			case 'Shift_JIS':
+			case 'SJIS':
+			case '932':
+			case 'EUC-JP':
+			case 'EUCJP':
+				$HTMLstring = htmlentities($string, ENT_COMPAT, $charset);
+				break;
+
+			case 'UTF-8':
+				$strlen = strlen($string);
+				for ($i = 0; $i < $strlen; $i++) {
+					$char_ord_val = ord($string{$i});
+					$charval = 0;
+					if ($char_ord_val < 0x80) {
+						$charval = $char_ord_val;
+					} elseif ((($char_ord_val & 0xF0) >> 4) == 0x0F  &&  $i+3 < $strlen) {
+						$charval  = (($char_ord_val & 0x07) << 18);
+						$charval += ((ord($string{++$i}) & 0x3F) << 12);
+						$charval += ((ord($string{++$i}) & 0x3F) << 6);
+						$charval +=  (ord($string{++$i}) & 0x3F);
+					} elseif ((($char_ord_val & 0xE0) >> 5) == 0x07  &&  $i+2 < $strlen) {
+						$charval  = (($char_ord_val & 0x0F) << 12);
+						$charval += ((ord($string{++$i}) & 0x3F) << 6);
+						$charval +=  (ord($string{++$i}) & 0x3F);
+					} elseif ((($char_ord_val & 0xC0) >> 6) == 0x03  &&  $i+1 < $strlen) {
+						$charval  = (($char_ord_val & 0x1F) << 6);
+						$charval += (ord($string{++$i}) & 0x3F);
+					}
+					if (($charval >= 32) && ($charval <= 127)) {
+						$HTMLstring .= htmlentities(chr($charval));
+					} else {
+						$HTMLstring .= '&#'.$charval.';';
+					}
+				}
+				break;
+
+			case 'UTF-16LE':
+				for ($i = 0; $i < strlen($string); $i += 2) {
+					$charval = getid3_lib::LittleEndian2Int(substr($string, $i, 2));
+					if (($charval >= 32) && ($charval <= 127)) {
+						$HTMLstring .= chr($charval);
+					} else {
+						$HTMLstring .= '&#'.$charval.';';
+					}
+				}
+				break;
+
+			case 'UTF-16BE':
+				for ($i = 0; $i < strlen($string); $i += 2) {
+					$charval = getid3_lib::BigEndian2Int(substr($string, $i, 2));
+					if (($charval >= 32) && ($charval <= 127)) {
+						$HTMLstring .= chr($charval);
+					} else {
+						$HTMLstring .= '&#'.$charval.';';
+					}
+				}
+				break;
+
+			default:
+				$HTMLstring = 'ERROR: Character set "'.$charset.'" not supported in MultiByteCharString2HTML()';
+				break;
+		}
+		return $HTMLstring;
+	}
+
+
+
+	function RGADnameLookup($namecode) {
+		static $RGADname = array();
+		if (empty($RGADname)) {
+			$RGADname[0] = 'not set';
+			$RGADname[1] = 'Track Gain Adjustment';
+			$RGADname[2] = 'Album Gain Adjustment';
+		}
+
+		return (isset($RGADname[$namecode]) ? $RGADname[$namecode] : '');
+	}
+
+
+	function RGADoriginatorLookup($originatorcode) {
+		static $RGADoriginator = array();
+		if (empty($RGADoriginator)) {
+			$RGADoriginator[0] = 'unspecified';
+			$RGADoriginator[1] = 'pre-set by artist/producer/mastering engineer';
+			$RGADoriginator[2] = 'set by user';
+			$RGADoriginator[3] = 'determined automatically';
+		}
+
+		return (isset($RGADoriginator[$originatorcode]) ? $RGADoriginator[$originatorcode] : '');
+	}
+
+
+	function RGADadjustmentLookup($rawadjustment, $signbit) {
+		$adjustment = $rawadjustment / 10;
+		if ($signbit == 1) {
+			$adjustment *= -1;
+		}
+		return (float) $adjustment;
+	}
+
+
+	function RGADgainString($namecode, $originatorcode, $replaygain) {
+		if ($replaygain < 0) {
+			$signbit = '1';
+		} else {
+			$signbit = '0';
+		}
+		$storedreplaygain = intval(round($replaygain * 10));
+		$gainstring  = str_pad(decbin($namecode), 3, '0', STR_PAD_LEFT);
+		$gainstring .= str_pad(decbin($originatorcode), 3, '0', STR_PAD_LEFT);
+		$gainstring .= $signbit;
+		$gainstring .= str_pad(decbin($storedreplaygain), 9, '0', STR_PAD_LEFT);
+
+		return $gainstring;
+	}
+
+	function RGADamplitude2dB($amplitude) {
+		return 20 * log10($amplitude);
+	}
+
+
+	function GetDataImageSize($imgData, &$imageinfo) {
+		$GetDataImageSize = false;
+		if ($tempfilename = tempnam('*', 'getID3')) {
+			if ($tmp = @fopen($tempfilename, 'wb')) {
+				fwrite($tmp, $imgData);
+				fclose($tmp);
+				$GetDataImageSize = @GetImageSize($tempfilename, $imageinfo);
+			}
+			unlink($tempfilename);
+		}
+		return $GetDataImageSize;
+	}
+
+	function ImageTypesLookup($imagetypeid) {
+		static $ImageTypesLookup = array();
+		if (empty($ImageTypesLookup)) {
+			$ImageTypesLookup[1]  = 'gif';
+			$ImageTypesLookup[2]  = 'jpeg';
+			$ImageTypesLookup[3]  = 'png';
+			$ImageTypesLookup[4]  = 'swf';
+			$ImageTypesLookup[5]  = 'psd';
+			$ImageTypesLookup[6]  = 'bmp';
+			$ImageTypesLookup[7]  = 'tiff (little-endian)';
+			$ImageTypesLookup[8]  = 'tiff (big-endian)';
+			$ImageTypesLookup[9]  = 'jpc';
+			$ImageTypesLookup[10] = 'jp2';
+			$ImageTypesLookup[11] = 'jpx';
+			$ImageTypesLookup[12] = 'jb2';
+			$ImageTypesLookup[13] = 'swc';
+			$ImageTypesLookup[14] = 'iff';
+		}
+		return (isset($ImageTypesLookup[$imagetypeid]) ? $ImageTypesLookup[$imagetypeid] : '');
+	}
+
+	function CopyTagsToComments(&$ThisFileInfo) {
+
+		// Copy all entries from ['tags'] into common ['comments']
+		if (!empty($ThisFileInfo['tags'])) {
+			foreach ($ThisFileInfo['tags'] as $tagtype => $tagarray) {
+				foreach ($tagarray as $tagname => $tagdata) {
+					foreach ($tagdata as $key => $value) {
+						if (!empty($value)) {
+							if (empty($ThisFileInfo['comments'][$tagname])) {
+
+								// fall through and append value
+
+							} elseif ($tagtype == 'id3v1') {
+
+								$newvaluelength = strlen(trim($value));
+								foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
+									$oldvaluelength = strlen(trim($existingvalue));
+									if (($newvaluelength <= $oldvaluelength) && (substr($existingvalue, 0, $newvaluelength) == trim($value))) {
+										// new value is identical but shorter-than (or equal-length to) one already in comments - skip
+										break 2;
+									}
+								}
+
+							} else {
+
+								$newvaluelength = strlen(trim($value));
+								foreach ($ThisFileInfo['comments'][$tagname] as $existingkey => $existingvalue) {
+									$oldvaluelength = strlen(trim($existingvalue));
+									if (($newvaluelength > $oldvaluelength) && (substr(trim($value), 0, strlen($existingvalue)) == $existingvalue)) {
+										$ThisFileInfo['comments'][$tagname][$existingkey] = trim($value);
+										break 2;
+									}
+								}
+
+							}
+							if (empty($ThisFileInfo['comments'][$tagname]) || !in_array(trim($value), $ThisFileInfo['comments'][$tagname])) {
+								$ThisFileInfo['comments'][$tagname][] = trim($value);
+							}
+						}
+					}
+				}
+			}
+
+			// Copy to ['comments_html']
+    		foreach ($ThisFileInfo['comments'] as $field => $values) {
+    		    foreach ($values as $index => $value) {
+    		        $ThisFileInfo['comments_html'][$field][$index] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $ThisFileInfo['encoding']));
+    		    }
+            }
+		}
+	}
+
+
+	function EmbeddedLookup($key, $begin, $end, $file, $name) {
+
+		// Cached
+		static $cache;
+		if (isset($cache[$file][$name])) {
+			return @$cache[$file][$name][$key];
+		}
+
+		// Init
+		$keylength  = strlen($key);
+		$line_count = $end - $begin - 7;
+
+		// Open php file
+		$fp = fopen($file, 'r');
+
+		// Discard $begin lines
+		for ($i = 0; $i < ($begin + 3); $i++) {
+			fgets($fp, 1024);
+		}
+
+		// Loop thru line
+		while (0 < $line_count--) {
+
+			// Read line
+			$line = ltrim(fgets($fp, 1024), "\t ");
+
+			// METHOD A: only cache the matching key - less memory but slower on next lookup of not-previously-looked-up key
+			//$keycheck = substr($line, 0, $keylength);
+			//if ($key == $keycheck)  {
+			//	$cache[$file][$name][$keycheck] = substr($line, $keylength + 1);
+			//	break;
+			//}
+
+			// METHOD B: cache all keys in this lookup - more memory but faster on next lookup of not-previously-looked-up key
+			//$cache[$file][$name][substr($line, 0, $keylength)] = trim(substr($line, $keylength + 1));
+			@list($ThisKey, $ThisValue) = explode("\t", $line, 2);
+			$cache[$file][$name][$ThisKey] = trim($ThisValue);
+		}
+
+		// Close and return
+		fclose($fp);
+		return @$cache[$file][$name][$key];
+	}
+
+	function IncludeDependency($filename, $sourcefile, $DieOnFailure=false) {
+		global $GETID3_ERRORARRAY;
+
+		if (file_exists($filename)) {
+			if (@include_once($filename)) {
+				return true;
+			} else {
+				$diemessage = basename($sourcefile).' depends on '.$filename.', which has errors';
+			}
+		} else {
+			$diemessage = basename($sourcefile).' depends on '.$filename.', which is missing';
+		}
+		if ($DieOnFailure) {
+			die($diemessage);
+		} else {
+			$GETID3_ERRORARRAY[] = $diemessage;
+		}
+		return false;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/getid3.php b/apps/media/getID3/getid3/getid3.php
new file mode 100644
index 0000000000000000000000000000000000000000..f9dcf706d0a6f1c0d8b68aeef0bb2445287082ef
--- /dev/null
+++ b/apps/media/getID3/getid3/getid3.php
@@ -0,0 +1,1397 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// Please see readme.txt for more information                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+// Defines
+define('GETID3_VERSION', '1.7.9-20090308');
+define('GETID3_FREAD_BUFFER_SIZE', 16384); // read buffer size in bytes
+
+
+
+class getID3
+{
+	// public: Settings
+	var $encoding        = 'ISO-8859-1';   // CASE SENSITIVE! - i.e. (must be supported by iconv())
+	                                       // Examples:  ISO-8859-1  UTF-8  UTF-16  UTF-16BE
+
+	var $encoding_id3v1  = 'ISO-8859-1';   // Should always be 'ISO-8859-1', but some tags may be written in other encodings such as 'EUC-CN'
+
+	var $tempdir         = '*';            // default '*' should use system temp dir
+
+	// public: Optional tag checks - disable for speed.
+	var $option_tag_id3v1         = true;  // Read and process ID3v1 tags
+	var $option_tag_id3v2         = true;  // Read and process ID3v2 tags
+	var $option_tag_lyrics3       = true;  // Read and process Lyrics3 tags
+	var $option_tag_apetag        = true;  // Read and process APE tags
+	var $option_tags_process      = true;  // Copy tags to root key 'tags' and encode to $this->encoding
+	var $option_tags_html         = true;  // Copy tags to root key 'tags_html' properly translated from various encodings to HTML entities
+
+	// public: Optional tag/comment calucations
+	var $option_extra_info        = true;  // Calculate additional info such as bitrate, channelmode etc
+
+	// public: Optional calculations
+	var $option_md5_data          = false; // Get MD5 sum of data part - slow
+	var $option_md5_data_source   = false; // Use MD5 of source file if availble - only FLAC and OptimFROG
+	var $option_sha1_data         = false; // Get SHA1 sum of data part - slow
+	var $option_max_2gb_check     = true;  // Check whether file is larger than 2 Gb and thus not supported by PHP
+
+	// private
+	var $filename;
+
+
+	// public: constructor
+	function getID3()
+	{
+
+		$this->startup_error   = '';
+		$this->startup_warning = '';
+
+		// Check for PHP version >= 4.2.0
+		if (phpversion() < '4.2.0') {
+		    $this->startup_error .= 'getID3() requires PHP v4.2.0 or higher - you are running v'.phpversion();
+		}
+
+		// Check memory
+		$memory_limit = ini_get('memory_limit');
+		if (eregi('([0-9]+)M', $memory_limit, $matches)) {
+			// could be stored as "16M" rather than 16777216 for example
+			$memory_limit = $matches[1] * 1048576;
+		}
+		if ($memory_limit <= 0) {
+			// memory limits probably disabled
+		} elseif ($memory_limit <= 3145728) {
+	    	$this->startup_error .= 'PHP has less than 3MB available memory and will very likely run out. Increase memory_limit in php.ini';
+		} elseif ($memory_limit <= 12582912) {
+	    	$this->startup_warning .= 'PHP has less than 12MB available memory and might run out if all modules are loaded. Increase memory_limit in php.ini';
+		}
+
+		// Check safe_mode off
+		if ((bool) ini_get('safe_mode')) {
+		    $this->warning('WARNING: Safe mode is on, shorten support disabled, md5data/sha1data for ogg vorbis disabled, ogg vorbos/flac tag writing disabled.');
+		}
+
+
+		// define a constant rather than looking up every time it is needed
+		if (!defined('GETID3_OS_ISWINDOWS')) {
+			if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') {
+				define('GETID3_OS_ISWINDOWS', true);
+			} else {
+				define('GETID3_OS_ISWINDOWS', false);
+			}
+		}
+
+		// Get base path of getID3() - ONCE
+		if (!defined('GETID3_INCLUDEPATH')) {
+			foreach (get_included_files() as $key => $val) {
+				if (basename($val) == 'getid3.php') {
+					define('GETID3_INCLUDEPATH', dirname($val).DIRECTORY_SEPARATOR);
+					break;
+				}
+			}
+		}
+
+		// Load support library
+		if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
+			$this->startup_error .= 'getid3.lib.php is missing or corrupt';
+		}
+
+
+		// Needed for Windows only:
+		// Define locations of helper applications for Shorten, VorbisComment, MetaFLAC
+		//   as well as other helper functions such as head, tail, md5sum, etc
+		// IMPORTANT: This path cannot have spaces in it. If neccesary, use the 8dot3 equivalent
+		//   ie for "C:/Program Files/Apache/" put "C:/PROGRA~1/APACHE/"
+		// IMPORTANT: This path must include the trailing slash
+		if (GETID3_OS_ISWINDOWS && !defined('GETID3_HELPERAPPSDIR')) {
+
+			$helperappsdir = GETID3_INCLUDEPATH.'..'.DIRECTORY_SEPARATOR.'helperapps'; // must not have any space in this path
+
+			if (!is_dir($helperappsdir)) {
+				$this->startup_error .= '"'.$helperappsdir.'" cannot be defined as GETID3_HELPERAPPSDIR because it does not exist';
+			} elseif (strpos(realpath($helperappsdir), ' ') !== false) {
+				$DirPieces = explode(DIRECTORY_SEPARATOR, realpath($helperappsdir));
+				foreach ($DirPieces as $key => $value) {
+					if ((strpos($value, '.') !== false) && (strpos($value, ' ') === false)) {
+						if (strpos($value, '.') > 8) {
+							$value = substr($value, 0, 6).'~1';
+						}
+					} elseif ((strpos($value, ' ') !== false) || strlen($value) > 8) {
+						$value = substr($value, 0, 6).'~1';
+					}
+					$DirPieces[$key] = strtoupper($value);
+				}
+				$this->startup_error .= 'GETID3_HELPERAPPSDIR must not have any spaces in it - use 8dot3 naming convention if neccesary (on this server that would be something like "'.implode(DIRECTORY_SEPARATOR, $DirPieces).'" - NOTE: this may or may not be the actual 8.3 equivalent of "'.$helperappsdir.'", please double-check). You can run "dir /x" from the commandline to see the correct 8.3-style names.';
+			}
+			define('GETID3_HELPERAPPSDIR', realpath($helperappsdir).DIRECTORY_SEPARATOR);
+		}
+
+	}
+
+
+	// public: setOption
+	function setOption($optArray) {
+		if (!is_array($optArray) || empty($optArray)) {
+			return false;
+		}
+		foreach ($optArray as $opt => $val) {
+			//if (isset($this, $opt) === false) {
+			if (isset($this->$opt) === false) {
+				continue;
+			}
+			$this->$opt = $val;
+		}
+		return true;
+	}
+
+
+	// public: analyze file - replaces GetAllFileInfo() and GetTagOnly()
+	function analyze($filename) {
+
+		if (!empty($this->startup_error)) {
+			return $this->error($this->startup_error);
+		}
+		if (!empty($this->startup_warning)) {
+			$this->warning($this->startup_warning);
+		}
+
+		// init result array and set parameters
+		$this->info = array();
+		$this->info['GETID3_VERSION'] = GETID3_VERSION;
+
+		// Check encoding/iconv support
+		if (!function_exists('iconv') && !in_array($this->encoding, array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
+			$errormessage = 'iconv() support is needed for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
+			if (GETID3_OS_ISWINDOWS) {
+				$errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
+			} else {
+				$errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
+			}
+	    	return $this->error($errormessage);
+		}
+
+		// Disable magic_quotes_runtime, if neccesary
+		$old_magic_quotes_runtime = get_magic_quotes_runtime(); // store current setting of magic_quotes_runtime
+		if ($old_magic_quotes_runtime) {
+			set_magic_quotes_runtime(0);                        // turn off magic_quotes_runtime
+			if (get_magic_quotes_runtime()) {
+				return $this->error('Could not disable magic_quotes_runtime - getID3() cannot work properly with this setting enabled');
+			}
+		}
+
+		// remote files not supported
+		if (preg_match('/^(ht|f)tp:\/\//', $filename)) {
+			return $this->error('Remote files are not supported in this version of getID3() - please copy the file locally first');
+		}
+
+		$filename = str_replace('/', DIRECTORY_SEPARATOR, $filename);
+		$filename = preg_replace('#'.preg_quote(DIRECTORY_SEPARATOR).'{2,}#', DIRECTORY_SEPARATOR, $filename);
+
+		// open local file
+		if (file_exists($filename) && ($fp = @fopen($filename, 'rb'))) {
+			// great
+		} else {
+			return $this->error('Could not open file "'.$filename.'"');
+		}
+
+		// set parameters
+		$this->info['filesize'] = filesize($filename);
+
+		// option_max_2gb_check
+		if ($this->option_max_2gb_check) {
+			// PHP doesn't support integers larger than 31-bit (~2GB)
+			// filesize() simply returns (filesize % (pow(2, 32)), no matter the actual filesize
+			// ftell() returns 0 if seeking to the end is beyond the range of unsigned integer
+			fseek($fp, 0, SEEK_END);
+			if ((($this->info['filesize'] != 0) && (ftell($fp) == 0)) ||
+				($this->info['filesize'] < 0) ||
+				(ftell($fp) < 0)) {
+					$real_filesize = false;
+					if (GETID3_OS_ISWINDOWS) {
+						$commandline = 'dir /-C "'.str_replace('/', DIRECTORY_SEPARATOR, $filename).'"';
+						$dir_output = `$commandline`;
+						if (eregi('1 File\(s\)[ ]+([0-9]+) bytes', $dir_output, $matches)) {
+							$real_filesize = (float) $matches[1];
+						}
+					} else {
+						$commandline = 'ls -o -g -G --time-style=long-iso '.escapeshellarg($filename);
+						$dir_output = `$commandline`;
+						if (eregi('([0-9]+) ([0-9]{4}-[0-9]{2}\-[0-9]{2} [0-9]{2}:[0-9]{2}) '.preg_quote($filename).'$', $dir_output, $matches)) {
+							$real_filesize = (float) $matches[1];
+						}
+					}
+					if ($real_filesize === false) {
+						unset($this->info['filesize']);
+						fclose($fp);
+						return $this->error('File is most likely larger than 2GB and is not supported by PHP');
+					} elseif ($real_filesize < pow(2, 31)) {
+						unset($this->info['filesize']);
+						fclose($fp);
+						return $this->error('PHP seems to think the file is larger than 2GB, but filesystem reports it as '.number_format($real_filesize, 3).'GB, please report to info@getid3.org');
+					}
+					$this->info['filesize'] = $real_filesize;
+					$this->error('File is larger than 2GB (filesystem reports it as '.number_format($real_filesize, 3).'GB) and is not properly supported by PHP.');
+			}
+		}
+
+		// set more parameters
+		$this->info['avdataoffset']        = 0;
+		$this->info['avdataend']           = $this->info['filesize'];
+		$this->info['fileformat']          = '';                // filled in later
+		$this->info['audio']['dataformat'] = '';                // filled in later, unset if not used
+		$this->info['video']['dataformat'] = '';                // filled in later, unset if not used
+		$this->info['tags']                = array();           // filled in later, unset if not used
+		$this->info['error']               = array();           // filled in later, unset if not used
+		$this->info['warning']             = array();           // filled in later, unset if not used
+		$this->info['comments']            = array();           // filled in later, unset if not used
+		$this->info['encoding']            = $this->encoding;   // required by id3v2 and iso modules - can be unset at the end if desired
+
+		// set redundant parameters - might be needed in some include file
+		$this->info['filename']            = basename($filename);
+		$this->info['filepath']            = str_replace('\\', '/', realpath(dirname($filename)));
+		$this->info['filenamepath']        = $this->info['filepath'].'/'.$this->info['filename'];
+
+
+		// handle ID3v2 tag - done first - already at beginning of file
+		// ID3v2 detection (even if not parsing) is always done otherwise fileformat is much harder to detect
+		if ($this->option_tag_id3v2) {
+
+			$GETID3_ERRORARRAY = &$this->info['warning'];
+			if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, false)) {
+				$tag = new getid3_id3v2($fp, $this->info);
+				unset($tag);
+			}
+
+		} else {
+
+			fseek($fp, 0, SEEK_SET);
+			$header = fread($fp, 10);
+			if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
+				$this->info['id3v2']['header']           = true;
+				$this->info['id3v2']['majorversion']     = ord($header{3});
+				$this->info['id3v2']['minorversion']     = ord($header{4});
+				$this->info['id3v2']['headerlength']     = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
+
+				$this->info['id3v2']['tag_offset_start'] = 0;
+				$this->info['id3v2']['tag_offset_end']   = $this->info['id3v2']['tag_offset_start'] + $this->info['id3v2']['headerlength'];
+				$this->info['avdataoffset']              = $this->info['id3v2']['tag_offset_end'];
+			}
+
+		}
+
+
+		// handle ID3v1 tag
+		if ($this->option_tag_id3v1) {
+			if (!@include_once(GETID3_INCLUDEPATH.'module.tag.id3v1.php')) {
+				return $this->error('module.tag.id3v1.php is missing - you may disable option_tag_id3v1.');
+			}
+			$tag = new getid3_id3v1($fp, $this->info);
+			unset($tag);
+		}
+
+		// handle APE tag
+		if ($this->option_tag_apetag) {
+			if (!@include_once(GETID3_INCLUDEPATH.'module.tag.apetag.php')) {
+				return $this->error('module.tag.apetag.php is missing - you may disable option_tag_apetag.');
+			}
+			$tag = new getid3_apetag($fp, $this->info);
+			unset($tag);
+		}
+
+		// handle lyrics3 tag
+		if ($this->option_tag_lyrics3) {
+			if (!@include_once(GETID3_INCLUDEPATH.'module.tag.lyrics3.php')) {
+				return $this->error('module.tag.lyrics3.php is missing - you may disable option_tag_lyrics3.');
+			}
+			$tag = new getid3_lyrics3($fp, $this->info);
+			unset($tag);
+		}
+
+		// read 32 kb file data
+		fseek($fp, $this->info['avdataoffset'], SEEK_SET);
+		$formattest = fread($fp, 32774);
+
+		// determine format
+		$determined_format = $this->GetFileFormat($formattest, $filename);
+
+		// unable to determine file format
+		if (!$determined_format) {
+			fclose($fp);
+			return $this->error('unable to determine file format');
+		}
+
+		// check for illegal ID3 tags
+		if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
+			if ($determined_format['fail_id3'] === 'ERROR') {
+				fclose($fp);
+				return $this->error('ID3 tags not allowed on this file type.');
+			} elseif ($determined_format['fail_id3'] === 'WARNING') {
+				$this->info['warning'][] = 'ID3 tags not allowed on this file type.';
+			}
+		}
+
+		// check for illegal APE tags
+		if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
+			if ($determined_format['fail_ape'] === 'ERROR') {
+				fclose($fp);
+				return $this->error('APE tags not allowed on this file type.');
+			} elseif ($determined_format['fail_ape'] === 'WARNING') {
+				$this->info['warning'][] = 'APE tags not allowed on this file type.';
+			}
+		}
+
+		// set mime type
+		$this->info['mime_type'] = $determined_format['mime_type'];
+
+		// supported format signature pattern detected, but module deleted
+		if (!file_exists(GETID3_INCLUDEPATH.$determined_format['include'])) {
+			fclose($fp);
+			return $this->error('Format not supported, module "'.$determined_format['include'].'" was removed.');
+		}
+
+		// module requires iconv support
+        if (!function_exists('iconv') && @$determined_format['iconv_req']) {
+		    return $this->error('iconv support is required for this module ('.$determined_format['include'].').');
+		}
+
+		// include module
+		include_once(GETID3_INCLUDEPATH.$determined_format['include']);
+
+		// instantiate module class
+		$class_name = 'getid3_'.$determined_format['module'];
+		if (!class_exists($class_name)) {
+			return $this->error('Format not supported, module "'.$determined_format['include'].'" is corrupt.');
+		}
+		if (isset($determined_format['option'])) {
+			$class = new $class_name($fp, $this->info, $determined_format['option']);
+		} else {
+			$class = new $class_name($fp, $this->info);
+		}
+		unset($class);
+
+		// close file
+		fclose($fp);
+
+		// process all tags - copy to 'tags' and convert charsets
+		if ($this->option_tags_process) {
+			$this->HandleAllTags();
+		}
+
+		// perform more calculations
+		if ($this->option_extra_info) {
+			$this->ChannelsBitratePlaytimeCalculations();
+			$this->CalculateCompressionRatioVideo();
+			$this->CalculateCompressionRatioAudio();
+			$this->CalculateReplayGain();
+			$this->ProcessAudioStreams();
+		}
+
+		// get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
+		if ($this->option_md5_data) {
+			// do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
+			if (!$this->option_md5_data_source || empty($this->info['md5_data_source'])) {
+				$this->getHashdata('md5');
+			}
+		}
+
+		// get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
+		if ($this->option_sha1_data) {
+			$this->getHashdata('sha1');
+		}
+
+		// remove undesired keys
+		$this->CleanUp();
+
+		// restore magic_quotes_runtime setting
+		set_magic_quotes_runtime($old_magic_quotes_runtime);
+
+		// return info array
+		return $this->info;
+	}
+
+
+	// private: error handling
+	function error($message) {
+
+		$this->CleanUp();
+
+		$this->info['error'][] = $message;
+		return $this->info;
+	}
+
+
+	// private: warning handling
+	function warning($message) {
+		$this->info['warning'][] = $message;
+		return true;
+	}
+
+
+	// private: CleanUp
+	function CleanUp() {
+
+		// remove possible empty keys
+		$AVpossibleEmptyKeys = array('dataformat', 'bits_per_sample', 'encoder_options', 'streams', 'bitrate');
+		foreach ($AVpossibleEmptyKeys as $dummy => $key) {
+			if (empty($this->info['audio'][$key]) && isset($this->info['audio'][$key])) {
+				unset($this->info['audio'][$key]);
+			}
+			if (empty($this->info['video'][$key]) && isset($this->info['video'][$key])) {
+				unset($this->info['video'][$key]);
+			}
+		}
+
+		// remove empty root keys
+		if (!empty($this->info)) {
+			foreach ($this->info as $key => $value) {
+				if (empty($this->info[$key]) && ($this->info[$key] !== 0) && ($this->info[$key] !== '0')) {
+					unset($this->info[$key]);
+				}
+			}
+		}
+
+		// remove meaningless entries from unknown-format files
+		if (empty($this->info['fileformat'])) {
+			if (isset($this->info['avdataoffset'])) {
+				unset($this->info['avdataoffset']);
+			}
+			if (isset($this->info['avdataend'])) {
+				unset($this->info['avdataend']);
+			}
+		}
+	}
+
+
+	// return array containing information about all supported formats
+	function GetFileFormatArray() {
+		static $format_info = array();
+		if (empty($format_info)) {
+			$format_info = array(
+
+				// Audio formats
+
+				// AC-3   - audio      - Dolby AC-3 / Dolby Digital
+				'ac3'  => array(
+							'pattern'   => '^\x0B\x77',
+							'group'     => 'audio',
+							'module'    => 'ac3',
+							'mime_type' => 'audio/ac3',
+						),
+
+				// AAC  - audio       - Advanced Audio Coding (AAC) - ADIF format
+				'adif' => array(
+							'pattern'   => '^ADIF',
+							'group'     => 'audio',
+							'module'    => 'aac',
+							'option'    => 'adif',
+							'mime_type' => 'application/octet-stream',
+							'fail_ape'  => 'WARNING',
+						),
+
+
+				// AAC  - audio       - Advanced Audio Coding (AAC) - ADTS format (very similar to MP3)
+				'adts' => array(
+							'pattern'   => '^\xFF[\xF0-\xF1\xF8-\xF9]',
+							'group'     => 'audio',
+							'module'    => 'aac',
+							'option'    => 'adts',
+							'mime_type' => 'application/octet-stream',
+							'fail_ape'  => 'WARNING',
+						),
+
+
+				// AU   - audio       - NeXT/Sun AUdio (AU)
+				'au'   => array(
+							'pattern'   => '^\.snd',
+							'group'     => 'audio',
+							'module'    => 'au',
+							'mime_type' => 'audio/basic',
+						),
+
+				// AVR  - audio       - Audio Visual Research
+				'avr'  => array(
+							'pattern'   => '^2BIT',
+							'group'     => 'audio',
+							'module'    => 'avr',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// BONK - audio       - Bonk v0.9+
+				'bonk' => array(
+							'pattern'   => '^\x00(BONK|INFO|META| ID3)',
+							'group'     => 'audio',
+							'module'    => 'bonk',
+							'mime_type' => 'audio/xmms-bonk',
+						),
+
+				// DSS  - audio       - Digital Speech Standard
+				'dss'  => array(
+							'pattern'   => '^[\x02]dss',
+							'group'     => 'audio',
+							'module'    => 'dss',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// DTS  - audio       - Dolby Theatre System
+				'dts'  => array(
+							'pattern'   => '^\x7F\xFE\x80\x01',
+							'group'     => 'audio',
+							'module'    => 'dts',
+							'mime_type' => 'audio/dts',
+						),
+
+				// FLAC - audio       - Free Lossless Audio Codec
+				'flac' => array(
+							'pattern'   => '^fLaC',
+							'group'     => 'audio',
+							'module'    => 'flac',
+							'mime_type' => 'audio/x-flac',
+						),
+
+				// LA   - audio       - Lossless Audio (LA)
+				'la'   => array(
+							'pattern'   => '^LA0[2-4]',
+							'group'     => 'audio',
+							'module'    => 'la',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// LPAC - audio       - Lossless Predictive Audio Compression (LPAC)
+				'lpac' => array(
+							'pattern'   => '^LPAC',
+							'group'     => 'audio',
+							'module'    => 'lpac',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// MIDI - audio       - MIDI (Musical Instrument Digital Interface)
+				'midi' => array(
+							'pattern'   => '^MThd',
+							'group'     => 'audio',
+							'module'    => 'midi',
+							'mime_type' => 'audio/midi',
+						),
+
+				// MAC  - audio       - Monkey's Audio Compressor
+				'mac'  => array(
+							'pattern'   => '^MAC ',
+							'group'     => 'audio',
+							'module'    => 'monkey',
+							'mime_type' => 'application/octet-stream',
+						),
+
+// has been known to produce false matches in random files (e.g. JPEGs), leave out until more precise matching available
+//				// MOD  - audio       - MODule (assorted sub-formats)
+//				'mod'  => array(
+//							'pattern'   => '^.{1080}(M\\.K\\.|M!K!|FLT4|FLT8|[5-9]CHN|[1-3][0-9]CH)',
+//							'group'     => 'audio',
+//							'module'    => 'mod',
+//							'option'    => 'mod',
+//							'mime_type' => 'audio/mod',
+//						),
+
+				// MOD  - audio       - MODule (Impulse Tracker)
+				'it'   => array(
+							'pattern'   => '^IMPM',
+							'group'     => 'audio',
+							'module'    => 'mod',
+							'option'    => 'it',
+							'mime_type' => 'audio/it',
+						),
+
+				// MOD  - audio       - MODule (eXtended Module, various sub-formats)
+				'xm'   => array(
+							'pattern'   => '^Extended Module',
+							'group'     => 'audio',
+							'module'    => 'mod',
+							'option'    => 'xm',
+							'mime_type' => 'audio/xm',
+						),
+
+				// MOD  - audio       - MODule (ScreamTracker)
+				's3m'  => array(
+							'pattern'   => '^.{44}SCRM',
+							'group'     => 'audio',
+							'module'    => 'mod',
+							'option'    => 's3m',
+							'mime_type' => 'audio/s3m',
+						),
+
+				// MPC  - audio       - Musepack / MPEGplus
+				'mpc'  => array(
+							'pattern'   => '^(MPCK|MP\+|[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0])',
+							'group'     => 'audio',
+							'module'    => 'mpc',
+							'mime_type' => 'audio/x-musepack',
+						),
+
+				// MP3  - audio       - MPEG-audio Layer 3 (very similar to AAC-ADTS)
+				'mp3'  => array(
+							'pattern'   => '^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]',
+							'group'     => 'audio',
+							'module'    => 'mp3',
+							'mime_type' => 'audio/mpeg',
+						),
+
+				// OFR  - audio       - OptimFROG
+				'ofr'  => array(
+							'pattern'   => '^(\*RIFF|OFR)',
+							'group'     => 'audio',
+							'module'    => 'optimfrog',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// RKAU - audio       - RKive AUdio compressor
+				'rkau' => array(
+							'pattern'   => '^RKA',
+							'group'     => 'audio',
+							'module'    => 'rkau',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// SHN  - audio       - Shorten
+				'shn'  => array(
+							'pattern'   => '^ajkg',
+							'group'     => 'audio',
+							'module'    => 'shorten',
+							'mime_type' => 'audio/xmms-shn',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// TTA  - audio       - TTA Lossless Audio Compressor (http://tta.corecodec.org)
+				'tta'  => array(
+							'pattern'   => '^TTA',  // could also be '^TTA(\x01|\x02|\x03|2|1)'
+							'group'     => 'audio',
+							'module'    => 'tta',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// VOC  - audio       - Creative Voice (VOC)
+				'voc'  => array(
+							'pattern'   => '^Creative Voice File',
+							'group'     => 'audio',
+							'module'    => 'voc',
+							'mime_type' => 'audio/voc',
+						),
+
+				// VQF  - audio       - transform-domain weighted interleave Vector Quantization Format (VQF)
+				'vqf'  => array(
+							'pattern'   => '^TWIN',
+							'group'     => 'audio',
+							'module'    => 'vqf',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// WV  - audio        - WavPack (v4.0+)
+				'wv'   => array(
+							'pattern'   => '^wvpk',
+							'group'     => 'audio',
+							'module'    => 'wavpack',
+							'mime_type' => 'application/octet-stream',
+						),
+
+
+				// Audio-Video formats
+
+				// ASF  - audio/video - Advanced Streaming Format, Windows Media Video, Windows Media Audio
+				'asf'  => array(
+							'pattern'   => '^\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C',
+							'group'     => 'audio-video',
+							'module'    => 'asf',
+							'mime_type' => 'video/x-ms-asf',
+							'iconv_req' => false,
+						),
+
+				// BINK - audio/video - Bink / Smacker
+				'bink' => array(
+							'pattern'   => '^(BIK|SMK)',
+							'group'     => 'audio-video',
+							'module'    => 'bink',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// FLV  - audio/video - FLash Video
+				'flv' => array(
+							'pattern'   => '^FLV\x01',
+							'group'     => 'audio-video',
+							'module'    => 'flv',
+							'mime_type' => 'video/x-flv',
+						),
+
+				// MKAV - audio/video - Mastroka
+				'matroska' => array(
+							'pattern'   => '^\x1A\x45\xDF\xA3',
+							'group'     => 'audio-video',
+							'module'    => 'matroska',
+							'mime_type' => 'video/x-matroska', // may also be audio/x-matroska
+						),
+
+				// MPEG - audio/video - MPEG (Moving Pictures Experts Group)
+				'mpeg' => array(
+							'pattern'   => '^\x00\x00\x01(\xBA|\xB3)',
+							'group'     => 'audio-video',
+							'module'    => 'mpeg',
+							'mime_type' => 'video/mpeg',
+						),
+
+				// NSV  - audio/video - Nullsoft Streaming Video (NSV)
+				'nsv'  => array(
+							'pattern'   => '^NSV[sf]',
+							'group'     => 'audio-video',
+							'module'    => 'nsv',
+							'mime_type' => 'application/octet-stream',
+						),
+
+				// Ogg  - audio/video - Ogg (Ogg-Vorbis, Ogg-FLAC, Speex, Ogg-Theora(*), Ogg-Tarkin(*))
+				'ogg'  => array(
+							'pattern'   => '^OggS',
+							'group'     => 'audio',
+							'module'    => 'ogg',
+							'mime_type' => 'application/ogg',
+							'fail_id3'  => 'WARNING',
+							'fail_ape'  => 'WARNING',
+						),
+
+				// QT   - audio/video - Quicktime
+				'quicktime' => array(
+							'pattern'   => '^.{4}(cmov|free|ftyp|mdat|moov|pnot|skip|wide)',
+							'group'     => 'audio-video',
+							'module'    => 'quicktime',
+							'mime_type' => 'video/quicktime',
+						),
+
+				// RIFF - audio/video - Resource Interchange File Format (RIFF) / WAV / AVI / CD-audio / SDSS = renamed variant used by SmartSound QuickTracks (www.smartsound.com) / FORM = Audio Interchange File Format (AIFF)
+				'riff' => array(
+							'pattern'   => '^(RIFF|SDSS|FORM)',
+							'group'     => 'audio-video',
+							'module'    => 'riff',
+							'mime_type' => 'audio/x-wave',
+							'fail_ape'  => 'WARNING',
+						),
+
+				// Real - audio/video - RealAudio, RealVideo
+				'real' => array(
+							'pattern'   => '^(\\.RMF|\\.ra)',
+							'group'     => 'audio-video',
+							'module'    => 'real',
+							'mime_type' => 'audio/x-realaudio',
+						),
+
+				// SWF - audio/video - ShockWave Flash
+				'swf' => array(
+							'pattern'   => '^(F|C)WS',
+							'group'     => 'audio-video',
+							'module'    => 'swf',
+							'mime_type' => 'application/x-shockwave-flash',
+						),
+
+
+				// Still-Image formats
+
+				// BMP  - still image - Bitmap (Windows, OS/2; uncompressed, RLE8, RLE4)
+				'bmp'  => array(
+							'pattern'   => '^BM',
+							'group'     => 'graphic',
+							'module'    => 'bmp',
+							'mime_type' => 'image/bmp',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// GIF  - still image - Graphics Interchange Format
+				'gif'  => array(
+							'pattern'   => '^GIF',
+							'group'     => 'graphic',
+							'module'    => 'gif',
+							'mime_type' => 'image/gif',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// JPEG - still image - Joint Photographic Experts Group (JPEG)
+				'jpg'  => array(
+							'pattern'   => '^\xFF\xD8\xFF',
+							'group'     => 'graphic',
+							'module'    => 'jpg',
+							'mime_type' => 'image/jpeg',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// PCD  - still image - Kodak Photo CD
+				'pcd'  => array(
+							'pattern'   => '^.{2048}PCD_IPI\x00',
+							'group'     => 'graphic',
+							'module'    => 'pcd',
+							'mime_type' => 'image/x-photo-cd',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+
+				// PNG  - still image - Portable Network Graphics (PNG)
+				'png'  => array(
+							'pattern'   => '^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A',
+							'group'     => 'graphic',
+							'module'    => 'png',
+							'mime_type' => 'image/png',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+
+                // SVG  - still image - Scalable Vector Graphics (SVG)
+				'svg'  => array(
+							'pattern'   => '<!DOCTYPE svg PUBLIC ',
+							'group'     => 'graphic',
+							'module'    => 'svg',
+							'mime_type' => 'image/svg+xml',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+
+				// TIFF  - still image - Tagged Information File Format (TIFF)
+				'tiff' => array(
+							'pattern'   => '^(II\x2A\x00|MM\x00\x2A)',
+							'group'     => 'graphic',
+							'module'    => 'tiff',
+							'mime_type' => 'image/tiff',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+
+				// Data formats
+
+				// ISO  - data        - International Standards Organization (ISO) CD-ROM Image
+				'iso'  => array(
+							'pattern'   => '^.{32769}CD001',
+							'group'     => 'misc',
+							'module'    => 'iso',
+							'mime_type' => 'application/octet-stream',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+							'iconv_req' => false,
+						),
+
+				// RAR  - data        - RAR compressed data
+				'rar'  => array(
+							'pattern'   => '^Rar\!',
+							'group'     => 'archive',
+							'module'    => 'rar',
+							'mime_type' => 'application/octet-stream',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// SZIP - audio/data  - SZIP compressed data
+				'szip' => array(
+							'pattern'   => '^SZ\x0A\x04',
+							'group'     => 'archive',
+							'module'    => 'szip',
+							'mime_type' => 'application/octet-stream',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// TAR  - data        - TAR compressed data
+				'tar'  => array(
+							'pattern'   => '^.{100}[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20]{7}\x00[0-9\x20\x00]{12}[0-9\x20\x00]{12}',
+							'group'     => 'archive',
+							'module'    => 'tar',
+							'mime_type' => 'application/x-tar',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// GZIP  - data        - GZIP compressed data
+				'gz'  => array(
+							'pattern'   => '^\x1F\x8B\x08',
+							'group'     => 'archive',
+							'module'    => 'gzip',
+							'mime_type' => 'application/x-gzip',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// ZIP  - data         - ZIP compressed data
+				'zip'  => array(
+							'pattern'   => '^PK\x03\x04',
+							'group'     => 'archive',
+							'module'    => 'zip',
+							'mime_type' => 'application/zip',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+
+				// Misc other formats
+
+                // PAR2 - data        - Parity Volume Set Specification 2.0
+                'par2' => array (
+                			'pattern'   => '^PAR2\x00PKT',
+                			'group'     => 'misc',
+							'module'    => 'par2',
+							'mime_type' => 'application/octet-stream',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// PDF  - data        - Portable Document Format
+				'pdf'  => array(
+							'pattern'   => '^\x25PDF',
+							'group'     => 'misc',
+							'module'    => 'pdf',
+							'mime_type' => 'application/pdf',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+
+				// MSOFFICE  - data   - ZIP compressed data
+				'msoffice' => array(
+							'pattern'   => '^\xD0\xCF\x11\xE0', // D0CF11E == DOCFILE == Microsoft Office Document
+							'group'     => 'misc',
+							'module'    => 'msoffice',
+							'mime_type' => 'application/octet-stream',
+							'fail_id3'  => 'ERROR',
+							'fail_ape'  => 'ERROR',
+						),
+			);
+		}
+
+		return $format_info;
+	}
+
+
+
+	function GetFileFormat(&$filedata, $filename='') {
+		// this function will determine the format of a file based on usually
+		// the first 2-4 bytes of the file (8 bytes for PNG, 16 bytes for JPG,
+		// and in the case of ISO CD image, 6 bytes offset 32kb from the start
+		// of the file).
+
+		// Identify file format - loop through $format_info and detect with reg expr
+		foreach ($this->GetFileFormatArray() as $format_name => $info) {
+			// Using preg_match() instead of ereg() - much faster
+			// The /s switch on preg_match() forces preg_match() NOT to treat
+			// newline (0x0A) characters as special chars but do a binary match
+			if (preg_match('/'.$info['pattern'].'/s', $filedata)) {
+				$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
+				return $info;
+			}
+		}
+
+
+		if (preg_match('/\.mp[123a]$/i', $filename)) {
+			// Too many mp3 encoders on the market put gabage in front of mpeg files
+			// use assume format on these if format detection failed
+			$GetFileFormatArray = $this->GetFileFormatArray();
+			$info = $GetFileFormatArray['mp3'];
+			$info['include'] = 'module.'.$info['group'].'.'.$info['module'].'.php';
+			return $info;
+		}
+
+		return false;
+	}
+
+
+	// converts array to $encoding charset from $this->encoding
+	function CharConvert(&$array, $encoding) {
+
+		// identical encoding - end here
+		if ($encoding == $this->encoding) {
+			return;
+		}
+
+		// loop thru array
+		foreach ($array as $key => $value) {
+
+			// go recursive
+			if (is_array($value)) {
+				$this->CharConvert($array[$key], $encoding);
+			}
+
+			// convert string
+			elseif (is_string($value)) {
+				$array[$key] = trim(getid3_lib::iconv_fallback($encoding, $this->encoding, $value));
+			}
+		}
+	}
+
+
+	function HandleAllTags() {
+
+		// key name => array (tag name, character encoding)
+		static $tags;
+		if (empty($tags)) {
+			$tags = array(
+				'asf'       => array('asf'           , 'UTF-16LE'),
+				'midi'      => array('midi'          , 'ISO-8859-1'),
+				'nsv'       => array('nsv'           , 'ISO-8859-1'),
+				'ogg'       => array('vorbiscomment' , 'UTF-8'),
+				'png'       => array('png'           , 'UTF-8'),
+				'tiff'      => array('tiff'          , 'ISO-8859-1'),
+				'quicktime' => array('quicktime'     , 'ISO-8859-1'),
+				'real'      => array('real'          , 'ISO-8859-1'),
+				'vqf'       => array('vqf'           , 'ISO-8859-1'),
+				'zip'       => array('zip'           , 'ISO-8859-1'),
+				'riff'      => array('riff'          , 'ISO-8859-1'),
+				'lyrics3'   => array('lyrics3'       , 'ISO-8859-1'),
+				'id3v1'     => array('id3v1'         , $this->encoding_id3v1),
+				'id3v2'     => array('id3v2'         , 'UTF-8'), // not according to the specs (every frame can have a different encoding), but getID3() force-converts all encodings to UTF-8
+				'ape'       => array('ape'           , 'UTF-8')
+			);
+		}
+
+		// loop thru comments array
+		foreach ($tags as $comment_name => $tagname_encoding_array) {
+			list($tag_name, $encoding) = $tagname_encoding_array;
+
+			// fill in default encoding type if not already present
+			if (isset($this->info[$comment_name]) && !isset($this->info[$comment_name]['encoding'])) {
+				$this->info[$comment_name]['encoding'] = $encoding;
+			}
+
+			// copy comments if key name set
+			if (!empty($this->info[$comment_name]['comments'])) {
+
+				foreach ($this->info[$comment_name]['comments'] as $tag_key => $valuearray) {
+					foreach ($valuearray as $key => $value) {
+						if (strlen(trim($value)) > 0) {
+							$this->info['tags'][trim($tag_name)][trim($tag_key)][] = $value; // do not trim!! Unicode characters will get mangled if trailing nulls are removed!
+						}
+					}
+				}
+
+				if (!isset($this->info['tags'][$tag_name])) {
+					// comments are set but contain nothing but empty strings, so skip
+					continue;
+				}
+
+				if ($this->option_tags_html) {
+					foreach ($this->info['tags'][$tag_name] as $tag_key => $valuearray) {
+						foreach ($valuearray as $key => $value) {
+							if (is_string($value)) {
+								//$this->info['tags_html'][$tag_name][$tag_key][$key] = getid3_lib::MultiByteCharString2HTML($value, $encoding);
+								$this->info['tags_html'][$tag_name][$tag_key][$key] = str_replace('&#0;', '', getid3_lib::MultiByteCharString2HTML($value, $encoding));
+							} else {
+								$this->info['tags_html'][$tag_name][$tag_key][$key] = $value;
+							}
+						}
+					}
+				}
+
+				$this->CharConvert($this->info['tags'][$tag_name], $encoding);           // only copy gets converted!
+			}
+
+		}
+		return true;
+	}
+
+
+	function getHashdata($algorithm) {
+		switch ($algorithm) {
+			case 'md5':
+			case 'sha1':
+				break;
+
+			default:
+				return $this->error('bad algorithm "'.$algorithm.'" in getHashdata()');
+				break;
+		}
+
+		if ((@$this->info['fileformat'] == 'ogg') && (@$this->info['audio']['dataformat'] == 'vorbis')) {
+
+			// We cannot get an identical md5_data value for Ogg files where the comments
+			// span more than 1 Ogg page (compared to the same audio data with smaller
+			// comments) using the normal getID3() method of MD5'ing the data between the
+			// end of the comments and the end of the file (minus any trailing tags),
+			// because the page sequence numbers of the pages that the audio data is on
+			// do not match. Under normal circumstances, where comments are smaller than
+			// the nominal 4-8kB page size, then this is not a problem, but if there are
+			// very large comments, the only way around it is to strip off the comment
+			// tags with vorbiscomment and MD5 that file.
+			// This procedure must be applied to ALL Ogg files, not just the ones with
+			// comments larger than 1 page, because the below method simply MD5's the
+			// whole file with the comments stripped, not just the portion after the
+			// comments block (which is the standard getID3() method.
+
+			// The above-mentioned problem of comments spanning multiple pages and changing
+			// page sequence numbers likely happens for OggSpeex and OggFLAC as well, but
+			// currently vorbiscomment only works on OggVorbis files.
+
+			if ((bool) ini_get('safe_mode')) {
+
+				$this->info['warning'][] = 'Failed making system call to vorbiscomment.exe - '.$algorithm.'_data is incorrect - error returned: PHP running in Safe Mode (backtick operator not available)';
+				$this->info[$algorithm.'_data']  = false;
+
+			} else {
+
+				// Prevent user from aborting script
+				$old_abort = ignore_user_abort(true);
+
+				// Create empty file
+				$empty = tempnam('*', 'getID3');
+				touch($empty);
+
+
+				// Use vorbiscomment to make temp file without comments
+				$temp = tempnam('*', 'getID3');
+				$file = $this->info['filenamepath'];
+
+				if (GETID3_OS_ISWINDOWS) {
+
+					if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
+
+						$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w -c "'.$empty.'" "'.$file.'" "'.$temp.'"';
+						$VorbisCommentError = `$commandline`;
+
+					} else {
+
+						$VorbisCommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
+
+					}
+
+				} else {
+
+					$commandline = 'vorbiscomment -w -c "'.$empty.'" "'.$file.'" "'.$temp.'" 2>&1';
+					$commandline = 'vorbiscomment -w -c '.escapeshellarg($empty).' '.escapeshellarg($file).' '.escapeshellarg($temp).' 2>&1';
+					$VorbisCommentError = `$commandline`;
+
+				}
+
+				if (!empty($VorbisCommentError)) {
+
+					$this->info['warning'][]         = 'Failed making system call to vorbiscomment(.exe) - '.$algorithm.'_data will be incorrect. If vorbiscomment is unavailable, please download from http://www.vorbis.com/download.psp and put in the getID3() directory. Error returned: '.$VorbisCommentError;
+					$this->info[$algorithm.'_data']  = false;
+
+				} else {
+
+					// Get hash of newly created file
+					switch ($algorithm) {
+						case 'md5':
+							$this->info[$algorithm.'_data'] = getid3_lib::md5_file($temp);
+							break;
+
+						case 'sha1':
+							$this->info[$algorithm.'_data'] = getid3_lib::sha1_file($temp);
+							break;
+					}
+				}
+
+				// Clean up
+				unlink($empty);
+				unlink($temp);
+
+				// Reset abort setting
+				ignore_user_abort($old_abort);
+
+			}
+
+		} else {
+
+			if (!empty($this->info['avdataoffset']) || (isset($this->info['avdataend']) && ($this->info['avdataend'] < $this->info['filesize']))) {
+
+				// get hash from part of file
+				$this->info[$algorithm.'_data'] = getid3_lib::hash_data($this->info['filenamepath'], $this->info['avdataoffset'], $this->info['avdataend'], $algorithm);
+
+			} else {
+
+				// get hash from whole file
+				switch ($algorithm) {
+					case 'md5':
+						$this->info[$algorithm.'_data'] = getid3_lib::md5_file($this->info['filenamepath']);
+						break;
+
+					case 'sha1':
+						$this->info[$algorithm.'_data'] = getid3_lib::sha1_file($this->info['filenamepath']);
+						break;
+				}
+			}
+
+		}
+		return true;
+	}
+
+
+	function ChannelsBitratePlaytimeCalculations() {
+
+		// set channelmode on audio
+		if (@$this->info['audio']['channels'] == '1') {
+			$this->info['audio']['channelmode'] = 'mono';
+		} elseif (@$this->info['audio']['channels'] == '2') {
+			$this->info['audio']['channelmode'] = 'stereo';
+		}
+
+		// Calculate combined bitrate - audio + video
+		$CombinedBitrate  = 0;
+		$CombinedBitrate += (isset($this->info['audio']['bitrate']) ? $this->info['audio']['bitrate'] : 0);
+		$CombinedBitrate += (isset($this->info['video']['bitrate']) ? $this->info['video']['bitrate'] : 0);
+		if (($CombinedBitrate > 0) && empty($this->info['bitrate'])) {
+			$this->info['bitrate'] = $CombinedBitrate;
+		}
+		//if ((isset($this->info['video']) && !isset($this->info['video']['bitrate'])) || (isset($this->info['audio']) && !isset($this->info['audio']['bitrate']))) {
+		//	// for example, VBR MPEG video files cannot determine video bitrate:
+		//	// should not set overall bitrate and playtime from audio bitrate only
+		//	unset($this->info['bitrate']);
+		//}
+
+		// video bitrate undetermined, but calculable
+		if (isset($this->info['video']['dataformat']) && $this->info['video']['dataformat'] && (!isset($this->info['video']['bitrate']) || ($this->info['video']['bitrate'] == 0))) {
+			// if video bitrate not set
+			if (isset($this->info['audio']['bitrate']) && ($this->info['audio']['bitrate'] > 0) && ($this->info['audio']['bitrate'] == $this->info['bitrate'])) {
+				// AND if audio bitrate is set to same as overall bitrate
+				if (isset($this->info['playtime_seconds']) && ($this->info['playtime_seconds'] > 0)) {
+					// AND if playtime is set
+					if (isset($this->info['avdataend']) && isset($this->info['avdataoffset'])) {
+						// AND if AV data offset start/end is known
+						// THEN we can calculate the video bitrate
+						$this->info['bitrate'] = round((($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds']);
+						$this->info['video']['bitrate'] = $this->info['bitrate'] - $this->info['audio']['bitrate'];
+					}
+				}
+			}
+		}
+
+		if ((!isset($this->info['playtime_seconds']) || ($this->info['playtime_seconds'] <= 0)) && !empty($this->info['bitrate'])) {
+			$this->info['playtime_seconds'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['bitrate'];
+		}
+
+		if (!isset($this->info['bitrate']) && !empty($this->info['playtime_seconds'])) {
+			$this->info['bitrate'] = (($this->info['avdataend'] - $this->info['avdataoffset']) * 8) / $this->info['playtime_seconds'];
+		}
+//echo '<pre>';
+//var_dump($this->info['bitrate']);
+//var_dump($this->info['audio']['bitrate']);
+//var_dump($this->info['video']['bitrate']);
+//echo '</pre>';
+		if (isset($this->info['bitrate']) && empty($this->info['audio']['bitrate']) && empty($this->info['video']['bitrate'])) {
+			if (isset($this->info['audio']['dataformat']) && empty($this->info['video']['resolution_x'])) {
+				// audio only
+				$this->info['audio']['bitrate'] = $this->info['bitrate'];
+			} elseif (isset($this->info['video']['resolution_x']) && empty($this->info['audio']['dataformat'])) {
+				// video only
+				$this->info['video']['bitrate'] = $this->info['bitrate'];
+			}
+		}
+
+		// Set playtime string
+		if (!empty($this->info['playtime_seconds']) && empty($this->info['playtime_string'])) {
+			$this->info['playtime_string'] = getid3_lib::PlaytimeString($this->info['playtime_seconds']);
+		}
+	}
+
+
+	function CalculateCompressionRatioVideo() {
+		if (empty($this->info['video'])) {
+			return false;
+		}
+		if (empty($this->info['video']['resolution_x']) || empty($this->info['video']['resolution_y'])) {
+			return false;
+		}
+		if (empty($this->info['video']['bits_per_sample'])) {
+			return false;
+		}
+
+		switch ($this->info['video']['dataformat']) {
+			case 'bmp':
+			case 'gif':
+			case 'jpeg':
+			case 'jpg':
+			case 'png':
+			case 'tiff':
+				$FrameRate = 1;
+				$PlaytimeSeconds = 1;
+				$BitrateCompressed = $this->info['filesize'] * 8;
+				break;
+
+			default:
+				if (!empty($this->info['video']['frame_rate'])) {
+					$FrameRate = $this->info['video']['frame_rate'];
+				} else {
+					return false;
+				}
+				if (!empty($this->info['playtime_seconds'])) {
+					$PlaytimeSeconds = $this->info['playtime_seconds'];
+				} else {
+					return false;
+				}
+				if (!empty($this->info['video']['bitrate'])) {
+					$BitrateCompressed = $this->info['video']['bitrate'];
+				} else {
+					return false;
+				}
+				break;
+		}
+		$BitrateUncompressed = $this->info['video']['resolution_x'] * $this->info['video']['resolution_y'] * $this->info['video']['bits_per_sample'] * $FrameRate;
+
+		$this->info['video']['compression_ratio'] = $BitrateCompressed / $BitrateUncompressed;
+		return true;
+	}
+
+
+	function CalculateCompressionRatioAudio() {
+		if (empty($this->info['audio']['bitrate']) || empty($this->info['audio']['channels']) || empty($this->info['audio']['sample_rate'])) {
+			return false;
+		}
+		$this->info['audio']['compression_ratio'] = $this->info['audio']['bitrate'] / ($this->info['audio']['channels'] * $this->info['audio']['sample_rate'] * (!empty($this->info['audio']['bits_per_sample']) ? $this->info['audio']['bits_per_sample'] : 16));
+
+		if (!empty($this->info['audio']['streams'])) {
+			foreach ($this->info['audio']['streams'] as $streamnumber => $streamdata) {
+				if (!empty($streamdata['bitrate']) && !empty($streamdata['channels']) && !empty($streamdata['sample_rate'])) {
+					$this->info['audio']['streams'][$streamnumber]['compression_ratio'] = $streamdata['bitrate'] / ($streamdata['channels'] * $streamdata['sample_rate'] * (!empty($streamdata['bits_per_sample']) ? $streamdata['bits_per_sample'] : 16));
+				}
+			}
+		}
+		return true;
+	}
+
+
+	function CalculateReplayGain() {
+		if (isset($this->info['replay_gain'])) {
+			$this->info['replay_gain']['reference_volume'] = 89;
+			if (isset($this->info['replay_gain']['track']['adjustment'])) {
+				$this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
+			}
+			if (isset($this->info['replay_gain']['album']['adjustment'])) {
+				$this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
+			}
+
+			if (isset($this->info['replay_gain']['track']['peak'])) {
+				$this->info['replay_gain']['track']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
+			}
+			if (isset($this->info['replay_gain']['album']['peak'])) {
+				$this->info['replay_gain']['album']['max_noclip_gain'] = 0 - getid3_lib::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
+			}
+		}
+		return true;
+	}
+
+	function ProcessAudioStreams() {
+		if (!empty($this->info['audio']['bitrate']) || !empty($this->info['audio']['channels']) || !empty($this->info['audio']['sample_rate'])) {
+			if (!isset($this->info['audio']['streams'])) {
+				foreach ($this->info['audio'] as $key => $value) {
+					if ($key != 'streams') {
+						$this->info['audio']['streams'][0][$key] = $value;
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	function getid3_tempnam() {
+		return tempnam($this->tempdir, 'gI3');
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.archive.gzip.php b/apps/media/getID3/getid3/module.archive.gzip.php
new file mode 100644
index 0000000000000000000000000000000000000000..7e9376f37ba32dfdd601dbbf2ec3508d5e7c75f4
--- /dev/null
+++ b/apps/media/getID3/getid3/module.archive.gzip.php
@@ -0,0 +1,271 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.gzip.php                                     //
+// module for analyzing GZIP files                             //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// Module originally written by                                //
+//      Mike Mozolin <teddybearØmail*ru>                       //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_gzip {
+
+	// public: Optional file list - disable for speed.
+	var $option_gzip_parse_contents = false; // decode gzipped files, if possible, and parse recursively (.tar.gz for example)
+
+	function getid3_gzip(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat'] = 'gzip';
+
+		$start_length = 10;
+		$unpack_header = 'a1id1/a1id2/a1cmethod/a1flags/a4mtime/a1xflags/a1os';
+		//+---+---+---+---+---+---+---+---+---+---+
+		//|ID1|ID2|CM |FLG|     MTIME     |XFL|OS |
+		//+---+---+---+---+---+---+---+---+---+---+
+		@fseek($fd, 0);
+		$buffer = @fread($fd, $ThisFileInfo['filesize']);
+
+		$arr_members = explode("\x1F\x8B\x08", $buffer);
+		while (true) {
+			$is_wrong_members = false;
+			$num_members = intval(count($arr_members));
+			for ($i = 0; $i < $num_members; $i++) {
+				if (strlen($arr_members[$i]) == 0) {
+					continue;
+				}
+				$buf = "\x1F\x8B\x08".$arr_members[$i];
+
+				$attr = unpack($unpack_header, substr($buf, 0, $start_length));
+				if (!$this->get_os_type(ord($attr['os']))) {
+					// Merge member with previous if wrong OS type
+					$arr_members[$i - 1] .= $buf;
+					$arr_members[$i] = '';
+					$is_wrong_members = true;
+					continue;
+				}
+			}
+			if (!$is_wrong_members) {
+				break;
+			}
+		}
+
+		$ThisFileInfo['gzip']['files'] = array();
+
+		$fpointer = 0;
+		$idx = 0;
+		for ($i = 0; $i < $num_members; $i++) {
+			if (strlen($arr_members[$i]) == 0) {
+				continue;
+			}
+			$thisThisFileInfo = &$ThisFileInfo['gzip']['member_header'][++$idx];
+
+			$buff = "\x1F\x8B\x08".$arr_members[$i];
+
+			$attr = unpack($unpack_header, substr($buff, 0, $start_length));
+			$thisThisFileInfo['filemtime']      = getid3_lib::LittleEndian2Int($attr['mtime']);
+			$thisThisFileInfo['raw']['id1']     = ord($attr['cmethod']);
+			$thisThisFileInfo['raw']['id2']     = ord($attr['cmethod']);
+			$thisThisFileInfo['raw']['cmethod'] = ord($attr['cmethod']);
+			$thisThisFileInfo['raw']['os']      = ord($attr['os']);
+			$thisThisFileInfo['raw']['xflags']  = ord($attr['xflags']);
+			$thisThisFileInfo['raw']['flags']   = ord($attr['flags']);
+
+			$thisThisFileInfo['flags']['crc16']    = (bool) ($thisThisFileInfo['raw']['flags'] & 0x02);
+			$thisThisFileInfo['flags']['extra']    = (bool) ($thisThisFileInfo['raw']['flags'] & 0x04);
+			$thisThisFileInfo['flags']['filename'] = (bool) ($thisThisFileInfo['raw']['flags'] & 0x08);
+			$thisThisFileInfo['flags']['comment']  = (bool) ($thisThisFileInfo['raw']['flags'] & 0x10);
+
+			$thisThisFileInfo['compression'] = $this->get_xflag_type($thisThisFileInfo['raw']['xflags']);
+
+			$thisThisFileInfo['os'] = $this->get_os_type($thisThisFileInfo['raw']['os']);
+			if (!$thisThisFileInfo['os']) {
+				$ThisFileInfo['error'][] = 'Read error on gzip file';
+				return false;
+			}
+
+			$fpointer = 10;
+			$arr_xsubfield = array();
+			// bit 2 - FLG.FEXTRA
+			//+---+---+=================================+
+			//| XLEN  |...XLEN bytes of "extra field"...|
+			//+---+---+=================================+
+			if ($thisThisFileInfo['flags']['extra']) {
+				$w_xlen = substr($buff, $fpointer, 2);
+				$xlen = getid3_lib::LittleEndian2Int($w_xlen);
+				$fpointer += 2;
+
+				$thisThisFileInfo['raw']['xfield'] = substr($buff, $fpointer, $xlen);
+				// Extra SubFields
+				//+---+---+---+---+==================================+
+				//|SI1|SI2|  LEN  |... LEN bytes of subfield data ...|
+				//+---+---+---+---+==================================+
+				$idx = 0;
+				while (true) {
+					if ($idx >= $xlen) {
+						break;
+					}
+					$si1 = ord(substr($buff, $fpointer + $idx++, 1));
+					$si2 = ord(substr($buff, $fpointer + $idx++, 1));
+					if (($si1 == 0x41) && ($si2 == 0x70)) {
+						$w_xsublen = substr($buff, $fpointer + $idx, 2);
+						$xsublen = getid3_lib::LittleEndian2Int($w_xsublen);
+						$idx += 2;
+						$arr_xsubfield[] = substr($buff, $fpointer + $idx, $xsublen);
+						$idx += $xsublen;
+					} else {
+						break;
+					}
+				}
+				$fpointer += $xlen;
+			}
+			// bit 3 - FLG.FNAME
+			//+=========================================+
+			//|...original file name, zero-terminated...|
+			//+=========================================+
+			// GZIP files may have only one file, with no filename, so assume original filename is current filename without .gz
+			$thisThisFileInfo['filename'] = eregi_replace('.gz$', '', $ThisFileInfo['filename']);
+			if ($thisThisFileInfo['flags']['filename']) {
+				while (true) {
+					if (ord($buff[$fpointer]) == 0) {
+						$fpointer++;
+						break;
+					}
+					$thisThisFileInfo['filename'] .= $buff[$fpointer];
+					$fpointer++;
+				}
+			}
+			// bit 4 - FLG.FCOMMENT
+			//+===================================+
+			//|...file comment, zero-terminated...|
+			//+===================================+
+			if ($thisThisFileInfo['flags']['comment']) {
+				while (true) {
+					if (ord($buff[$fpointer]) == 0) {
+						$fpointer++;
+						break;
+					}
+					$thisThisFileInfo['comment'] .= $buff[$fpointer];
+					$fpointer++;
+				}
+			}
+			// bit 1 - FLG.FHCRC
+			//+---+---+
+			//| CRC16 |
+			//+---+---+
+			if ($thisThisFileInfo['flags']['crc16']) {
+				$w_crc = substr($buff, $fpointer, 2);
+				$thisThisFileInfo['crc16'] = getid3_lib::LittleEndian2Int($w_crc);
+				$fpointer += 2;
+			}
+			// bit 0 - FLG.FTEXT
+			//if ($thisThisFileInfo['raw']['flags'] & 0x01) {
+			//	Ignored...
+			//}
+			// bits 5, 6, 7 - reserved
+
+			$thisThisFileInfo['crc32']    = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 8, 4));
+			$thisThisFileInfo['filesize'] = getid3_lib::LittleEndian2Int(substr($buff, strlen($buff) - 4));
+
+			$ThisFileInfo['gzip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['gzip']['files'], getid3_lib::CreateDeepArray($thisThisFileInfo['filename'], '/', $thisThisFileInfo['filesize']));
+
+			if ($this->option_gzip_parse_contents) {
+				// Try to inflate GZip
+				$csize = 0;
+				$inflated = '';
+				$chkcrc32 = '';
+				if (function_exists('gzinflate')) {
+					$cdata = substr($buff, $fpointer);
+					$cdata = substr($cdata, 0, strlen($cdata) - 8);
+					$csize = strlen($cdata);
+					$inflated = gzinflate($cdata);
+
+					// Calculate CRC32 for inflated content
+					$thisThisFileInfo['crc32_valid'] = (bool) (sprintf('%u', crc32($inflated)) == $thisThisFileInfo['crc32']);
+
+					// determine format
+					$formattest = substr($inflated, 0, 32774);
+					$newgetID3 = new getID3();
+					$determined_format = $newgetID3->GetFileFormat($formattest);
+					unset($newgetID3);
+
+	        		// file format is determined
+	        		switch (@$determined_format['module']) {
+	        			case 'tar':
+							// view TAR-file info
+							if (file_exists(GETID3_INCLUDEPATH.$determined_format['include']) && @include_once(GETID3_INCLUDEPATH.$determined_format['include'])) {
+								if (($temp_tar_filename = tempnam('*', 'getID3')) === false) {
+									// can't find anywhere to create a temp file, abort
+									$ThisFileInfo['error'][] = 'Unable to create temp file to parse TAR inside GZIP file';
+									break;
+								}
+								if ($fp_temp_tar = fopen($temp_tar_filename, 'w+b')) {
+									fwrite($fp_temp_tar, $inflated);
+									rewind($fp_temp_tar);
+									$getid3_tar = new getid3_tar($fp_temp_tar, $dummy);
+									$ThisFileInfo['gzip']['member_header'][$idx]['tar'] = $dummy['tar'];
+									unset($dummy);
+									unset($getid3_tar);
+									fclose($fp_temp_tar);
+									unlink($temp_tar_filename);
+								} else {
+									$ThisFileInfo['error'][] = 'Unable to fopen() temp file to parse TAR inside GZIP file';
+									break;
+								}
+							}
+							break;
+
+	        			case '':
+	        			default:
+	        				// unknown or unhandled format
+	        				break;
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	// Converts the OS type
+	function get_os_type($key) {
+		static $os_type = array(
+			'0'   => 'FAT filesystem (MS-DOS, OS/2, NT/Win32)',
+			'1'   => 'Amiga',
+			'2'   => 'VMS (or OpenVMS)',
+			'3'   => 'Unix',
+			'4'   => 'VM/CMS',
+			'5'   => 'Atari TOS',
+			'6'   => 'HPFS filesystem (OS/2, NT)',
+			'7'   => 'Macintosh',
+			'8'   => 'Z-System',
+			'9'   => 'CP/M',
+			'10'  => 'TOPS-20',
+			'11'  => 'NTFS filesystem (NT)',
+			'12'  => 'QDOS',
+			'13'  => 'Acorn RISCOS',
+			'255' => 'unknown'
+		);
+		return @$os_type[$key];
+	}
+
+	// Converts the eXtra FLags
+	function get_xflag_type($key) {
+		static $xflag_type = array(
+			'0' => 'unknown',
+			'2' => 'maximum compression',
+			'4' => 'fastest algorithm'
+		);
+		return @$xflag_type[$key];
+	}
+}
+
+?>
diff --git a/apps/media/getID3/getid3/module.archive.rar.php b/apps/media/getID3/getid3/module.archive.rar.php
new file mode 100644
index 0000000000000000000000000000000000000000..59820b2fad90839c0b0a80401151ecab692cd4c1
--- /dev/null
+++ b/apps/media/getID3/getid3/module.archive.rar.php
@@ -0,0 +1,52 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.rar.php                                      //
+// module for analyzing RAR files                              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_rar
+{
+
+	var $option_use_rar_extension = false;
+
+	function getid3_rar(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat'] = 'rar';
+
+		if ($this->option_use_rar_extension === true) {
+			if (function_exists('rar_open')) {
+				if ($rp = rar_open($ThisFileInfo['filename'])) {
+					$ThisFileInfo['rar']['files'] = array();
+					$entries = rar_list($rp);
+					foreach ($entries as $entry) {
+						$ThisFileInfo['rar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['rar']['files'], getid3_lib::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize()));
+					}
+					rar_close($rp);
+					return true;
+				} else {
+					$ThisFileInfo['error'][] = 'failed to rar_open('.$ThisFileInfo['filename'].')';
+				}
+			} else {
+				$ThisFileInfo['error'][] = 'RAR support does not appear to be available in this PHP installation';
+			}
+		} else {
+			$ThisFileInfo['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)';
+		}
+		return false;
+
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.archive.szip.php b/apps/media/getID3/getid3/module.archive.szip.php
new file mode 100644
index 0000000000000000000000000000000000000000..2513c85c7b37e9efc20ba5a94d39fb57fb01e1c2
--- /dev/null
+++ b/apps/media/getID3/getid3/module.archive.szip.php
@@ -0,0 +1,97 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.szip.php                                     //
+// module for analyzing SZIP compressed files                  //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_szip
+{
+
+	function getid3_szip(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$SZIPHeader = fread($fd, 6);
+		if (substr($SZIPHeader, 0, 4) != 'SZ'."\x0A\x04") {
+			$ThisFileInfo['error'][] = 'Expecting "SZ[x0A][x04]" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($SZIPHeader, 0, 4).'"';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat']            = 'szip';
+
+		$ThisFileInfo['szip']['major_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 4, 1));
+		$ThisFileInfo['szip']['minor_version'] = getid3_lib::BigEndian2Int(substr($SZIPHeader, 5, 1));
+
+		while (!feof($fd)) {
+			$NextBlockID = fread($fd, 2);
+			switch ($NextBlockID) {
+				case 'SZ':
+					// Note that szip files can be concatenated, this has the same effect as
+					// concatenating the files. this also means that global header blocks
+					// might be present between directory/data blocks.
+					fseek($fd, 4, SEEK_CUR);
+					break;
+
+				case 'BH':
+					$BHheaderbytes  = getid3_lib::BigEndian2Int(fread($fd, 3));
+					$BHheaderdata   = fread($fd, $BHheaderbytes);
+					$BHheaderoffset = 0;
+					while (strpos($BHheaderdata, "\x00", $BHheaderoffset) > 0) {
+						//filename as \0 terminated string  (empty string indicates end)
+						//owner as \0 terminated string (empty is same as last file)
+						//group as \0 terminated string (empty is same as last file)
+						//3 byte filelength in this block
+						//2 byte access flags
+						//4 byte creation time (like in unix)
+						//4 byte modification time (like in unix)
+						//4 byte access time (like in unix)
+
+						$BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
+						$BHheaderoffset += (strlen($BHdataArray['filename']) + 1);
+
+						$BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
+						$BHheaderoffset += (strlen($BHdataArray['owner']) + 1);
+
+						$BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, "\x00"));
+						$BHheaderoffset += (strlen($BHdataArray['group']) + 1);
+
+						$BHdataArray['filelength'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
+						$BHheaderoffset += 3;
+
+						$BHdataArray['access_flags'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
+						$BHheaderoffset += 2;
+
+						$BHdataArray['creation_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
+						$BHheaderoffset += 4;
+
+						$BHdataArray['modification_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
+						$BHheaderoffset += 4;
+
+						$BHdataArray['access_time'] = getid3_lib::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
+						$BHheaderoffset += 4;
+
+						$ThisFileInfo['szip']['BH'][] = $BHdataArray;
+					}
+					break;
+
+				default:
+					break 2;
+			}
+		}
+
+		return true;
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.archive.tar.php b/apps/media/getID3/getid3/module.archive.tar.php
new file mode 100644
index 0000000000000000000000000000000000000000..aa4390fc9f92e401e092b4b0bc6219556137ba25
--- /dev/null
+++ b/apps/media/getID3/getid3/module.archive.tar.php
@@ -0,0 +1,175 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.tar.php                                      //
+// module for analyzing TAR files                              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// Module originally written by                                //
+//      Mike Mozolin <teddybearØmail*ru>                       //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_tar {
+
+	function getid3_tar(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat'] = 'tar';
+		$ThisFileInfo['tar']['files'] = array();
+
+		$unpack_header = 'a100fname/a8mode/a8uid/a8gid/a12size/a12mtime/a8chksum/a1typflag/a100lnkname/a6magic/a2ver/a32uname/a32gname/a8devmaj/a8devmin/a155prefix';
+		$null_512k = str_repeat("\x00", 512); // end-of-file marker
+
+		@fseek($fd, 0);
+		while (!feof($fd)) {
+			$buffer = fread($fd, 512);
+			if (strlen($buffer) < 512) {
+				break;
+			}
+
+			// check the block
+			$checksum = 0;
+			for ($i = 0; $i < 148; $i++) {
+				$checksum += ord($buffer{$i});
+			}
+			for ($i = 148; $i < 156; $i++) {
+				$checksum += ord(' ');
+			}
+			for ($i = 156; $i < 512; $i++) {
+				$checksum += ord($buffer{$i});
+			}
+			$attr    = unpack($unpack_header, $buffer);
+			$name    =        trim(@$attr['fname']);
+			$mode    = octdec(trim(@$attr['mode']));
+			$uid     = octdec(trim(@$attr['uid']));
+			$gid     = octdec(trim(@$attr['gid']));
+			$size    = octdec(trim(@$attr['size']));
+			$mtime   = octdec(trim(@$attr['mtime']));
+			$chksum  = octdec(trim(@$attr['chksum']));
+			$typflag =        trim(@$attr['typflag']);
+			$lnkname =        trim(@$attr['lnkname']);
+			$magic   =        trim(@$attr['magic']);
+			$ver     =        trim(@$attr['ver']);
+			$uname   =        trim(@$attr['uname']);
+			$gname   =        trim(@$attr['gname']);
+			$devmaj  = octdec(trim(@$attr['devmaj']));
+			$devmin  = octdec(trim(@$attr['devmin']));
+			$prefix  =        trim(@$attr['prefix']);
+			if (($checksum == 256) && ($chksum == 0)) {
+				// EOF Found
+				break;
+			}
+			if ($prefix) {
+				$name = $prefix.'/'.$name;
+			}
+			if ((preg_match('#/$#', $name)) && !$name) {
+				$typeflag = 5;
+			}
+			if ($buffer == $null_512k) {
+				// it's the end of the tar-file...
+				break;
+			}
+
+			// Read to the next chunk
+			fseek($fd, $size, SEEK_CUR);
+
+			$diff = $size % 512;
+			if ($diff != 0) {
+				// Padding, throw away
+				fseek($fd, (512 - $diff), SEEK_CUR);
+			}
+			// Protect against tar-files with garbage at the end
+			if ($name == '') {
+				break;
+			}
+			$ThisFileInfo['tar']['file_details'][$name] = array (
+				'name'     => $name,
+				'mode_raw' => $mode,
+				'mode'     => getid3_tar::display_perms($mode),
+				'uid'      => $uid,
+				'gid'      => $gid,
+				'size'     => $size,
+				'mtime'    => $mtime,
+				'chksum'   => $chksum,
+				'typeflag' => getid3_tar::get_flag_type($typflag),
+				'linkname' => $lnkname,
+				'magic'    => $magic,
+				'version'  => $ver,
+				'uname'    => $uname,
+				'gname'    => $gname,
+				'devmajor' => $devmaj,
+				'devminor' => $devmin
+			);
+			$ThisFileInfo['tar']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['tar']['files'], getid3_lib::CreateDeepArray($ThisFileInfo['tar']['file_details'][$name]['name'], '/', $size));
+		}
+		return true;
+	}
+
+	// Parses the file mode to file permissions
+	function display_perms($mode) {
+		// Determine Type
+		if     ($mode & 0x1000) $type='p'; // FIFO pipe
+		elseif ($mode & 0x2000) $type='c'; // Character special
+		elseif ($mode & 0x4000) $type='d'; // Directory
+		elseif ($mode & 0x6000) $type='b'; // Block special
+		elseif ($mode & 0x8000) $type='-'; // Regular
+		elseif ($mode & 0xA000) $type='l'; // Symbolic Link
+		elseif ($mode & 0xC000) $type='s'; // Socket
+		else                    $type='u'; // UNKNOWN
+
+		// Determine permissions
+		$owner['read']    = (($mode & 00400) ? 'r' : '-');
+		$owner['write']   = (($mode & 00200) ? 'w' : '-');
+		$owner['execute'] = (($mode & 00100) ? 'x' : '-');
+		$group['read']    = (($mode & 00040) ? 'r' : '-');
+		$group['write']   = (($mode & 00020) ? 'w' : '-');
+		$group['execute'] = (($mode & 00010) ? 'x' : '-');
+		$world['read']    = (($mode & 00004) ? 'r' : '-');
+		$world['write']   = (($mode & 00002) ? 'w' : '-');
+		$world['execute'] = (($mode & 00001) ? 'x' : '-');
+
+		// Adjust for SUID, SGID and sticky bit
+		if ($mode & 0x800) $owner['execute'] = ($owner['execute'] == 'x') ? 's' : 'S';
+		if ($mode & 0x400) $group['execute'] = ($group['execute'] == 'x') ? 's' : 'S';
+		if ($mode & 0x200) $world['execute'] = ($world['execute'] == 'x') ? 't' : 'T';
+
+		$s  = sprintf('%1s', $type);
+		$s .= sprintf('%1s%1s%1s',      $owner['read'], $owner['write'], $owner['execute']);
+		$s .= sprintf('%1s%1s%1s',      $group['read'], $group['write'], $group['execute']);
+		$s .= sprintf('%1s%1s%1s'."\n", $world['read'], $world['write'], $world['execute']);
+		return $s;
+	}
+
+	// Converts the file type
+	function get_flag_type($typflag) {
+		static $flag_types = array(
+			'0' => 'LF_NORMAL',
+			'1' => 'LF_LINK',
+			'2' => 'LF_SYNLINK',
+			'3' => 'LF_CHR',
+			'4' => 'LF_BLK',
+			'5' => 'LF_DIR',
+			'6' => 'LF_FIFO',
+			'7' => 'LF_CONFIG',
+			'D' => 'LF_DUMPDIR',
+			'K' => 'LF_LONGLINK',
+			'L' => 'LF_LONGNAME',
+			'M' => 'LF_MULTIVOL',
+			'N' => 'LF_NAMES',
+			'S' => 'LF_SPARSE',
+			'V' => 'LF_VOLHDR'
+		);
+		return @$flag_types[$typflag];
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.archive.zip.php b/apps/media/getID3/getid3/module.archive.zip.php
new file mode 100644
index 0000000000000000000000000000000000000000..1d2be0691f8149a92e49bb2607db597d1608c11b
--- /dev/null
+++ b/apps/media/getID3/getid3/module.archive.zip.php
@@ -0,0 +1,416 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.zip.php                                      //
+// module for analyzing pkZip files                            //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_zip
+{
+
+	function getid3_zip(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat']      = 'zip';
+		$ThisFileInfo['zip']['encoding'] = 'ISO-8859-1';
+		$ThisFileInfo['zip']['files']    = array();
+
+		$ThisFileInfo['zip']['compressed_size']   = 0;
+		$ThisFileInfo['zip']['uncompressed_size'] = 0;
+		$ThisFileInfo['zip']['entries_count']     = 0;
+
+		if ($ThisFileInfo['filesize'] < pow(2, 31)) {
+			$EOCDsearchData    = '';
+			$EOCDsearchCounter = 0;
+			while ($EOCDsearchCounter++ < 512) {
+
+				fseek($fd, -128 * $EOCDsearchCounter, SEEK_END);
+				$EOCDsearchData = fread($fd, 128).$EOCDsearchData;
+
+				if (strstr($EOCDsearchData, 'PK'."\x05\x06")) {
+
+					$EOCDposition = strpos($EOCDsearchData, 'PK'."\x05\x06");
+					fseek($fd, (-128 * $EOCDsearchCounter) + $EOCDposition, SEEK_END);
+					$ThisFileInfo['zip']['end_central_directory'] = $this->ZIPparseEndOfCentralDirectory($fd);
+
+					fseek($fd, $ThisFileInfo['zip']['end_central_directory']['directory_offset'], SEEK_SET);
+					$ThisFileInfo['zip']['entries_count'] = 0;
+					while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) {
+						$ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry;
+						$ThisFileInfo['zip']['entries_count']++;
+						$ThisFileInfo['zip']['compressed_size']   += $centraldirectoryentry['compressed_size'];
+						$ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
+
+						if ($centraldirectoryentry['uncompressed_size'] > 0) {
+							$ThisFileInfo['zip']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['zip']['files'], getid3_lib::CreateDeepArray($centraldirectoryentry['filename'], '/', $centraldirectoryentry['uncompressed_size']));
+						}
+					}
+
+					if ($ThisFileInfo['zip']['entries_count'] == 0) {
+						$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
+						return false;
+					}
+
+					if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
+						$ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment'];
+					}
+
+					if (isset($ThisFileInfo['zip']['central_directory'][0]['compression_method'])) {
+						$ThisFileInfo['zip']['compression_method'] = $ThisFileInfo['zip']['central_directory'][0]['compression_method'];
+					}
+					if (isset($ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'])) {
+						$ThisFileInfo['zip']['compression_speed']  = $ThisFileInfo['zip']['central_directory'][0]['flags']['compression_speed'];
+					}
+					if (isset($ThisFileInfo['zip']['compression_method']) && ($ThisFileInfo['zip']['compression_method'] == 'store') && !isset($ThisFileInfo['zip']['compression_speed'])) {
+						$ThisFileInfo['zip']['compression_speed']  = 'store';
+					}
+
+					return true;
+
+				}
+			}
+		}
+
+		if ($this->getZIPentriesFilepointer($fd, $ThisFileInfo)) {
+
+			// central directory couldn't be found and/or parsed
+			// scan through actual file data entries, recover as much as possible from probable trucated file
+			if ($ThisFileInfo['zip']['compressed_size'] > ($ThisFileInfo['filesize'] - 46 - 22)) {
+				$ThisFileInfo['error'][] = 'Warning: Truncated file! - Total compressed file sizes ('.$ThisFileInfo['zip']['compressed_size'].' bytes) is greater than filesize minus Central Directory and End Of Central Directory structures ('.($ThisFileInfo['filesize'] - 46 - 22).' bytes)';
+			}
+			$ThisFileInfo['error'][] = 'Cannot find End Of Central Directory - returned list of files in [zip][entries] array may not be complete';
+			foreach ($ThisFileInfo['zip']['entries'] as $key => $valuearray) {
+				$ThisFileInfo['zip']['files'][$valuearray['filename']] = $valuearray['uncompressed_size'];
+			}
+			return true;
+
+		} else {
+
+			unset($ThisFileInfo['zip']);
+			$ThisFileInfo['fileformat'] = '';
+			$ThisFileInfo['error'][] = 'Cannot find End Of Central Directory (truncated file?)';
+			return false;
+
+		}
+	}
+
+
+	function getZIPHeaderFilepointerTopDown(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat'] = 'zip';
+
+		$ThisFileInfo['zip']['compressed_size']   = 0;
+		$ThisFileInfo['zip']['uncompressed_size'] = 0;
+		$ThisFileInfo['zip']['entries_count']     = 0;
+
+		rewind($fd);
+		while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
+			$ThisFileInfo['zip']['entries'][] = $fileentry;
+			$ThisFileInfo['zip']['entries_count']++;
+		}
+		if ($ThisFileInfo['zip']['entries_count'] == 0) {
+			$ThisFileInfo['error'][] = 'No Local File Header entries found';
+			return false;
+		}
+
+		$ThisFileInfo['zip']['entries_count']     = 0;
+		while ($centraldirectoryentry = $this->ZIPparseCentralDirectory($fd)) {
+			$ThisFileInfo['zip']['central_directory'][] = $centraldirectoryentry;
+			$ThisFileInfo['zip']['entries_count']++;
+			$ThisFileInfo['zip']['compressed_size']   += $centraldirectoryentry['compressed_size'];
+			$ThisFileInfo['zip']['uncompressed_size'] += $centraldirectoryentry['uncompressed_size'];
+		}
+		if ($ThisFileInfo['zip']['entries_count'] == 0) {
+			$ThisFileInfo['error'][] = 'No Central Directory entries found (truncated file?)';
+			return false;
+		}
+
+		if ($EOCD = $this->ZIPparseEndOfCentralDirectory($fd)) {
+			$ThisFileInfo['zip']['end_central_directory'] = $EOCD;
+		} else {
+			$ThisFileInfo['error'][] = 'No End Of Central Directory entry found (truncated file?)';
+			return false;
+		}
+
+		if (!empty($ThisFileInfo['zip']['end_central_directory']['comment'])) {
+			$ThisFileInfo['zip']['comments']['comment'][] = $ThisFileInfo['zip']['end_central_directory']['comment'];
+		}
+
+		return true;
+	}
+
+
+	function getZIPentriesFilepointer(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['zip']['compressed_size']   = 0;
+		$ThisFileInfo['zip']['uncompressed_size'] = 0;
+		$ThisFileInfo['zip']['entries_count']     = 0;
+
+		rewind($fd);
+		while ($fileentry = $this->ZIPparseLocalFileHeader($fd)) {
+			$ThisFileInfo['zip']['entries'][] = $fileentry;
+			$ThisFileInfo['zip']['entries_count']++;
+			$ThisFileInfo['zip']['compressed_size']   += $fileentry['compressed_size'];
+			$ThisFileInfo['zip']['uncompressed_size'] += $fileentry['uncompressed_size'];
+		}
+		if ($ThisFileInfo['zip']['entries_count'] == 0) {
+			$ThisFileInfo['error'][] = 'No Local File Header entries found';
+			return false;
+		}
+
+		return true;
+	}
+
+
+	function ZIPparseLocalFileHeader(&$fd) {
+		$LocalFileHeader['offset'] = ftell($fd);
+
+		$ZIPlocalFileHeader = fread($fd, 30);
+
+		$LocalFileHeader['raw']['signature']          = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader,  0, 4));
+		if ($LocalFileHeader['raw']['signature'] != 0x04034B50) {
+			// invalid Local File Header Signature
+			fseek($fd, $LocalFileHeader['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
+			return false;
+		}
+		$LocalFileHeader['raw']['extract_version']    = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader,  4, 2));
+		$LocalFileHeader['raw']['general_flags']      = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader,  6, 2));
+		$LocalFileHeader['raw']['compression_method'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader,  8, 2));
+		$LocalFileHeader['raw']['last_mod_file_time'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 10, 2));
+		$LocalFileHeader['raw']['last_mod_file_date'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 12, 2));
+		$LocalFileHeader['raw']['crc_32']             = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 14, 4));
+		$LocalFileHeader['raw']['compressed_size']    = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 18, 4));
+		$LocalFileHeader['raw']['uncompressed_size']  = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 22, 4));
+		$LocalFileHeader['raw']['filename_length']    = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 26, 2));
+		$LocalFileHeader['raw']['extra_field_length'] = getid3_lib::LittleEndian2Int(substr($ZIPlocalFileHeader, 28, 2));
+
+		$LocalFileHeader['extract_version']           = sprintf('%1.1f', $LocalFileHeader['raw']['extract_version'] / 10);
+		$LocalFileHeader['host_os']                   = $this->ZIPversionOSLookup(($LocalFileHeader['raw']['extract_version'] & 0xFF00) >> 8);
+		$LocalFileHeader['compression_method']        = $this->ZIPcompressionMethodLookup($LocalFileHeader['raw']['compression_method']);
+		$LocalFileHeader['compressed_size']           = $LocalFileHeader['raw']['compressed_size'];
+		$LocalFileHeader['uncompressed_size']         = $LocalFileHeader['raw']['uncompressed_size'];
+		$LocalFileHeader['flags']                     = $this->ZIPparseGeneralPurposeFlags($LocalFileHeader['raw']['general_flags'], $LocalFileHeader['raw']['compression_method']);
+		$LocalFileHeader['last_modified_timestamp']   = $this->DOStime2UNIXtime($LocalFileHeader['raw']['last_mod_file_date'], $LocalFileHeader['raw']['last_mod_file_time']);
+
+		$FilenameExtrafieldLength = $LocalFileHeader['raw']['filename_length'] + $LocalFileHeader['raw']['extra_field_length'];
+		if ($FilenameExtrafieldLength > 0) {
+			$ZIPlocalFileHeader .= fread($fd, $FilenameExtrafieldLength);
+
+			if ($LocalFileHeader['raw']['filename_length'] > 0) {
+				$LocalFileHeader['filename']                = substr($ZIPlocalFileHeader, 30, $LocalFileHeader['raw']['filename_length']);
+			}
+			if ($LocalFileHeader['raw']['extra_field_length'] > 0) {
+				$LocalFileHeader['raw']['extra_field_data'] = substr($ZIPlocalFileHeader, 30 + $LocalFileHeader['raw']['filename_length'], $LocalFileHeader['raw']['extra_field_length']);
+			}
+		}
+
+		$LocalFileHeader['data_offset'] = ftell($fd);
+		//$LocalFileHeader['compressed_data'] = fread($fd, $LocalFileHeader['raw']['compressed_size']);
+		fseek($fd, $LocalFileHeader['raw']['compressed_size'], SEEK_CUR);
+
+		if ($LocalFileHeader['flags']['data_descriptor_used']) {
+			$DataDescriptor = fread($fd, 12);
+			$LocalFileHeader['data_descriptor']['crc_32']            = getid3_lib::LittleEndian2Int(substr($DataDescriptor,  0, 4));
+			$LocalFileHeader['data_descriptor']['compressed_size']   = getid3_lib::LittleEndian2Int(substr($DataDescriptor,  4, 4));
+			$LocalFileHeader['data_descriptor']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($DataDescriptor,  8, 4));
+		}
+
+		return $LocalFileHeader;
+	}
+
+
+	function ZIPparseCentralDirectory(&$fd) {
+		$CentralDirectory['offset'] = ftell($fd);
+
+		$ZIPcentralDirectory = fread($fd, 46);
+
+		$CentralDirectory['raw']['signature']            = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory,  0, 4));
+		if ($CentralDirectory['raw']['signature'] != 0x02014B50) {
+			// invalid Central Directory Signature
+			fseek($fd, $CentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
+			return false;
+		}
+		$CentralDirectory['raw']['create_version']       = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory,  4, 2));
+		$CentralDirectory['raw']['extract_version']      = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory,  6, 2));
+		$CentralDirectory['raw']['general_flags']        = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory,  8, 2));
+		$CentralDirectory['raw']['compression_method']   = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 10, 2));
+		$CentralDirectory['raw']['last_mod_file_time']   = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 12, 2));
+		$CentralDirectory['raw']['last_mod_file_date']   = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 14, 2));
+		$CentralDirectory['raw']['crc_32']               = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 16, 4));
+		$CentralDirectory['raw']['compressed_size']      = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 20, 4));
+		$CentralDirectory['raw']['uncompressed_size']    = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 24, 4));
+		$CentralDirectory['raw']['filename_length']      = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 28, 2));
+		$CentralDirectory['raw']['extra_field_length']   = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 30, 2));
+		$CentralDirectory['raw']['file_comment_length']  = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 32, 2));
+		$CentralDirectory['raw']['disk_number_start']    = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 34, 2));
+		$CentralDirectory['raw']['internal_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 36, 2));
+		$CentralDirectory['raw']['external_file_attrib'] = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 38, 4));
+		$CentralDirectory['raw']['local_header_offset']  = getid3_lib::LittleEndian2Int(substr($ZIPcentralDirectory, 42, 4));
+
+		$CentralDirectory['entry_offset']              = $CentralDirectory['raw']['local_header_offset'];
+		$CentralDirectory['create_version']            = sprintf('%1.1f', $CentralDirectory['raw']['create_version'] / 10);
+		$CentralDirectory['extract_version']           = sprintf('%1.1f', $CentralDirectory['raw']['extract_version'] / 10);
+		$CentralDirectory['host_os']                   = $this->ZIPversionOSLookup(($CentralDirectory['raw']['extract_version'] & 0xFF00) >> 8);
+		$CentralDirectory['compression_method']        = $this->ZIPcompressionMethodLookup($CentralDirectory['raw']['compression_method']);
+		$CentralDirectory['compressed_size']           = $CentralDirectory['raw']['compressed_size'];
+		$CentralDirectory['uncompressed_size']         = $CentralDirectory['raw']['uncompressed_size'];
+		$CentralDirectory['flags']                     = $this->ZIPparseGeneralPurposeFlags($CentralDirectory['raw']['general_flags'], $CentralDirectory['raw']['compression_method']);
+		$CentralDirectory['last_modified_timestamp']   = $this->DOStime2UNIXtime($CentralDirectory['raw']['last_mod_file_date'], $CentralDirectory['raw']['last_mod_file_time']);
+
+		$FilenameExtrafieldCommentLength = $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'] + $CentralDirectory['raw']['file_comment_length'];
+		if ($FilenameExtrafieldCommentLength > 0) {
+			$FilenameExtrafieldComment = fread($fd, $FilenameExtrafieldCommentLength);
+
+			if ($CentralDirectory['raw']['filename_length'] > 0) {
+				$CentralDirectory['filename']                  = substr($FilenameExtrafieldComment, 0, $CentralDirectory['raw']['filename_length']);
+			}
+			if ($CentralDirectory['raw']['extra_field_length'] > 0) {
+				$CentralDirectory['raw']['extra_field_data']   = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'], $CentralDirectory['raw']['extra_field_length']);
+			}
+			if ($CentralDirectory['raw']['file_comment_length'] > 0) {
+				$CentralDirectory['file_comment']              = substr($FilenameExtrafieldComment, $CentralDirectory['raw']['filename_length'] + $CentralDirectory['raw']['extra_field_length'], $CentralDirectory['raw']['file_comment_length']);
+			}
+		}
+
+		return $CentralDirectory;
+	}
+
+	function ZIPparseEndOfCentralDirectory(&$fd) {
+		$EndOfCentralDirectory['offset'] = ftell($fd);
+
+		$ZIPendOfCentralDirectory = fread($fd, 22);
+
+		$EndOfCentralDirectory['signature']                   = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory,  0, 4));
+		if ($EndOfCentralDirectory['signature'] != 0x06054B50) {
+			// invalid End Of Central Directory Signature
+			fseek($fd, $EndOfCentralDirectory['offset'], SEEK_SET); // seek back to where filepointer originally was so it can be handled properly
+			return false;
+		}
+		$EndOfCentralDirectory['disk_number_current']         = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory,  4, 2));
+		$EndOfCentralDirectory['disk_number_start_directory'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory,  6, 2));
+		$EndOfCentralDirectory['directory_entries_this_disk'] = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory,  8, 2));
+		$EndOfCentralDirectory['directory_entries_total']     = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
+		$EndOfCentralDirectory['directory_size']              = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
+		$EndOfCentralDirectory['directory_offset']            = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
+		$EndOfCentralDirectory['comment_length']              = getid3_lib::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
+
+		if ($EndOfCentralDirectory['comment_length'] > 0) {
+			$EndOfCentralDirectory['comment']                 = fread($fd, $EndOfCentralDirectory['comment_length']);
+		}
+
+		return $EndOfCentralDirectory;
+	}
+
+
+	function ZIPparseGeneralPurposeFlags($flagbytes, $compressionmethod) {
+		$ParsedFlags['encrypted'] = (bool) ($flagbytes & 0x0001);
+
+		switch ($compressionmethod) {
+			case 6:
+				$ParsedFlags['dictionary_size']    = (($flagbytes & 0x0002) ? 8192 : 4096);
+				$ParsedFlags['shannon_fano_trees'] = (($flagbytes & 0x0004) ? 3    : 2);
+				break;
+
+			case 8:
+			case 9:
+				switch (($flagbytes & 0x0006) >> 1) {
+					case 0:
+						$ParsedFlags['compression_speed'] = 'normal';
+						break;
+					case 1:
+						$ParsedFlags['compression_speed'] = 'maximum';
+						break;
+					case 2:
+						$ParsedFlags['compression_speed'] = 'fast';
+						break;
+					case 3:
+						$ParsedFlags['compression_speed'] = 'superfast';
+						break;
+				}
+				break;
+		}
+		$ParsedFlags['data_descriptor_used']       = (bool) ($flagbytes & 0x0008);
+
+		return $ParsedFlags;
+	}
+
+
+	function ZIPversionOSLookup($index) {
+		static $ZIPversionOSLookup = array(
+			0  => 'MS-DOS and OS/2 (FAT / VFAT / FAT32 file systems)',
+			1  => 'Amiga',
+			2  => 'OpenVMS',
+			3  => 'Unix',
+			4  => 'VM/CMS',
+			5  => 'Atari ST',
+			6  => 'OS/2 H.P.F.S.',
+			7  => 'Macintosh',
+			8  => 'Z-System',
+			9  => 'CP/M',
+			10 => 'Windows NTFS',
+			11 => 'MVS',
+			12 => 'VSE',
+			13 => 'Acorn Risc',
+			14 => 'VFAT',
+			15 => 'Alternate MVS',
+			16 => 'BeOS',
+			17 => 'Tandem'
+		);
+
+		return (isset($ZIPversionOSLookup[$index]) ? $ZIPversionOSLookup[$index] : '[unknown]');
+	}
+
+	function ZIPcompressionMethodLookup($index) {
+		static $ZIPcompressionMethodLookup = array(
+			0  => 'store',
+			1  => 'shrink',
+			2  => 'reduce-1',
+			3  => 'reduce-2',
+			4  => 'reduce-3',
+			5  => 'reduce-4',
+			6  => 'implode',
+			7  => 'tokenize',
+			8  => 'deflate',
+			9  => 'deflate64',
+			10 => 'PKWARE Date Compression Library Imploding'
+		);
+
+		return (isset($ZIPcompressionMethodLookup[$index]) ? $ZIPcompressionMethodLookup[$index] : '[unknown]');
+	}
+
+	function DOStime2UNIXtime($DOSdate, $DOStime) {
+		// wFatDate
+		// Specifies the MS-DOS date. The date is a packed 16-bit value with the following format:
+		// Bits      Contents
+		// 0-4    Day of the month (1-31)
+		// 5-8    Month (1 = January, 2 = February, and so on)
+		// 9-15   Year offset from 1980 (add 1980 to get actual year)
+
+		$UNIXday    =  ($DOSdate & 0x001F);
+		$UNIXmonth  = (($DOSdate & 0x01E0) >> 5);
+		$UNIXyear   = (($DOSdate & 0xFE00) >> 9) + 1980;
+
+		// wFatTime
+		// Specifies the MS-DOS time. The time is a packed 16-bit value with the following format:
+		// Bits   Contents
+		// 0-4    Second divided by 2
+		// 5-10   Minute (0-59)
+		// 11-15  Hour (0-23 on a 24-hour clock)
+
+		$UNIXsecond =  ($DOStime & 0x001F) * 2;
+		$UNIXminute = (($DOStime & 0x07E0) >> 5);
+		$UNIXhour   = (($DOStime & 0xF800) >> 11);
+
+		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.asf.php b/apps/media/getID3/getid3/module.audio-video.asf.php
new file mode 100644
index 0000000000000000000000000000000000000000..4526291be236c5fb7edd9d2214e20d1d4cd23433
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.asf.php
@@ -0,0 +1,1673 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.asf.php                                  //
+// module for analyzing ASF, WMA and WMV files                 //
+// dependencies: module.audio-video.riff.php                   //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+$GUIDarray = getid3_asf::KnownGUIDs();
+foreach ($GUIDarray as $GUIDname => $hexstringvalue) {
+	// initialize all GUID constants
+	define($GUIDname, getid3_asf::GUIDtoBytestring($hexstringvalue));
+}
+
+
+
+class getid3_asf
+{
+
+	function getid3_asf(&$fd, &$ThisFileInfo) {
+
+		// Shortcuts
+		$thisfile_audio = &$ThisFileInfo['audio'];
+		$thisfile_video = &$ThisFileInfo['video'];
+		$ThisFileInfo['asf'] = array();
+		$thisfile_asf        = &$ThisFileInfo['asf'];
+		$thisfile_asf['comments'] = array();
+		$thisfile_asf_comments    = &$thisfile_asf['comments'];
+		$thisfile_asf['header_object'] = array();
+		$thisfile_asf_headerobject     = &$thisfile_asf['header_object'];
+
+
+		// ASF structure:
+		// * Header Object [required]
+		//   * File Properties Object [required]   (global file attributes)
+		//   * Stream Properties Object [required] (defines media stream & characteristics)
+		//   * Header Extension Object [required]  (additional functionality)
+		//   * Content Description Object          (bibliographic information)
+		//   * Script Command Object               (commands for during playback)
+		//   * Marker Object                       (named jumped points within the file)
+		// * Data Object [required]
+		//   * Data Packets
+		// * Index Object
+
+		// Header Object: (mandatory, one only)
+		// Field Name                   Field Type   Size (bits)
+		// Object ID                    GUID         128             // GUID for header object - GETID3_ASF_Header_Object
+		// Object Size                  QWORD        64              // size of header object, including 30 bytes of Header Object header
+		// Number of Header Objects     DWORD        32              // number of objects in header object
+		// Reserved1                    BYTE         8               // hardcoded: 0x01
+		// Reserved2                    BYTE         8               // hardcoded: 0x02
+
+		$ThisFileInfo['fileformat'] = 'asf';
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$HeaderObjectData = fread($fd, 30);
+
+		$thisfile_asf_headerobject['objectid']      = substr($HeaderObjectData, 0, 16);
+		$thisfile_asf_headerobject['objectid_guid'] = $this->BytestringToGUID($thisfile_asf_headerobject['objectid']);
+		if ($thisfile_asf_headerobject['objectid'] != GETID3_ASF_Header_Object) {
+			$ThisFileInfo['warning'][] = 'ASF header GUID {'.$this->BytestringToGUID($thisfile_asf_headerobject['objectid']).'} does not match expected "GETID3_ASF_Header_Object" GUID {'.$this->BytestringToGUID(GETID3_ASF_Header_Object).'}';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['asf']);
+			return false;
+			break;
+		}
+		$thisfile_asf_headerobject['objectsize']    = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 16, 8));
+		$thisfile_asf_headerobject['headerobjects'] = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 24, 4));
+		$thisfile_asf_headerobject['reserved1']     = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 28, 1));
+		$thisfile_asf_headerobject['reserved2']     = getid3_lib::LittleEndian2Int(substr($HeaderObjectData, 29, 1));
+
+		$ASFHeaderData = fread($fd, $thisfile_asf_headerobject['objectsize'] - 30);
+		$offset = 0;
+
+		for ($HeaderObjectsCounter = 0; $HeaderObjectsCounter < $thisfile_asf_headerobject['headerobjects']; $HeaderObjectsCounter++) {
+			$NextObjectGUID     = substr($ASFHeaderData, $offset, 16);
+			$offset += 16;
+			$NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID);
+			$NextObjectSize = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+			$offset += 8;
+			switch ($NextObjectGUID) {
+
+				case GETID3_ASF_File_Properties_Object:
+					// File Properties Object: (mandatory, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for file properties object - GETID3_ASF_File_Properties_Object
+					// Object Size                  QWORD        64              // size of file properties object, including 104 bytes of File Properties Object header
+					// File ID                      GUID         128             // unique ID - identical to File ID in Data Object
+					// File Size                    QWORD        64              // entire file in bytes. Invalid if Broadcast Flag == 1
+					// Creation Date                QWORD        64              // date & time of file creation. Maybe invalid if Broadcast Flag == 1
+					// Data Packets Count           QWORD        64              // number of data packets in Data Object. Invalid if Broadcast Flag == 1
+					// Play Duration                QWORD        64              // playtime, in 100-nanosecond units. Invalid if Broadcast Flag == 1
+					// Send Duration                QWORD        64              // time needed to send file, in 100-nanosecond units. Players can ignore this value. Invalid if Broadcast Flag == 1
+					// Preroll                      QWORD        64              // time to buffer data before starting to play file, in 1-millisecond units. If <> 0, PlayDuration and PresentationTime have been offset by this amount
+					// Flags                        DWORD        32              //
+					// * Broadcast Flag             bits         1  (0x01)       // file is currently being written, some header values are invalid
+					// * Seekable Flag              bits         1  (0x02)       // is file seekable
+					// * Reserved                   bits         30 (0xFFFFFFFC) // reserved - set to zero
+					// Minimum Data Packet Size     DWORD        32              // in bytes. should be same as Maximum Data Packet Size. Invalid if Broadcast Flag == 1
+					// Maximum Data Packet Size     DWORD        32              // in bytes. should be same as Minimum Data Packet Size. Invalid if Broadcast Flag == 1
+					// Maximum Bitrate              DWORD        32              // maximum instantaneous bitrate in bits per second for entire file, including all data streams and ASF overhead
+
+					// shortcut
+					$thisfile_asf['file_properties_object'] = array();
+					$thisfile_asf_filepropertiesobject      = &$thisfile_asf['file_properties_object'];
+
+					$thisfile_asf_filepropertiesobject['objectid']           = $NextObjectGUID;
+					$thisfile_asf_filepropertiesobject['objectid_guid']      = $NextObjectGUIDtext;
+					$thisfile_asf_filepropertiesobject['objectsize']         = $NextObjectSize;
+					$thisfile_asf_filepropertiesobject['fileid']             = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_filepropertiesobject['fileid_guid']        = $this->BytestringToGUID($thisfile_asf_filepropertiesobject['fileid']);
+					$thisfile_asf_filepropertiesobject['filesize']           = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_filepropertiesobject['creation_date']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$thisfile_asf_filepropertiesobject['creation_date_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_filepropertiesobject['creation_date']);
+					$offset += 8;
+					$thisfile_asf_filepropertiesobject['data_packets']       = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_filepropertiesobject['play_duration']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_filepropertiesobject['send_duration']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_filepropertiesobject['preroll']            = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_filepropertiesobject['flags_raw']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_filepropertiesobject['flags']['broadcast'] = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0001);
+					$thisfile_asf_filepropertiesobject['flags']['seekable']  = (bool) ($thisfile_asf_filepropertiesobject['flags_raw'] & 0x0002);
+
+					$thisfile_asf_filepropertiesobject['min_packet_size']    = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_filepropertiesobject['max_packet_size']    = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_filepropertiesobject['max_bitrate']        = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+
+					if ($thisfile_asf_filepropertiesobject['flags']['broadcast']) {
+
+						// broadcast flag is set, some values invalid
+						unset($thisfile_asf_filepropertiesobject['filesize']);
+						unset($thisfile_asf_filepropertiesobject['data_packets']);
+						unset($thisfile_asf_filepropertiesobject['play_duration']);
+						unset($thisfile_asf_filepropertiesobject['send_duration']);
+						unset($thisfile_asf_filepropertiesobject['min_packet_size']);
+						unset($thisfile_asf_filepropertiesobject['max_packet_size']);
+
+					} else {
+
+						// broadcast flag NOT set, perform calculations
+						$ThisFileInfo['playtime_seconds'] = ($thisfile_asf_filepropertiesobject['play_duration'] / 10000000) - ($thisfile_asf_filepropertiesobject['preroll'] / 1000);
+
+						//$ThisFileInfo['bitrate'] = $thisfile_asf_filepropertiesobject['max_bitrate'];
+						$ThisFileInfo['bitrate'] = ((isset($thisfile_asf_filepropertiesobject['filesize']) ? $thisfile_asf_filepropertiesobject['filesize'] : $ThisFileInfo['filesize']) * 8) / $ThisFileInfo['playtime_seconds'];
+					}
+					break;
+
+				case GETID3_ASF_Stream_Properties_Object:
+					// Stream Properties Object: (mandatory, one per media stream)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for stream properties object - GETID3_ASF_Stream_Properties_Object
+					// Object Size                  QWORD        64              // size of stream properties object, including 78 bytes of Stream Properties Object header
+					// Stream Type                  GUID         128             // GETID3_ASF_Audio_Media, GETID3_ASF_Video_Media or GETID3_ASF_Command_Media
+					// Error Correction Type        GUID         128             // GETID3_ASF_Audio_Spread for audio-only streams, GETID3_ASF_No_Error_Correction for other stream types
+					// Time Offset                  QWORD        64              // 100-nanosecond units. typically zero. added to all timestamps of samples in the stream
+					// Type-Specific Data Length    DWORD        32              // number of bytes for Type-Specific Data field
+					// Error Correction Data Length DWORD        32              // number of bytes for Error Correction Data field
+					// Flags                        WORD         16              //
+					// * Stream Number              bits         7 (0x007F)      // number of this stream.  1 <= valid <= 127
+					// * Reserved                   bits         8 (0x7F80)      // reserved - set to zero
+					// * Encrypted Content Flag     bits         1 (0x8000)      // stream contents encrypted if set
+					// Reserved                     DWORD        32              // reserved - set to zero
+					// Type-Specific Data           BYTESTREAM   variable        // type-specific format data, depending on value of Stream Type
+					// Error Correction Data        BYTESTREAM   variable        // error-correction-specific format data, depending on value of Error Correct Type
+
+					// There is one GETID3_ASF_Stream_Properties_Object for each stream (audio, video) but the
+					// stream number isn't known until halfway through decoding the structure, hence it
+					// it is decoded to a temporary variable and then stuck in the appropriate index later
+
+					$StreamPropertiesObjectData['objectid']           = $NextObjectGUID;
+					$StreamPropertiesObjectData['objectid_guid']      = $NextObjectGUIDtext;
+					$StreamPropertiesObjectData['objectsize']         = $NextObjectSize;
+					$StreamPropertiesObjectData['stream_type']        = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$StreamPropertiesObjectData['stream_type_guid']   = $this->BytestringToGUID($StreamPropertiesObjectData['stream_type']);
+					$StreamPropertiesObjectData['error_correct_type'] = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$StreamPropertiesObjectData['error_correct_guid'] = $this->BytestringToGUID($StreamPropertiesObjectData['error_correct_type']);
+					$StreamPropertiesObjectData['time_offset']        = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+					$offset += 8;
+					$StreamPropertiesObjectData['type_data_length']   = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$StreamPropertiesObjectData['error_data_length']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$StreamPropertiesObjectData['flags_raw']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$StreamPropertiesObjectStreamNumber               = $StreamPropertiesObjectData['flags_raw'] & 0x007F;
+					$StreamPropertiesObjectData['flags']['encrypted'] = (bool) ($StreamPropertiesObjectData['flags_raw'] & 0x8000);
+
+					$offset += 4; // reserved - DWORD
+					$StreamPropertiesObjectData['type_specific_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['type_data_length']);
+					$offset += $StreamPropertiesObjectData['type_data_length'];
+					$StreamPropertiesObjectData['error_correct_data'] = substr($ASFHeaderData, $offset, $StreamPropertiesObjectData['error_data_length']);
+					$offset += $StreamPropertiesObjectData['error_data_length'];
+
+					switch ($StreamPropertiesObjectData['stream_type']) {
+
+						case GETID3_ASF_Audio_Media:
+							$thisfile_audio['dataformat']   = (!empty($thisfile_audio['dataformat'])   ? $thisfile_audio['dataformat']   : 'asf');
+							$thisfile_audio['bitrate_mode'] = (!empty($thisfile_audio['bitrate_mode']) ? $thisfile_audio['bitrate_mode'] : 'cbr');
+
+							$audiodata = getid3_riff::RIFFparseWAVEFORMATex(substr($StreamPropertiesObjectData['type_specific_data'], 0, 16));
+							unset($audiodata['raw']);
+							$thisfile_audio = getid3_lib::array_merge_noclobber($audiodata, $thisfile_audio);
+							break;
+
+						case GETID3_ASF_Video_Media:
+							$thisfile_video['dataformat']   = (!empty($thisfile_video['dataformat'])   ? $thisfile_video['dataformat']   : 'asf');
+							$thisfile_video['bitrate_mode'] = (!empty($thisfile_video['bitrate_mode']) ? $thisfile_video['bitrate_mode'] : 'cbr');
+							break;
+
+						case GETID3_ASF_Command_Media:
+						default:
+							// do nothing
+							break;
+
+					}
+
+					$thisfile_asf['stream_properties_object'][$StreamPropertiesObjectStreamNumber] = $StreamPropertiesObjectData;
+					unset($StreamPropertiesObjectData); // clear for next stream, if any
+					break;
+
+				case GETID3_ASF_Header_Extension_Object:
+					// Header Extension Object: (mandatory, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Header Extension object - GETID3_ASF_Header_Extension_Object
+					// Object Size                  QWORD        64              // size of Header Extension object, including 46 bytes of Header Extension Object header
+					// Reserved Field 1             GUID         128             // hardcoded: GETID3_ASF_Reserved_1
+					// Reserved Field 2             WORD         16              // hardcoded: 0x00000006
+					// Header Extension Data Size   DWORD        32              // in bytes. valid: 0, or > 24. equals object size minus 46
+					// Header Extension Data        BYTESTREAM   variable        // array of zero or more extended header objects
+
+					// shortcut
+					$thisfile_asf['header_extension_object'] = array();
+					$thisfile_asf_headerextensionobject      = &$thisfile_asf['header_extension_object'];
+
+					$thisfile_asf_headerextensionobject['objectid']            = $NextObjectGUID;
+					$thisfile_asf_headerextensionobject['objectid_guid']       = $NextObjectGUIDtext;
+					$thisfile_asf_headerextensionobject['objectsize']          = $NextObjectSize;
+					$thisfile_asf_headerextensionobject['reserved_1']          = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_headerextensionobject['reserved_1_guid']     = $this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']);
+					if ($thisfile_asf_headerextensionobject['reserved_1'] != GETID3_ASF_Reserved_1) {
+						$ThisFileInfo['warning'][] = 'header_extension_object.reserved_1 GUID ('.$this->BytestringToGUID($thisfile_asf_headerextensionobject['reserved_1']).') does not match expected "GETID3_ASF_Reserved_1" GUID ('.$this->BytestringToGUID(GETID3_ASF_Reserved_1).')';
+						//return false;
+						break;
+					}
+					$thisfile_asf_headerextensionobject['reserved_2']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_asf_headerextensionobject['reserved_2'] != 6) {
+						$ThisFileInfo['warning'][] = 'header_extension_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_headerextensionobject['reserved_2']).') does not match expected value of "6"';
+						//return false;
+						break;
+					}
+					$thisfile_asf_headerextensionobject['extension_data_size'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_headerextensionobject['extension_data']      =                              substr($ASFHeaderData, $offset, $thisfile_asf_headerextensionobject['extension_data_size']);
+					$offset += $thisfile_asf_headerextensionobject['extension_data_size'];
+					break;
+
+				case GETID3_ASF_Codec_List_Object:
+					// Codec List Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Codec List object - GETID3_ASF_Codec_List_Object
+					// Object Size                  QWORD        64              // size of Codec List object, including 44 bytes of Codec List Object header
+					// Reserved                     GUID         128             // hardcoded: 86D15241-311D-11D0-A3A4-00A0C90348F6
+					// Codec Entries Count          DWORD        32              // number of entries in Codec Entries array
+					// Codec Entries                array of:    variable        //
+					// * Type                       WORD         16              // 0x0001 = Video Codec, 0x0002 = Audio Codec, 0xFFFF = Unknown Codec
+					// * Codec Name Length          WORD         16              // number of Unicode characters stored in the Codec Name field
+					// * Codec Name                 WCHAR        variable        // array of Unicode characters - name of codec used to create the content
+					// * Codec Description Length   WORD         16              // number of Unicode characters stored in the Codec Description field
+					// * Codec Description          WCHAR        variable        // array of Unicode characters - description of format used to create the content
+					// * Codec Information Length   WORD         16              // number of Unicode characters stored in the Codec Information field
+					// * Codec Information          BYTESTREAM   variable        // opaque array of information bytes about the codec used to create the content
+
+					// shortcut
+					$thisfile_asf['codec_list_object'] = array();
+					$thisfile_asf_codeclistobject      = &$thisfile_asf['codec_list_object'];
+
+					$thisfile_asf_codeclistobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_codeclistobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_codeclistobject['objectsize']                = $NextObjectSize;
+					$thisfile_asf_codeclistobject['reserved']                  = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_codeclistobject['reserved_guid']             = $this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']);
+					if ($thisfile_asf_codeclistobject['reserved'] != $this->GUIDtoBytestring('86D15241-311D-11D0-A3A4-00A0C90348F6')) {
+						$ThisFileInfo['warning'][] = 'codec_list_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_codeclistobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {86D15241-311D-11D0-A3A4-00A0C90348F6}';
+						//return false;
+						break;
+					}
+					$thisfile_asf_codeclistobject['codec_entries_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					for ($CodecEntryCounter = 0; $CodecEntryCounter < $thisfile_asf_codeclistobject['codec_entries_count']; $CodecEntryCounter++) {
+						// shortcut
+						$thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter] = array();
+						$thisfile_asf_codeclistobject_codecentries_current = &$thisfile_asf_codeclistobject['codec_entries'][$CodecEntryCounter];
+
+						$thisfile_asf_codeclistobject_codecentries_current['type_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_codeclistobject_codecentries_current['type'] = $this->ASFCodecListObjectTypeLookup($thisfile_asf_codeclistobject_codecentries_current['type_raw']);
+
+						$CodecNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
+						$offset += 2;
+						$thisfile_asf_codeclistobject_codecentries_current['name'] = substr($ASFHeaderData, $offset, $CodecNameLength);
+						$offset += $CodecNameLength;
+
+						$CodecDescriptionLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
+						$offset += 2;
+						$thisfile_asf_codeclistobject_codecentries_current['description'] = substr($ASFHeaderData, $offset, $CodecDescriptionLength);
+						$offset += $CodecDescriptionLength;
+
+						$CodecInformationLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_codeclistobject_codecentries_current['information'] = substr($ASFHeaderData, $offset, $CodecInformationLength);
+						$offset += $CodecInformationLength;
+
+						if ($thisfile_asf_codeclistobject_codecentries_current['type_raw'] == 2) {
+							// audio codec
+							if (strpos($thisfile_asf_codeclistobject_codecentries_current['description'], ',') === false) {
+								$ThisFileInfo['error'][] = '[asf][codec_list_object][codec_entries]['.$CodecEntryCounter.'][description] expected to contain comma-seperated list of parameters: "'.$thisfile_asf_codeclistobject_codecentries_current['description'].'"';
+								return false;
+							}
+							list($AudioCodecBitrate, $AudioCodecFrequency, $AudioCodecChannels) = explode(',', $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']));
+							$thisfile_audio['codec'] = $this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['name']);
+
+							if (!isset($thisfile_audio['bitrate']) && strstr($AudioCodecBitrate, 'kbps')) {
+								$thisfile_audio['bitrate'] = (int) (trim(str_replace('kbps', '', $AudioCodecBitrate)) * 1000);
+							}
+							//if (!isset($thisfile_video['bitrate']) && isset($thisfile_audio['bitrate']) && isset($thisfile_asf['file_properties_object']['max_bitrate']) && ($thisfile_asf_codeclistobject['codec_entries_count'] > 1)) {
+							if (!@$thisfile_video['bitrate'] && @$thisfile_audio['bitrate'] && @$ThisFileInfo['bitrate']) {
+								//$thisfile_video['bitrate'] = $thisfile_asf['file_properties_object']['max_bitrate'] - $thisfile_audio['bitrate'];
+								$thisfile_video['bitrate'] = $ThisFileInfo['bitrate'] - $thisfile_audio['bitrate'];
+							}
+
+							$AudioCodecFrequency = (int) trim(str_replace('kHz', '', $AudioCodecFrequency));
+							switch ($AudioCodecFrequency) {
+								case 8:
+								case 8000:
+									$thisfile_audio['sample_rate'] = 8000;
+									break;
+
+								case 11:
+								case 11025:
+									$thisfile_audio['sample_rate'] = 11025;
+									break;
+
+								case 12:
+								case 12000:
+									$thisfile_audio['sample_rate'] = 12000;
+									break;
+
+								case 16:
+								case 16000:
+									$thisfile_audio['sample_rate'] = 16000;
+									break;
+
+								case 22:
+								case 22050:
+									$thisfile_audio['sample_rate'] = 22050;
+									break;
+
+								case 24:
+								case 24000:
+									$thisfile_audio['sample_rate'] = 24000;
+									break;
+
+								case 32:
+								case 32000:
+									$thisfile_audio['sample_rate'] = 32000;
+									break;
+
+								case 44:
+								case 441000:
+									$thisfile_audio['sample_rate'] = 44100;
+									break;
+
+								case 48:
+								case 48000:
+									$thisfile_audio['sample_rate'] = 48000;
+									break;
+
+								default:
+									$ThisFileInfo['warning'][] = 'unknown frequency: "'.$AudioCodecFrequency.'" ('.$this->TrimConvert($thisfile_asf_codeclistobject_codecentries_current['description']).')';
+									break;
+							}
+
+							if (!isset($thisfile_audio['channels'])) {
+								if (strstr($AudioCodecChannels, 'stereo')) {
+									$thisfile_audio['channels'] = 2;
+								} elseif (strstr($AudioCodecChannels, 'mono')) {
+									$thisfile_audio['channels'] = 1;
+								}
+							}
+						}
+					}
+					break;
+
+				case GETID3_ASF_Script_Command_Object:
+					// Script Command Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Script Command object - GETID3_ASF_Script_Command_Object
+					// Object Size                  QWORD        64              // size of Script Command object, including 44 bytes of Script Command Object header
+					// Reserved                     GUID         128             // hardcoded: 4B1ACBE3-100B-11D0-A39B-00A0C90348F6
+					// Commands Count               WORD         16              // number of Commands structures in the Script Commands Objects
+					// Command Types Count          WORD         16              // number of Command Types structures in the Script Commands Objects
+					// Command Types                array of:    variable        //
+					// * Command Type Name Length   WORD         16              // number of Unicode characters for Command Type Name
+					// * Command Type Name          WCHAR        variable        // array of Unicode characters - name of a type of command
+					// Commands                     array of:    variable        //
+					// * Presentation Time          DWORD        32              // presentation time of that command, in milliseconds
+					// * Type Index                 WORD         16              // type of this command, as a zero-based index into the array of Command Types of this object
+					// * Command Name Length        WORD         16              // number of Unicode characters for Command Name
+					// * Command Name               WCHAR        variable        // array of Unicode characters - name of this command
+
+					// shortcut
+					$thisfile_asf['script_command_object'] = array();
+					$thisfile_asf_scriptcommandobject      = &$thisfile_asf['script_command_object'];
+
+					$thisfile_asf_scriptcommandobject['objectid']             = $NextObjectGUID;
+					$thisfile_asf_scriptcommandobject['objectid_guid']        = $NextObjectGUIDtext;
+					$thisfile_asf_scriptcommandobject['objectsize']           = $NextObjectSize;
+					$thisfile_asf_scriptcommandobject['reserved']             = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_scriptcommandobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']);
+					if ($thisfile_asf_scriptcommandobject['reserved'] != $this->GUIDtoBytestring('4B1ACBE3-100B-11D0-A39B-00A0C90348F6')) {
+						$ThisFileInfo['warning'][] = 'script_command_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_scriptcommandobject['reserved']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4B1ACBE3-100B-11D0-A39B-00A0C90348F6}';
+						//return false;
+						break;
+					}
+					$thisfile_asf_scriptcommandobject['commands_count']       = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_scriptcommandobject['command_types_count']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					for ($CommandTypesCounter = 0; $CommandTypesCounter < $thisfile_asf_scriptcommandobject['command_types_count']; $CommandTypesCounter++) {
+						$CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
+						$offset += 2;
+						$thisfile_asf_scriptcommandobject['command_types'][$CommandTypesCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength);
+						$offset += $CommandTypeNameLength;
+					}
+					for ($CommandsCounter = 0; $CommandsCounter < $thisfile_asf_scriptcommandobject['commands_count']; $CommandsCounter++) {
+						$thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['presentation_time']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+						$offset += 4;
+						$thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['type_index']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+
+						$CommandTypeNameLength = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2)) * 2; // 2 bytes per character
+						$offset += 2;
+						$thisfile_asf_scriptcommandobject['commands'][$CommandsCounter]['name'] = substr($ASFHeaderData, $offset, $CommandTypeNameLength);
+						$offset += $CommandTypeNameLength;
+					}
+					break;
+
+				case GETID3_ASF_Marker_Object:
+					// Marker Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Marker object - GETID3_ASF_Marker_Object
+					// Object Size                  QWORD        64              // size of Marker object, including 48 bytes of Marker Object header
+					// Reserved                     GUID         128             // hardcoded: 4CFEDB20-75F6-11CF-9C0F-00A0C90349CB
+					// Markers Count                DWORD        32              // number of Marker structures in Marker Object
+					// Reserved                     WORD         16              // hardcoded: 0x0000
+					// Name Length                  WORD         16              // number of bytes in the Name field
+					// Name                         WCHAR        variable        // name of the Marker Object
+					// Markers                      array of:    variable        //
+					// * Offset                     QWORD        64              // byte offset into Data Object
+					// * Presentation Time          QWORD        64              // in 100-nanosecond units
+					// * Entry Length               WORD         16              // length in bytes of (Send Time + Flags + Marker Description Length + Marker Description + Padding)
+					// * Send Time                  DWORD        32              // in milliseconds
+					// * Flags                      DWORD        32              // hardcoded: 0x00000000
+					// * Marker Description Length  DWORD        32              // number of bytes in Marker Description field
+					// * Marker Description         WCHAR        variable        // array of Unicode characters - description of marker entry
+					// * Padding                    BYTESTREAM   variable        // optional padding bytes
+
+					// shortcut
+					$thisfile_asf['marker_object'] = array();
+					$thisfile_asf_markerobject     = &$thisfile_asf['marker_object'];
+
+					$thisfile_asf_markerobject['objectid']             = $NextObjectGUID;
+					$thisfile_asf_markerobject['objectid_guid']        = $NextObjectGUIDtext;
+					$thisfile_asf_markerobject['objectsize']           = $NextObjectSize;
+					$thisfile_asf_markerobject['reserved']             = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_markerobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_markerobject['reserved']);
+					if ($thisfile_asf_markerobject['reserved'] != $this->GUIDtoBytestring('4CFEDB20-75F6-11CF-9C0F-00A0C90349CB')) {
+						$ThisFileInfo['warning'][] = 'marker_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_markerobject['reserved_1']).'} does not match expected "GETID3_ASF_Reserved_1" GUID {4CFEDB20-75F6-11CF-9C0F-00A0C90349CB}';
+						break;
+					}
+					$thisfile_asf_markerobject['markers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_markerobject['reserved_2'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_asf_markerobject['reserved_2'] != 0) {
+						$ThisFileInfo['warning'][] = 'marker_object.reserved_2 ('.getid3_lib::PrintHexBytes($thisfile_asf_markerobject['reserved_2']).') does not match expected value of "0"';
+						break;
+					}
+					$thisfile_asf_markerobject['name_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_markerobject['name'] = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['name_length']);
+					$offset += $thisfile_asf_markerobject['name_length'];
+					for ($MarkersCounter = 0; $MarkersCounter < $thisfile_asf_markerobject['markers_count']; $MarkersCounter++) {
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['offset']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+						$offset += 8;
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['presentation_time']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 8));
+						$offset += 8;
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length']              = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['send_time']                 = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+						$offset += 4;
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['flags']                     = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+						$offset += 4;
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+						$offset += 4;
+						$thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description']        = substr($ASFHeaderData, $offset, $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length']);
+						$offset += $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'];
+						$PaddingLength = $thisfile_asf_markerobject['markers'][$MarkersCounter]['entry_length'] - 4 -  4 - 4 - $thisfile_asf_markerobject['markers'][$MarkersCounter]['marker_description_length'];
+						if ($PaddingLength > 0) {
+							$thisfile_asf_markerobject['markers'][$MarkersCounter]['padding']               = substr($ASFHeaderData, $offset, $PaddingLength);
+							$offset += $PaddingLength;
+						}
+					}
+					break;
+
+				case GETID3_ASF_Bitrate_Mutual_Exclusion_Object:
+					// Bitrate Mutual Exclusion Object: (optional)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Bitrate Mutual Exclusion object - GETID3_ASF_Bitrate_Mutual_Exclusion_Object
+					// Object Size                  QWORD        64              // size of Bitrate Mutual Exclusion object, including 42 bytes of Bitrate Mutual Exclusion Object header
+					// Exlusion Type                GUID         128             // nature of mutual exclusion relationship. one of: (GETID3_ASF_Mutex_Bitrate, GETID3_ASF_Mutex_Unknown)
+					// Stream Numbers Count         WORD         16              // number of video streams
+					// Stream Numbers               WORD         variable        // array of mutually exclusive video stream numbers. 1 <= valid <= 127
+
+					// shortcut
+					$thisfile_asf['bitrate_mutual_exclusion_object'] = array();
+					$thisfile_asf_bitratemutualexclusionobject       = &$thisfile_asf['bitrate_mutual_exclusion_object'];
+
+					$thisfile_asf_bitratemutualexclusionobject['objectid']             = $NextObjectGUID;
+					$thisfile_asf_bitratemutualexclusionobject['objectid_guid']        = $NextObjectGUIDtext;
+					$thisfile_asf_bitratemutualexclusionobject['objectsize']           = $NextObjectSize;
+					$thisfile_asf_bitratemutualexclusionobject['reserved']             = substr($ASFHeaderData, $offset, 16);
+					$thisfile_asf_bitratemutualexclusionobject['reserved_guid']        = $this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']);
+					$offset += 16;
+					if (($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Bitrate) && ($thisfile_asf_bitratemutualexclusionobject['reserved'] != GETID3_ASF_Mutex_Unknown)) {
+						$ThisFileInfo['warning'][] = 'bitrate_mutual_exclusion_object.reserved GUID {'.$this->BytestringToGUID($thisfile_asf_bitratemutualexclusionobject['reserved']).'} does not match expected "GETID3_ASF_Mutex_Bitrate" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Bitrate).'} or  "GETID3_ASF_Mutex_Unknown" GUID {'.$this->BytestringToGUID(GETID3_ASF_Mutex_Unknown).'}';
+						//return false;
+						break;
+					}
+					$thisfile_asf_bitratemutualexclusionobject['stream_numbers_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					for ($StreamNumberCounter = 0; $StreamNumberCounter < $thisfile_asf_bitratemutualexclusionobject['stream_numbers_count']; $StreamNumberCounter++) {
+						$thisfile_asf_bitratemutualexclusionobject['stream_numbers'][$StreamNumberCounter] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+					}
+					break;
+
+				case GETID3_ASF_Error_Correction_Object:
+					// Error Correction Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Error Correction object - GETID3_ASF_Error_Correction_Object
+					// Object Size                  QWORD        64              // size of Error Correction object, including 44 bytes of Error Correction Object header
+					// Error Correction Type        GUID         128             // type of error correction. one of: (GETID3_ASF_No_Error_Correction, GETID3_ASF_Audio_Spread)
+					// Error Correction Data Length DWORD        32              // number of bytes in Error Correction Data field
+					// Error Correction Data        BYTESTREAM   variable        // structure depends on value of Error Correction Type field
+
+					// shortcut
+					$thisfile_asf['error_correction_object'] = array();
+					$thisfile_asf_errorcorrectionobject      = &$thisfile_asf['error_correction_object'];
+
+					$thisfile_asf_errorcorrectionobject['objectid']              = $NextObjectGUID;
+					$thisfile_asf_errorcorrectionobject['objectid_guid']         = $NextObjectGUIDtext;
+					$thisfile_asf_errorcorrectionobject['objectsize']            = $NextObjectSize;
+					$thisfile_asf_errorcorrectionobject['error_correction_type'] = substr($ASFHeaderData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_errorcorrectionobject['error_correction_guid'] = $this->BytestringToGUID($thisfile_asf_errorcorrectionobject['error_correction_type']);
+					$thisfile_asf_errorcorrectionobject['error_correction_data_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+					$offset += 4;
+					switch ($thisfile_asf_errorcorrectionobject['error_correction_type']) {
+						case GETID3_ASF_No_Error_Correction:
+							// should be no data, but just in case there is, skip to the end of the field
+							$offset += $thisfile_asf_errorcorrectionobject['error_correction_data_length'];
+							break;
+
+						case GETID3_ASF_Audio_Spread:
+							// Field Name                   Field Type   Size (bits)
+							// Span                         BYTE         8               // number of packets over which audio will be spread.
+							// Virtual Packet Length        WORD         16              // size of largest audio payload found in audio stream
+							// Virtual Chunk Length         WORD         16              // size of largest audio payload found in audio stream
+							// Silence Data Length          WORD         16              // number of bytes in Silence Data field
+							// Silence Data                 BYTESTREAM   variable        // hardcoded: 0x00 * (Silence Data Length) bytes
+
+							$thisfile_asf_errorcorrectionobject['span']                  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 1));
+							$offset += 1;
+							$thisfile_asf_errorcorrectionobject['virtual_packet_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+							$offset += 2;
+							$thisfile_asf_errorcorrectionobject['virtual_chunk_length']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+							$offset += 2;
+							$thisfile_asf_errorcorrectionobject['silence_data_length']   = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+							$offset += 2;
+							$thisfile_asf_errorcorrectionobject['silence_data']          = substr($ASFHeaderData, $offset, $thisfile_asf_errorcorrectionobject['silence_data_length']);
+							$offset += $thisfile_asf_errorcorrectionobject['silence_data_length'];
+							break;
+
+						default:
+							$ThisFileInfo['warning'][] = 'error_correction_object.error_correction_type GUID {'.$this->BytestringToGUID($thisfile_asf_errorcorrectionobject['reserved']).'} does not match expected "GETID3_ASF_No_Error_Correction" GUID {'.$this->BytestringToGUID(GETID3_ASF_No_Error_Correction).'} or  "GETID3_ASF_Audio_Spread" GUID {'.$this->BytestringToGUID(GETID3_ASF_Audio_Spread).'}';
+							//return false;
+							break;
+					}
+
+					break;
+
+				case GETID3_ASF_Content_Description_Object:
+					// Content Description Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Content Description object - GETID3_ASF_Content_Description_Object
+					// Object Size                  QWORD        64              // size of Content Description object, including 34 bytes of Content Description Object header
+					// Title Length                 WORD         16              // number of bytes in Title field
+					// Author Length                WORD         16              // number of bytes in Author field
+					// Copyright Length             WORD         16              // number of bytes in Copyright field
+					// Description Length           WORD         16              // number of bytes in Description field
+					// Rating Length                WORD         16              // number of bytes in Rating field
+					// Title                        WCHAR        16              // array of Unicode characters - Title
+					// Author                       WCHAR        16              // array of Unicode characters - Author
+					// Copyright                    WCHAR        16              // array of Unicode characters - Copyright
+					// Description                  WCHAR        16              // array of Unicode characters - Description
+					// Rating                       WCHAR        16              // array of Unicode characters - Rating
+
+					// shortcut
+					$thisfile_asf['content_description_object'] = array();
+					$thisfile_asf_contentdescriptionobject      = &$thisfile_asf['content_description_object'];
+
+					$thisfile_asf_contentdescriptionobject['objectid']              = $NextObjectGUID;
+					$thisfile_asf_contentdescriptionobject['objectid_guid']         = $NextObjectGUIDtext;
+					$thisfile_asf_contentdescriptionobject['objectsize']            = $NextObjectSize;
+					$thisfile_asf_contentdescriptionobject['title_length']          = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_contentdescriptionobject['author_length']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_contentdescriptionobject['copyright_length']      = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_contentdescriptionobject['description_length']    = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_contentdescriptionobject['rating_length']         = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_contentdescriptionobject['title']                 = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['title_length']);
+					$offset += $thisfile_asf_contentdescriptionobject['title_length'];
+					$thisfile_asf_contentdescriptionobject['author']                = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['author_length']);
+					$offset += $thisfile_asf_contentdescriptionobject['author_length'];
+					$thisfile_asf_contentdescriptionobject['copyright']             = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['copyright_length']);
+					$offset += $thisfile_asf_contentdescriptionobject['copyright_length'];
+					$thisfile_asf_contentdescriptionobject['description']           = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['description_length']);
+					$offset += $thisfile_asf_contentdescriptionobject['description_length'];
+					$thisfile_asf_contentdescriptionobject['rating']                = substr($ASFHeaderData, $offset, $thisfile_asf_contentdescriptionobject['rating_length']);
+					$offset += $thisfile_asf_contentdescriptionobject['rating_length'];
+
+					$ASFcommentKeysToCopy = array('title'=>'title', 'author'=>'artist', 'copyright'=>'copyright', 'description'=>'comment', 'rating'=>'rating');
+					foreach ($ASFcommentKeysToCopy as $keytocopyfrom => $keytocopyto) {
+						if (!empty($thisfile_asf_contentdescriptionobject[$keytocopyfrom])) {
+							$thisfile_asf_comments[$keytocopyto][] = $this->TrimTerm($thisfile_asf_contentdescriptionobject[$keytocopyfrom]);
+						}
+					}
+					break;
+
+				case GETID3_ASF_Extended_Content_Description_Object:
+					// Extended Content Description Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Extended Content Description object - GETID3_ASF_Extended_Content_Description_Object
+					// Object Size                  QWORD        64              // size of ExtendedContent Description object, including 26 bytes of Extended Content Description Object header
+					// Content Descriptors Count    WORD         16              // number of entries in Content Descriptors list
+					// Content Descriptors          array of:    variable        //
+					// * Descriptor Name Length     WORD         16              // size in bytes of Descriptor Name field
+					// * Descriptor Name            WCHAR        variable        // array of Unicode characters - Descriptor Name
+					// * Descriptor Value Data Type WORD         16              // Lookup array:
+																					// 0x0000 = Unicode String (variable length)
+																					// 0x0001 = BYTE array     (variable length)
+																					// 0x0002 = BOOL           (DWORD, 32 bits)
+																					// 0x0003 = DWORD          (DWORD, 32 bits)
+																					// 0x0004 = QWORD          (QWORD, 64 bits)
+																					// 0x0005 = WORD           (WORD,  16 bits)
+					// * Descriptor Value Length    WORD         16              // number of bytes stored in Descriptor Value field
+					// * Descriptor Value           variable     variable        // value for Content Descriptor
+
+					// shortcut
+					$thisfile_asf['extended_content_description_object'] = array();
+					$thisfile_asf_extendedcontentdescriptionobject       = &$thisfile_asf['extended_content_description_object'];
+
+					$thisfile_asf_extendedcontentdescriptionobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_extendedcontentdescriptionobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_extendedcontentdescriptionobject['objectsize']                = $NextObjectSize;
+					$thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					for ($ExtendedContentDescriptorsCounter = 0; $ExtendedContentDescriptorsCounter < $thisfile_asf_extendedcontentdescriptionobject['content_descriptors_count']; $ExtendedContentDescriptorsCounter++) {
+						// shortcut
+						$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter] = array();
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current                                  = &$thisfile_asf_extendedcontentdescriptionobject['content_descriptors'][$ExtendedContentDescriptorsCounter];
+
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['base_offset']  = $offset + 30;
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']  = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']         = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length']);
+						$offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name_length'];
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']   = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']        = substr($ASFHeaderData, $offset, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length']);
+						$offset += $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_length'];
+						switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) {
+							case 0x0000: // Unicode string
+								break;
+
+							case 0x0001: // BYTE array
+								// do nothing
+								break;
+
+							case 0x0002: // BOOL
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = (bool) getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
+								break;
+
+							case 0x0003: // DWORD
+							case 0x0004: // QWORD
+							case 0x0005: // WORD
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'] = getid3_lib::LittleEndian2Int($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
+								break;
+
+							default:
+								$ThisFileInfo['warning'][] = 'extended_content_description.content_descriptors.'.$ExtendedContentDescriptorsCounter.'.value_type is invalid ('.$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type'].')';
+								//return false;
+								break;
+						}
+						switch ($this->TrimConvert(strtolower($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']))) {
+
+							case 'wm/albumartist':
+							case 'artist':
+								$thisfile_asf_comments['artist'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								break;
+
+							case 'wm/albumtitle':
+							case 'album':
+								$thisfile_asf_comments['album']  = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								break;
+
+							case 'wm/genre':
+							case 'genre':
+								$thisfile_asf_comments['genre'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								break;
+
+							case 'wm/tracknumber':
+							case 'tracknumber':
+								$thisfile_asf_comments['track'] = array(intval($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'])));
+								break;
+
+							case 'wm/track':
+								if (empty($thisfile_asf_comments['track'])) {
+									$thisfile_asf_comments['track'] = array(1 + $this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								}
+								break;
+
+							case 'wm/year':
+							case 'year':
+							case 'date':
+								$thisfile_asf_comments['year'] = array( $this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								break;
+
+							case 'wm/lyrics':
+							case 'lyrics':
+								$thisfile_asf_comments['lyrics'] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+								break;
+
+							case 'isvbr':
+								if ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']) {
+									$thisfile_audio['bitrate_mode'] = 'vbr';
+									$thisfile_video['bitrate_mode'] = 'vbr';
+								}
+								break;
+
+							case 'id3':
+								// id3v2 module might not be loaded
+								if (class_exists('getid3_id3v2')) {
+								    $tempfile         = tempnam('*', 'getID3');
+								    $tempfilehandle   = fopen($tempfile, "wb");
+									$tempThisfileInfo = array('encoding'=>$ThisFileInfo['encoding']);
+									fwrite($tempfilehandle, $thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
+									fclose($tempfilehandle);
+
+									$tempfilehandle = fopen($tempfile, "rb");
+									$id3 = new getid3_id3v2($tempfilehandle, $tempThisfileInfo);
+									unset($id3);
+									fclose($tempfilehandle);
+									unlink($tempfile);
+
+									$ThisFileInfo['id3v2'] = $tempThisfileInfo['id3v2'];
+									unset($tempThisfileInfo);
+								}
+								break;
+
+							case 'wm/encodingtime':
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix'] = $this->FILETIMEtoUNIXtime($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
+								$thisfile_asf_comments['encoding_time_unix'] = array($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['encoding_time_unix']);
+								break;
+
+							case 'wm/picture':
+								//typedef struct _WMPicture{
+								//  LPWSTR  pwszMIMEType;
+								//  BYTE  bPictureType;
+								//  LPWSTR  pwszDescription;
+								//  DWORD  dwDataLen;
+								//  BYTE*  pbData;
+								//} WM_PICTURE;
+
+								$wm_picture_offset = 0;
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id'] = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 1));
+								$wm_picture_offset += 1;
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type']    = $this->WMpictureTypeLookup($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_type_id']);
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_size']    = getid3_lib::LittleEndian2Int(substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 4));
+								$wm_picture_offset += 4;
+
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] = '';
+								do {
+									$next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2);
+									$wm_picture_offset += 2;
+									$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_mime'] .= $next_byte_pair;
+								} while ($next_byte_pair !== "\x00\x00");
+
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] = '';
+								do {
+									$next_byte_pair = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset, 2);
+									$wm_picture_offset += 2;
+									$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['image_description'] .= $next_byte_pair;
+								} while ($next_byte_pair !== "\x00\x00");
+
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['dataoffset'] = $wm_picture_offset;
+								$thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['data'] = substr($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value'], $wm_picture_offset);
+								unset($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']);
+
+								break;
+
+							default:
+								switch ($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value_type']) {
+									case 0: // Unicode string
+										if (substr($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name']), 0, 3) == 'WM/') {
+											$thisfile_asf_comments[str_replace('wm/', '', strtolower($this->TrimConvert($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['name'])))] = array($this->TrimTerm($thisfile_asf_extendedcontentdescriptionobject_contentdescriptor_current['value']));
+										}
+										break;
+
+									case 1:
+										break;
+								}
+								break;
+						}
+
+					}
+					break;
+
+				case GETID3_ASF_Stream_Bitrate_Properties_Object:
+					// Stream Bitrate Properties Object: (optional, one only)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Stream Bitrate Properties object - GETID3_ASF_Stream_Bitrate_Properties_Object
+					// Object Size                  QWORD        64              // size of Extended Content Description object, including 26 bytes of Stream Bitrate Properties Object header
+					// Bitrate Records Count        WORD         16              // number of records in Bitrate Records
+					// Bitrate Records              array of:    variable        //
+					// * Flags                      WORD         16              //
+					// * * Stream Number            bits         7  (0x007F)     // number of this stream
+					// * * Reserved                 bits         9  (0xFF80)     // hardcoded: 0
+					// * Average Bitrate            DWORD        32              // in bits per second
+
+					// shortcut
+					$thisfile_asf['stream_bitrate_properties_object'] = array();
+					$thisfile_asf_streambitratepropertiesobject       = &$thisfile_asf['stream_bitrate_properties_object'];
+
+					$thisfile_asf_streambitratepropertiesobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_streambitratepropertiesobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_streambitratepropertiesobject['objectsize']                = $NextObjectSize;
+					$thisfile_asf_streambitratepropertiesobject['bitrate_records_count']     = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+					$offset += 2;
+					for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitratepropertiesobject['bitrate_records_count']; $BitrateRecordsCounter++) {
+						$thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags']['stream_number'] = $thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['flags_raw'] & 0x007F;
+						$thisfile_asf_streambitratepropertiesobject['bitrate_records'][$BitrateRecordsCounter]['bitrate'] = getid3_lib::LittleEndian2Int(substr($ASFHeaderData, $offset, 4));
+						$offset += 4;
+					}
+					break;
+
+				case GETID3_ASF_Padding_Object:
+					// Padding Object: (optional)
+					// Field Name                   Field Type   Size (bits)
+					// Object ID                    GUID         128             // GUID for Padding object - GETID3_ASF_Padding_Object
+					// Object Size                  QWORD        64              // size of Padding object, including 24 bytes of ASF Padding Object header
+					// Padding Data                 BYTESTREAM   variable        // ignore
+
+					// shortcut
+					$thisfile_asf['padding_object'] = array();
+					$thisfile_asf_paddingobject     = &$thisfile_asf['padding_object'];
+
+					$thisfile_asf_paddingobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_paddingobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_paddingobject['objectsize']                = $NextObjectSize;
+					$thisfile_asf_paddingobject['padding_length']            = $thisfile_asf_paddingobject['objectsize'] - 16 - 8;
+					$thisfile_asf_paddingobject['padding']                   = substr($ASFHeaderData, $offset, $thisfile_asf_paddingobject['padding_length']);
+					$offset += ($NextObjectSize - 16 - 8);
+					break;
+
+				case GETID3_ASF_Extended_Content_Encryption_Object:
+				case GETID3_ASF_Content_Encryption_Object:
+					// WMA DRM - just ignore
+					$offset += ($NextObjectSize - 16 - 8);
+					break;
+
+				default:
+					// Implementations shall ignore any standard or non-standard object that they do not know how to handle.
+					if ($this->GUIDname($NextObjectGUIDtext)) {
+						$ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8);
+					} else {
+						$ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF header at offset '.($offset - 16 - 8);
+					}
+					$offset += ($NextObjectSize - 16 - 8);
+					break;
+			}
+		}
+		if (isset($thisfile_asf_streambitrateproperties['bitrate_records_count'])) {
+			$ASFbitrateAudio = 0;
+			$ASFbitrateVideo = 0;
+			for ($BitrateRecordsCounter = 0; $BitrateRecordsCounter < $thisfile_asf_streambitrateproperties['bitrate_records_count']; $BitrateRecordsCounter++) {
+				if (isset($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter])) {
+					switch ($thisfile_asf_codeclistobject['codec_entries'][$BitrateRecordsCounter]['type_raw']) {
+						case 1:
+							$ASFbitrateVideo += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
+							break;
+
+						case 2:
+							$ASFbitrateAudio += $thisfile_asf_streambitrateproperties['bitrate_records'][$BitrateRecordsCounter]['bitrate'];
+							break;
+
+						default:
+							// do nothing
+							break;
+					}
+				}
+			}
+			if ($ASFbitrateAudio > 0) {
+				$thisfile_audio['bitrate'] = $ASFbitrateAudio;
+			}
+			if ($ASFbitrateVideo > 0) {
+				$thisfile_video['bitrate'] = $ASFbitrateVideo;
+			}
+		}
+		if (isset($thisfile_asf['stream_properties_object']) && is_array($thisfile_asf['stream_properties_object'])) {
+
+			$thisfile_audio['bitrate'] = 0;
+			$thisfile_video['bitrate'] = 0;
+
+			foreach ($thisfile_asf['stream_properties_object'] as $streamnumber => $streamdata) {
+
+				switch ($streamdata['stream_type']) {
+					case GETID3_ASF_Audio_Media:
+						// Field Name                   Field Type   Size (bits)
+						// Codec ID / Format Tag        WORD         16              // unique ID of audio codec - defined as wFormatTag field of WAVEFORMATEX structure
+						// Number of Channels           WORD         16              // number of channels of audio - defined as nChannels field of WAVEFORMATEX structure
+						// Samples Per Second           DWORD        32              // in Hertz - defined as nSamplesPerSec field of WAVEFORMATEX structure
+						// Average number of Bytes/sec  DWORD        32              // bytes/sec of audio stream  - defined as nAvgBytesPerSec field of WAVEFORMATEX structure
+						// Block Alignment              WORD         16              // block size in bytes of audio codec - defined as nBlockAlign field of WAVEFORMATEX structure
+						// Bits per sample              WORD         16              // bits per sample of mono data. set to zero for variable bitrate codecs. defined as wBitsPerSample field of WAVEFORMATEX structure
+						// Codec Specific Data Size     WORD         16              // size in bytes of Codec Specific Data buffer - defined as cbSize field of WAVEFORMATEX structure
+						// Codec Specific Data          BYTESTREAM   variable        // array of codec-specific data bytes
+
+						// shortcut
+						$thisfile_asf['audio_media'][$streamnumber] = array();
+						$thisfile_asf_audiomedia_currentstream      = &$thisfile_asf['audio_media'][$streamnumber];
+
+						$audiomediaoffset = 0;
+
+						$thisfile_asf_audiomedia_currentstream = getid3_riff::RIFFparseWAVEFORMATex(substr($streamdata['type_specific_data'], $audiomediaoffset, 16));
+						$audiomediaoffset += 16;
+
+						$thisfile_audio['lossless'] = false;
+						switch ($thisfile_asf_audiomedia_currentstream['raw']['wFormatTag']) {
+							case 0x0001: // PCM
+							case 0x0163: // WMA9 Lossless
+								$thisfile_audio['lossless'] = true;
+								break;
+						}
+
+						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) {
+							foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) {
+								if (@$dataarray['flags']['stream_number'] == $streamnumber) {
+									$thisfile_asf_audiomedia_currentstream['bitrate'] = $dataarray['bitrate'];
+									$thisfile_audio['bitrate'] += $dataarray['bitrate'];
+									break;
+								}
+							}
+						} else {
+							if (@$thisfile_asf_audiomedia_currentstream['bytes_sec']) {
+								$thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bytes_sec'] * 8;
+							} elseif (@$thisfile_asf_audiomedia_currentstream['bitrate']) {
+								$thisfile_audio['bitrate'] += $thisfile_asf_audiomedia_currentstream['bitrate'];
+							}
+						}
+						$thisfile_audio['streams'][$streamnumber]                = $thisfile_asf_audiomedia_currentstream;
+						$thisfile_audio['streams'][$streamnumber]['wformattag']  = $thisfile_asf_audiomedia_currentstream['raw']['wFormatTag'];
+						$thisfile_audio['streams'][$streamnumber]['lossless']    = $thisfile_audio['lossless'];
+						$thisfile_audio['streams'][$streamnumber]['bitrate']     = $thisfile_audio['bitrate'];
+						$thisfile_audio['streams'][$streamnumber]['dataformat']  = 'wma';
+						unset($thisfile_audio['streams'][$streamnumber]['raw']);
+
+						$thisfile_asf_audiomedia_currentstream['codec_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $audiomediaoffset, 2));
+						$audiomediaoffset += 2;
+						$thisfile_asf_audiomedia_currentstream['codec_data']      = substr($streamdata['type_specific_data'], $audiomediaoffset, $thisfile_asf_audiomedia_currentstream['codec_data_size']);
+						$audiomediaoffset += $thisfile_asf_audiomedia_currentstream['codec_data_size'];
+
+						break;
+
+					case GETID3_ASF_Video_Media:
+						// Field Name                   Field Type   Size (bits)
+						// Encoded Image Width          DWORD        32              // width of image in pixels
+						// Encoded Image Height         DWORD        32              // height of image in pixels
+						// Reserved Flags               BYTE         8               // hardcoded: 0x02
+						// Format Data Size             WORD         16              // size of Format Data field in bytes
+						// Format Data                  array of:    variable        //
+						// * Format Data Size           DWORD        32              // number of bytes in Format Data field, in bytes - defined as biSize field of BITMAPINFOHEADER structure
+						// * Image Width                LONG         32              // width of encoded image in pixels - defined as biWidth field of BITMAPINFOHEADER structure
+						// * Image Height               LONG         32              // height of encoded image in pixels - defined as biHeight field of BITMAPINFOHEADER structure
+						// * Reserved                   WORD         16              // hardcoded: 0x0001 - defined as biPlanes field of BITMAPINFOHEADER structure
+						// * Bits Per Pixel Count       WORD         16              // bits per pixel - defined as biBitCount field of BITMAPINFOHEADER structure
+						// * Compression ID             FOURCC       32              // fourcc of video codec - defined as biCompression field of BITMAPINFOHEADER structure
+						// * Image Size                 DWORD        32              // image size in bytes - defined as biSizeImage field of BITMAPINFOHEADER structure
+						// * Horizontal Pixels / Meter  DWORD        32              // horizontal resolution of target device in pixels per meter - defined as biXPelsPerMeter field of BITMAPINFOHEADER structure
+						// * Vertical Pixels / Meter    DWORD        32              // vertical resolution of target device in pixels per meter - defined as biYPelsPerMeter field of BITMAPINFOHEADER structure
+						// * Colors Used Count          DWORD        32              // number of color indexes in the color table that are actually used - defined as biClrUsed field of BITMAPINFOHEADER structure
+						// * Important Colors Count     DWORD        32              // number of color index required for displaying bitmap. if zero, all colors are required. defined as biClrImportant field of BITMAPINFOHEADER structure
+						// * Codec Specific Data        BYTESTREAM   variable        // array of codec-specific data bytes
+
+						// shortcut
+						$thisfile_asf['video_media'][$streamnumber] = array();
+						$thisfile_asf_videomedia_currentstream      = &$thisfile_asf['video_media'][$streamnumber];
+
+						$videomediaoffset = 0;
+						$thisfile_asf_videomedia_currentstream['image_width']                     = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['image_height']                    = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['flags']                           = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 1));
+						$videomediaoffset += 1;
+						$thisfile_asf_videomedia_currentstream['format_data_size']                = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2));
+						$videomediaoffset += 2;
+						$thisfile_asf_videomedia_currentstream['format_data']['format_data_size'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['image_width']      = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['image_height']     = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['reserved']         = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2));
+						$videomediaoffset += 2;
+						$thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel']   = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 2));
+						$videomediaoffset += 2;
+						$thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']     = substr($streamdata['type_specific_data'], $videomediaoffset, 4);
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['image_size']       = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['horizontal_pels']  = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['vertical_pels']    = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['colors_used']      = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['colors_important'] = getid3_lib::LittleEndian2Int(substr($streamdata['type_specific_data'], $videomediaoffset, 4));
+						$videomediaoffset += 4;
+						$thisfile_asf_videomedia_currentstream['format_data']['codec_data']       = substr($streamdata['type_specific_data'], $videomediaoffset);
+
+						if (!empty($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'])) {
+							foreach ($thisfile_asf['stream_bitrate_properties_object']['bitrate_records'] as $dummy => $dataarray) {
+								if (@$dataarray['flags']['stream_number'] == $streamnumber) {
+									$thisfile_asf_videomedia_currentstream['bitrate'] = $dataarray['bitrate'];
+									$thisfile_video['streams'][$streamnumber]['bitrate'] = $dataarray['bitrate'];
+									$thisfile_video['bitrate'] += $dataarray['bitrate'];
+									break;
+								}
+							}
+						}
+
+						$thisfile_asf_videomedia_currentstream['format_data']['codec'] = getid3_riff::RIFFfourccLookup($thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc']);
+
+						$thisfile_video['streams'][$streamnumber]['fourcc']          = $thisfile_asf_videomedia_currentstream['format_data']['codec_fourcc'];
+						$thisfile_video['streams'][$streamnumber]['codec']           = $thisfile_asf_videomedia_currentstream['format_data']['codec'];
+						$thisfile_video['streams'][$streamnumber]['resolution_x']    = $thisfile_asf_videomedia_currentstream['image_width'];
+						$thisfile_video['streams'][$streamnumber]['resolution_y']    = $thisfile_asf_videomedia_currentstream['image_height'];
+						$thisfile_video['streams'][$streamnumber]['bits_per_sample'] = $thisfile_asf_videomedia_currentstream['format_data']['bits_per_pixel'];
+						break;
+
+					default:
+						break;
+				}
+			}
+		}
+
+		while (ftell($fd) < $ThisFileInfo['avdataend']) {
+			$NextObjectDataHeader = fread($fd, 24);
+			$offset = 0;
+			$NextObjectGUID = substr($NextObjectDataHeader, 0, 16);
+			$offset += 16;
+			$NextObjectGUIDtext = $this->BytestringToGUID($NextObjectGUID);
+			$NextObjectSize = getid3_lib::LittleEndian2Int(substr($NextObjectDataHeader, $offset, 8));
+			$offset += 8;
+
+			switch ($NextObjectGUID) {
+				case GETID3_ASF_Data_Object:
+					// Data Object: (mandatory, one only)
+					// Field Name                       Field Type   Size (bits)
+					// Object ID                        GUID         128             // GUID for Data object - GETID3_ASF_Data_Object
+					// Object Size                      QWORD        64              // size of Data object, including 50 bytes of Data Object header. may be 0 if FilePropertiesObject.BroadcastFlag == 1
+					// File ID                          GUID         128             // unique identifier. identical to File ID field in Header Object
+					// Total Data Packets               QWORD        64              // number of Data Packet entries in Data Object. invalid if FilePropertiesObject.BroadcastFlag == 1
+					// Reserved                         WORD         16              // hardcoded: 0x0101
+
+					// shortcut
+					$thisfile_asf['data_object'] = array();
+					$thisfile_asf_dataobject     = &$thisfile_asf['data_object'];
+
+					$DataObjectData = $NextObjectDataHeader.fread($fd, 50 - 24);
+					$offset = 24;
+
+					$thisfile_asf_dataobject['objectid']           = $NextObjectGUID;
+					$thisfile_asf_dataobject['objectid_guid']      = $NextObjectGUIDtext;
+					$thisfile_asf_dataobject['objectsize']         = $NextObjectSize;
+
+					$thisfile_asf_dataobject['fileid']             = substr($DataObjectData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_dataobject['fileid_guid']        = $this->BytestringToGUID($thisfile_asf_dataobject['fileid']);
+					$thisfile_asf_dataobject['total_data_packets'] = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_dataobject['reserved']           = getid3_lib::LittleEndian2Int(substr($DataObjectData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_asf_dataobject['reserved'] != 0x0101) {
+						$ThisFileInfo['warning'][] = 'data_object.reserved ('.getid3_lib::PrintHexBytes($thisfile_asf_dataobject['reserved']).') does not match expected value of "0x0101"';
+						//return false;
+						break;
+					}
+
+					// Data Packets                     array of:    variable        //
+					// * Error Correction Flags         BYTE         8               //
+					// * * Error Correction Data Length bits         4               // if Error Correction Length Type == 00, size of Error Correction Data in bytes, else hardcoded: 0000
+					// * * Opaque Data Present          bits         1               //
+					// * * Error Correction Length Type bits         2               // number of bits for size of the error correction data. hardcoded: 00
+					// * * Error Correction Present     bits         1               // If set, use Opaque Data Packet structure, else use Payload structure
+					// * Error Correction Data
+
+					$ThisFileInfo['avdataoffset'] = ftell($fd);
+					fseek($fd, ($thisfile_asf_dataobject['objectsize'] - 50), SEEK_CUR); // skip actual audio/video data
+					$ThisFileInfo['avdataend'] = ftell($fd);
+					break;
+
+				case GETID3_ASF_Simple_Index_Object:
+					// Simple Index Object: (optional, recommended, one per video stream)
+					// Field Name                       Field Type   Size (bits)
+					// Object ID                        GUID         128             // GUID for Simple Index object - GETID3_ASF_Data_Object
+					// Object Size                      QWORD        64              // size of Simple Index object, including 56 bytes of Simple Index Object header
+					// File ID                          GUID         128             // unique identifier. may be zero or identical to File ID field in Data Object and Header Object
+					// Index Entry Time Interval        QWORD        64              // interval between index entries in 100-nanosecond units
+					// Maximum Packet Count             DWORD        32              // maximum packet count for all index entries
+					// Index Entries Count              DWORD        32              // number of Index Entries structures
+					// Index Entries                    array of:    variable        //
+					// * Packet Number                  DWORD        32              // number of the Data Packet associated with this index entry
+					// * Packet Count                   WORD         16              // number of Data Packets to sent at this index entry
+
+					// shortcut
+					$thisfile_asf['simple_index_object'] = array();
+					$thisfile_asf_simpleindexobject      = &$thisfile_asf['simple_index_object'];
+
+					$SimpleIndexObjectData = $NextObjectDataHeader.fread($fd, 56 - 24);
+					$offset = 24;
+
+					$thisfile_asf_simpleindexobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_simpleindexobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_simpleindexobject['objectsize']                = $NextObjectSize;
+
+					$thisfile_asf_simpleindexobject['fileid']                    =                  substr($SimpleIndexObjectData, $offset, 16);
+					$offset += 16;
+					$thisfile_asf_simpleindexobject['fileid_guid']               = $this->BytestringToGUID($thisfile_asf_simpleindexobject['fileid']);
+					$thisfile_asf_simpleindexobject['index_entry_time_interval'] = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 8));
+					$offset += 8;
+					$thisfile_asf_simpleindexobject['maximum_packet_count']      = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_simpleindexobject['index_entries_count']       = getid3_lib::LittleEndian2Int(substr($SimpleIndexObjectData, $offset, 4));
+					$offset += 4;
+
+					$IndexEntriesData = $SimpleIndexObjectData.fread($fd, 6 * $thisfile_asf_simpleindexobject['index_entries_count']);
+					for ($IndexEntriesCounter = 0; $IndexEntriesCounter < $thisfile_asf_simpleindexobject['index_entries_count']; $IndexEntriesCounter++) {
+						$thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_number'] = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4));
+						$offset += 4;
+						$thisfile_asf_simpleindexobject['index_entries'][$IndexEntriesCounter]['packet_count']  = getid3_lib::LittleEndian2Int(substr($IndexEntriesData, $offset, 4));
+						$offset += 2;
+					}
+
+					break;
+
+				case GETID3_ASF_Index_Object:
+					// 6.2 ASF top-level Index Object (optional but recommended when appropriate, 0 or 1)
+					// Field Name                       Field Type   Size (bits)
+					// Object ID                        GUID         128             // GUID for the Index Object - GETID3_ASF_Index_Object
+					// Object Size                      QWORD        64              // Specifies the size, in bytes, of the Index Object, including at least 34 bytes of Index Object header
+					// Index Entry Time Interval        DWORD        32              // Specifies the time interval between each index entry in ms.
+					// Index Specifiers Count           WORD         16              // Specifies the number of Index Specifiers structures in this Index Object.
+					// Index Blocks Count               DWORD        32              // Specifies the number of Index Blocks structures in this Index Object.
+
+					// Index Entry Time Interval        DWORD        32              // Specifies the time interval between index entries in milliseconds.  This value cannot be 0.
+					// Index Specifiers Count           WORD         16              // Specifies the number of entries in the Index Specifiers list.  Valid values are 1 and greater.
+					// Index Specifiers                 array of:    varies          //
+					// * Stream Number                  WORD         16              // Specifies the stream number that the Index Specifiers refer to. Valid values are between 1 and 127.
+					// * Index Type                     WORD         16              // Specifies Index Type values as follows:
+																					//   1 = Nearest Past Data Packet - indexes point to the data packet whose presentation time is closest to the index entry time.
+																					//   2 = Nearest Past Media Object - indexes point to the closest data packet containing an entire object or first fragment of an object.
+																					//   3 = Nearest Past Cleanpoint. - indexes point to the closest data packet containing an entire object (or first fragment of an object) that has the Cleanpoint Flag set.
+																					//   Nearest Past Cleanpoint is the most common type of index.
+					// Index Entry Count                DWORD        32              // Specifies the number of Index Entries in the block.
+					// * Block Positions                QWORD        varies          // Specifies a list of byte offsets of the beginnings of the blocks relative to the beginning of the first Data Packet (i.e., the beginning of the Data Object + 50 bytes). The number of entries in this list is specified by the value of the Index Specifiers Count field. The order of those byte offsets is tied to the order in which Index Specifiers are listed.
+					// * Index Entries                  array of:    varies          //
+					// * * Offsets                      DWORD        varies          // An offset value of 0xffffffff indicates an invalid offset value
+
+					// shortcut
+					$thisfile_asf['asf_index_object'] = array();
+					$thisfile_asf_asfindexobject      = &$thisfile_asf['asf_index_object'];
+
+					$ASFIndexObjectData = $NextObjectDataHeader.fread($fd, 34 - 24);
+					$offset = 24;
+
+					$thisfile_asf_asfindexobject['objectid']                  = $NextObjectGUID;
+					$thisfile_asf_asfindexobject['objectid_guid']             = $NextObjectGUIDtext;
+					$thisfile_asf_asfindexobject['objectsize']                = $NextObjectSize;
+
+					$thisfile_asf_asfindexobject['entry_time_interval']       = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
+					$offset += 4;
+					$thisfile_asf_asfindexobject['index_specifiers_count']    = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2));
+					$offset += 2;
+					$thisfile_asf_asfindexobject['index_blocks_count']        = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
+					$offset += 4;
+
+					$ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count']);
+					for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
+						$IndexSpecifierStreamNumber = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['stream_number']   = $IndexSpecifierStreamNumber;
+						$thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']      = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 2));
+						$offset += 2;
+						$thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type_text'] = $this->ASFIndexObjectIndexTypeLookup($thisfile_asf_asfindexobject['index_specifiers'][$IndexSpecifiersCounter]['index_type']);
+					}
+
+					$ASFIndexObjectData .= fread($fd, 4);
+					$thisfile_asf_asfindexobject['index_entry_count'] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
+					$offset += 4;
+
+					$ASFIndexObjectData .= fread($fd, 8 * $thisfile_asf_asfindexobject['index_specifiers_count']);
+					for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
+						$thisfile_asf_asfindexobject['block_positions'][$IndexSpecifiersCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 8));
+						$offset += 8;
+					}
+
+					$ASFIndexObjectData .= fread($fd, 4 * $thisfile_asf_asfindexobject['index_specifiers_count'] * $thisfile_asf_asfindexobject['index_entry_count']);
+					for ($IndexEntryCounter = 0; $IndexEntryCounter < $thisfile_asf_asfindexobject['index_entry_count']; $IndexEntryCounter++) {
+						for ($IndexSpecifiersCounter = 0; $IndexSpecifiersCounter < $thisfile_asf_asfindexobject['index_specifiers_count']; $IndexSpecifiersCounter++) {
+							$thisfile_asf_asfindexobject['offsets'][$IndexSpecifiersCounter][$IndexEntryCounter] = getid3_lib::LittleEndian2Int(substr($ASFIndexObjectData, $offset, 4));
+							$offset += 4;
+						}
+					}
+					break;
+
+
+				default:
+					// Implementations shall ignore any standard or non-standard object that they do not know how to handle.
+					if ($this->GUIDname($NextObjectGUIDtext)) {
+						$ThisFileInfo['warning'][] = 'unhandled GUID "'.$this->GUIDname($NextObjectGUIDtext).'" {'.$NextObjectGUIDtext.'} in ASF body at offset '.($offset - 16 - 8);
+					} else {
+						$ThisFileInfo['warning'][] = 'unknown GUID {'.$NextObjectGUIDtext.'} in ASF body at offset '.(ftell($fd) - 16 - 8);
+					}
+					fseek($fd, ($NextObjectSize - 16 - 8), SEEK_CUR);
+					break;
+			}
+		}
+
+		if (isset($thisfile_asf_codeclistobject['codec_entries']) && is_array($thisfile_asf_codeclistobject['codec_entries'])) {
+			foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) {
+				switch ($streamdata['information']) {
+					case 'WMV1':
+					case 'WMV2':
+					case 'WMV3':
+                    case 'MSS1':
+                    case 'MSS2':
+                    case 'WMVA':
+                    case 'WVC1':
+                    case 'WMVP':
+                    case 'WVP2':
+						$thisfile_video['dataformat'] = 'wmv';
+						$ThisFileInfo['mime_type']    = 'video/x-ms-wmv';
+						break;
+
+					case 'MP42':
+					case 'MP43':
+					case 'MP4S':
+					case 'mp4s':
+						$thisfile_video['dataformat'] = 'asf';
+						$ThisFileInfo['mime_type']    = 'video/x-ms-asf';
+						break;
+
+					default:
+						switch ($streamdata['type_raw']) {
+							case 1:
+								if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) {
+									$thisfile_video['dataformat'] = 'wmv';
+									if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') {
+										$ThisFileInfo['mime_type'] = 'video/x-ms-wmv';
+									}
+								}
+								break;
+
+							case 2:
+								if (strstr($this->TrimConvert($streamdata['name']), 'Windows Media')) {
+									$thisfile_audio['dataformat'] = 'wma';
+									if ($ThisFileInfo['mime_type'] == 'video/x-ms-asf') {
+										$ThisFileInfo['mime_type'] = 'audio/x-ms-wma';
+									}
+								}
+								break;
+
+						}
+						break;
+				}
+			}
+		}
+
+		switch (@$thisfile_audio['codec']) {
+			case 'MPEG Layer-3':
+				$thisfile_audio['dataformat'] = 'mp3';
+				break;
+
+			default:
+				break;
+		}
+
+		if (isset($thisfile_asf_codeclistobject['codec_entries'])) {
+			foreach ($thisfile_asf_codeclistobject['codec_entries'] as $streamnumber => $streamdata) {
+				switch ($streamdata['type_raw']) {
+
+					case 1: // video
+						$thisfile_video['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']);
+						break;
+
+					case 2: // audio
+						$thisfile_audio['encoder'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][$streamnumber]['name']);
+
+						// AH 2003-10-01
+						$thisfile_audio['encoder_options'] = $this->TrimConvert($thisfile_asf_codeclistobject['codec_entries'][0]['description']);
+
+						$thisfile_audio['codec']   = $thisfile_audio['encoder'];
+						break;
+
+					default:
+						$ThisFileInfo['warning'][] = 'Unknown streamtype: [codec_list_object][codec_entries]['.$streamnumber.'][type_raw] == '.$streamdata['type_raw'];
+						break;
+
+				}
+			}
+		}
+
+		if (isset($ThisFileInfo['audio'])) {
+			$thisfile_audio['lossless']           = (isset($thisfile_audio['lossless'])           ? $thisfile_audio['lossless']           : false);
+			$thisfile_audio['dataformat']         = (!empty($thisfile_audio['dataformat'])        ? $thisfile_audio['dataformat']         : 'asf');
+		}
+		if (!empty($thisfile_video['dataformat'])) {
+			$thisfile_video['lossless']           = (isset($thisfile_audio['lossless'])           ? $thisfile_audio['lossless']           : false);
+			$thisfile_video['pixel_aspect_ratio'] = (isset($thisfile_audio['pixel_aspect_ratio']) ? $thisfile_audio['pixel_aspect_ratio'] : (float) 1);
+			$thisfile_video['dataformat']         = (!empty($thisfile_video['dataformat'])        ? $thisfile_video['dataformat']         : 'asf');
+		}
+		if (!empty($thisfile_video['streams'])) {
+			$thisfile_video['streams']['resolution_x'] = 0;
+			$thisfile_video['streams']['resolution_y'] = 0;
+			foreach ($thisfile_video['streams'] as $key => $valuearray) {
+				if (($valuearray['resolution_x'] > $thisfile_video['streams']['resolution_x']) || ($valuearray['resolution_y'] > $thisfile_video['streams']['resolution_y'])) {
+					$thisfile_video['resolution_x'] = $valuearray['resolution_x'];
+					$thisfile_video['resolution_y'] = $valuearray['resolution_y'];
+				}
+			}
+		}
+		$ThisFileInfo['bitrate'] = @$thisfile_audio['bitrate'] + @$thisfile_video['bitrate'];
+
+		if ((!isset($ThisFileInfo['playtime_seconds']) || ($ThisFileInfo['playtime_seconds'] <= 0)) && ($ThisFileInfo['bitrate'] > 0)) {
+			$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['filesize'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['bitrate'] / 8);
+		}
+
+		return true;
+	}
+
+	function ASFCodecListObjectTypeLookup($CodecListType) {
+		static $ASFCodecListObjectTypeLookup = array();
+		if (empty($ASFCodecListObjectTypeLookup)) {
+			$ASFCodecListObjectTypeLookup[0x0001] = 'Video Codec';
+			$ASFCodecListObjectTypeLookup[0x0002] = 'Audio Codec';
+			$ASFCodecListObjectTypeLookup[0xFFFF] = 'Unknown Codec';
+		}
+
+		return (isset($ASFCodecListObjectTypeLookup[$CodecListType]) ? $ASFCodecListObjectTypeLookup[$CodecListType] : 'Invalid Codec Type');
+	}
+
+	function KnownGUIDs() {
+		static $GUIDarray = array();
+		if (empty($GUIDarray)) {
+			$GUIDarray['GETID3_ASF_Extended_Stream_Properties_Object']   = '14E6A5CB-C672-4332-8399-A96952065B5A';
+			$GUIDarray['GETID3_ASF_Padding_Object']                      = '1806D474-CADF-4509-A4BA-9AABCB96AAE8';
+			$GUIDarray['GETID3_ASF_Payload_Ext_Syst_Pixel_Aspect_Ratio'] = '1B1EE554-F9EA-4BC8-821A-376B74E4C4B8';
+			$GUIDarray['GETID3_ASF_Script_Command_Object']               = '1EFB1A30-0B62-11D0-A39B-00A0C90348F6';
+			$GUIDarray['GETID3_ASF_No_Error_Correction']                 = '20FB5700-5B55-11CF-A8FD-00805F5C442B';
+			$GUIDarray['GETID3_ASF_Content_Branding_Object']             = '2211B3FA-BD23-11D2-B4B7-00A0C955FC6E';
+			$GUIDarray['GETID3_ASF_Content_Encryption_Object']           = '2211B3FB-BD23-11D2-B4B7-00A0C955FC6E';
+			$GUIDarray['GETID3_ASF_Digital_Signature_Object']            = '2211B3FC-BD23-11D2-B4B7-00A0C955FC6E';
+			$GUIDarray['GETID3_ASF_Extended_Content_Encryption_Object']  = '298AE614-2622-4C17-B935-DAE07EE9289C';
+			$GUIDarray['GETID3_ASF_Simple_Index_Object']                 = '33000890-E5B1-11CF-89F4-00A0C90349CB';
+			$GUIDarray['GETID3_ASF_Degradable_JPEG_Media']               = '35907DE0-E415-11CF-A917-00805F5C442B';
+			$GUIDarray['GETID3_ASF_Payload_Extension_System_Timecode']   = '399595EC-8667-4E2D-8FDB-98814CE76C1E';
+			$GUIDarray['GETID3_ASF_Binary_Media']                        = '3AFB65E2-47EF-40F2-AC2C-70A90D71D343';
+			$GUIDarray['GETID3_ASF_Timecode_Index_Object']               = '3CB73FD0-0C4A-4803-953D-EDF7B6228F0C';
+			$GUIDarray['GETID3_ASF_Metadata_Library_Object']             = '44231C94-9498-49D1-A141-1D134E457054';
+			$GUIDarray['GETID3_ASF_Reserved_3']                          = '4B1ACBE3-100B-11D0-A39B-00A0C90348F6';
+			$GUIDarray['GETID3_ASF_Reserved_4']                          = '4CFEDB20-75F6-11CF-9C0F-00A0C90349CB';
+			$GUIDarray['GETID3_ASF_Command_Media']                       = '59DACFC0-59E6-11D0-A3AC-00A0C90348F6';
+			$GUIDarray['GETID3_ASF_Header_Extension_Object']             = '5FBF03B5-A92E-11CF-8EE3-00C00C205365';
+			$GUIDarray['GETID3_ASF_Media_Object_Index_Parameters_Obj']   = '6B203BAD-3F11-4E84-ACA8-D7613DE2CFA7';
+			$GUIDarray['GETID3_ASF_Header_Object']                       = '75B22630-668E-11CF-A6D9-00AA0062CE6C';
+			$GUIDarray['GETID3_ASF_Content_Description_Object']          = '75B22633-668E-11CF-A6D9-00AA0062CE6C';
+			$GUIDarray['GETID3_ASF_Error_Correction_Object']             = '75B22635-668E-11CF-A6D9-00AA0062CE6C';
+			$GUIDarray['GETID3_ASF_Data_Object']                         = '75B22636-668E-11CF-A6D9-00AA0062CE6C';
+			$GUIDarray['GETID3_ASF_Web_Stream_Media_Subtype']            = '776257D4-C627-41CB-8F81-7AC7FF1C40CC';
+			$GUIDarray['GETID3_ASF_Stream_Bitrate_Properties_Object']    = '7BF875CE-468D-11D1-8D82-006097C9A2B2';
+			$GUIDarray['GETID3_ASF_Language_List_Object']                = '7C4346A9-EFE0-4BFC-B229-393EDE415C85';
+			$GUIDarray['GETID3_ASF_Codec_List_Object']                   = '86D15240-311D-11D0-A3A4-00A0C90348F6';
+			$GUIDarray['GETID3_ASF_Reserved_2']                          = '86D15241-311D-11D0-A3A4-00A0C90348F6';
+			$GUIDarray['GETID3_ASF_File_Properties_Object']              = '8CABDCA1-A947-11CF-8EE4-00C00C205365';
+			$GUIDarray['GETID3_ASF_File_Transfer_Media']                 = '91BD222C-F21C-497A-8B6D-5AA86BFC0185';
+			$GUIDarray['GETID3_ASF_Old_RTP_Extension_Data']              = '96800C63-4C94-11D1-837B-0080C7A37F95';
+			$GUIDarray['GETID3_ASF_Advanced_Mutual_Exclusion_Object']    = 'A08649CF-4775-4670-8A16-6E35357566CD';
+			$GUIDarray['GETID3_ASF_Bandwidth_Sharing_Object']            = 'A69609E6-517B-11D2-B6AF-00C04FD908E9';
+			$GUIDarray['GETID3_ASF_Reserved_1']                          = 'ABD3D211-A9BA-11cf-8EE6-00C00C205365';
+			$GUIDarray['GETID3_ASF_Bandwidth_Sharing_Exclusive']         = 'AF6060AA-5197-11D2-B6AF-00C04FD908E9';
+			$GUIDarray['GETID3_ASF_Bandwidth_Sharing_Partial']           = 'AF6060AB-5197-11D2-B6AF-00C04FD908E9';
+			$GUIDarray['GETID3_ASF_JFIF_Media']                          = 'B61BE100-5B4E-11CF-A8FD-00805F5C442B';
+			$GUIDarray['GETID3_ASF_Stream_Properties_Object']            = 'B7DC0791-A9B7-11CF-8EE6-00C00C205365';
+			$GUIDarray['GETID3_ASF_Video_Media']                         = 'BC19EFC0-5B4D-11CF-A8FD-00805F5C442B';
+			$GUIDarray['GETID3_ASF_Audio_Spread']                        = 'BFC3CD50-618F-11CF-8BB2-00AA00B4E220';
+			$GUIDarray['GETID3_ASF_Metadata_Object']                     = 'C5F8CBEA-5BAF-4877-8467-AA8C44FA4CCA';
+			$GUIDarray['GETID3_ASF_Payload_Ext_Syst_Sample_Duration']    = 'C6BD9450-867F-4907-83A3-C77921B733AD';
+			$GUIDarray['GETID3_ASF_Group_Mutual_Exclusion_Object']       = 'D1465A40-5A79-4338-B71B-E36B8FD6C249';
+			$GUIDarray['GETID3_ASF_Extended_Content_Description_Object'] = 'D2D0A440-E307-11D2-97F0-00A0C95EA850';
+			$GUIDarray['GETID3_ASF_Stream_Prioritization_Object']        = 'D4FED15B-88D3-454F-81F0-ED5C45999E24';
+			$GUIDarray['GETID3_ASF_Payload_Ext_System_Content_Type']     = 'D590DC20-07BC-436C-9CF7-F3BBFBF1A4DC';
+			$GUIDarray['GETID3_ASF_Old_File_Properties_Object']          = 'D6E229D0-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_ASF_Header_Object']               = 'D6E229D1-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_ASF_Data_Object']                 = 'D6E229D2-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Index_Object']                        = 'D6E229D3-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Stream_Properties_Object']        = 'D6E229D4-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Content_Description_Object']      = 'D6E229D5-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Script_Command_Object']           = 'D6E229D6-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Marker_Object']                   = 'D6E229D7-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Component_Download_Object']       = 'D6E229D8-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Stream_Group_Object']             = 'D6E229D9-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Scalable_Object']                 = 'D6E229DA-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Prioritization_Object']           = 'D6E229DB-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Bitrate_Mutual_Exclusion_Object']     = 'D6E229DC-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Inter_Media_Dependency_Object']   = 'D6E229DD-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Rating_Object']                   = 'D6E229DE-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Index_Parameters_Object']             = 'D6E229DF-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Color_Table_Object']              = 'D6E229E0-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Language_List_Object']            = 'D6E229E1-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Audio_Media']                     = 'D6E229E2-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Video_Media']                     = 'D6E229E3-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Image_Media']                     = 'D6E229E4-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Timecode_Media']                  = 'D6E229E5-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Text_Media']                      = 'D6E229E6-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_MIDI_Media']                      = 'D6E229E7-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Command_Media']                   = 'D6E229E8-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_No_Error_Concealment']            = 'D6E229EA-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Scrambled_Audio']                 = 'D6E229EB-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_No_Color_Table']                  = 'D6E229EC-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_SMPTE_Time']                      = 'D6E229ED-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_ASCII_Text']                      = 'D6E229EE-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Unicode_Text']                    = 'D6E229EF-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_HTML_Text']                       = 'D6E229F0-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_URL_Command']                     = 'D6E229F1-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Filename_Command']                = 'D6E229F2-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_ACM_Codec']                       = 'D6E229F3-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_VCM_Codec']                       = 'D6E229F4-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_QuickTime_Codec']                 = 'D6E229F5-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_DirectShow_Transform_Filter']     = 'D6E229F6-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_DirectShow_Rendering_Filter']     = 'D6E229F7-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_No_Enhancement']                  = 'D6E229F8-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Unknown_Enhancement_Type']        = 'D6E229F9-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Temporal_Enhancement']            = 'D6E229FA-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Spatial_Enhancement']             = 'D6E229FB-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Quality_Enhancement']             = 'D6E229FC-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Number_of_Channels_Enhancement']  = 'D6E229FD-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Frequency_Response_Enhancement']  = 'D6E229FE-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Media_Object']                    = 'D6E229FF-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Mutex_Language']                      = 'D6E22A00-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Mutex_Bitrate']                       = 'D6E22A01-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Mutex_Unknown']                       = 'D6E22A02-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_ASF_Placeholder_Object']          = 'D6E22A0E-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Old_Data_Unit_Extension_Object']      = 'D6E22A0F-35DA-11D1-9034-00A0C90349BE';
+			$GUIDarray['GETID3_ASF_Web_Stream_Format']                   = 'DA1E6B13-8359-4050-B398-388E965BF00C';
+			$GUIDarray['GETID3_ASF_Payload_Ext_System_File_Name']        = 'E165EC0E-19ED-45D7-B4A7-25CBD1E28E9B';
+			$GUIDarray['GETID3_ASF_Marker_Object']                       = 'F487CD01-A951-11CF-8EE6-00C00C205365';
+			$GUIDarray['GETID3_ASF_Timecode_Index_Parameters_Object']    = 'F55E496D-9797-4B5D-8C8B-604DFE9BFB24';
+			$GUIDarray['GETID3_ASF_Audio_Media']                         = 'F8699E40-5B4D-11CF-A8FD-00805F5C442B';
+			$GUIDarray['GETID3_ASF_Media_Object_Index_Object']           = 'FEB103F8-12AD-4C64-840F-2A1D2F7AD48C';
+			$GUIDarray['GETID3_ASF_Alt_Extended_Content_Encryption_Obj'] = 'FF889EF1-ADEE-40DA-9E71-98704BB928CE';
+		}
+		return $GUIDarray;
+	}
+
+	function GUIDname($GUIDstring) {
+		static $GUIDarray = array();
+		if (empty($GUIDarray)) {
+			$GUIDarray = $this->KnownGUIDs();
+		}
+		return array_search($GUIDstring, $GUIDarray);
+	}
+
+	function ASFIndexObjectIndexTypeLookup($id) {
+		static $ASFIndexObjectIndexTypeLookup = array();
+		if (empty($ASFIndexObjectIndexTypeLookup)) {
+			$ASFIndexObjectIndexTypeLookup[1] = 'Nearest Past Data Packet';
+			$ASFIndexObjectIndexTypeLookup[2] = 'Nearest Past Media Object';
+			$ASFIndexObjectIndexTypeLookup[3] = 'Nearest Past Cleanpoint';
+		}
+		return (isset($ASFIndexObjectIndexTypeLookup[$id]) ? $ASFIndexObjectIndexTypeLookup[$id] : 'invalid');
+	}
+
+	function GUIDtoBytestring($GUIDstring) {
+		// Microsoft defines these 16-byte (128-bit) GUIDs in the strangest way:
+		// first 4 bytes are in little-endian order
+		// next 2 bytes are appended in little-endian order
+		// next 2 bytes are appended in little-endian order
+		// next 2 bytes are appended in big-endian order
+		// next 6 bytes are appended in big-endian order
+
+		// AaBbCcDd-EeFf-GgHh-IiJj-KkLlMmNnOoPp is stored as this 16-byte string:
+		// $Dd $Cc $Bb $Aa $Ff $Ee $Hh $Gg $Ii $Jj $Kk $Ll $Mm $Nn $Oo $Pp
+
+		$hexbytecharstring  = chr(hexdec(substr($GUIDstring,  6, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  4, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  2, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  0, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 11, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring,  9, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 16, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 14, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 19, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 21, 2)));
+
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 24, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 26, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 28, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 30, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 32, 2)));
+		$hexbytecharstring .= chr(hexdec(substr($GUIDstring, 34, 2)));
+
+		return $hexbytecharstring;
+	}
+
+	function BytestringToGUID($Bytestring) {
+		$GUIDstring  = str_pad(dechex(ord($Bytestring{3})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{2})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{1})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{0})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{5})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{4})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{7})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{6})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{8})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{9})),  2, '0', STR_PAD_LEFT);
+		$GUIDstring .= '-';
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{10})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{11})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{12})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{13})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{14})), 2, '0', STR_PAD_LEFT);
+		$GUIDstring .= str_pad(dechex(ord($Bytestring{15})), 2, '0', STR_PAD_LEFT);
+
+		return strtoupper($GUIDstring);
+	}
+
+	function FILETIMEtoUNIXtime($FILETIME, $round=true) {
+		// FILETIME is a 64-bit unsigned integer representing
+		// the number of 100-nanosecond intervals since January 1, 1601
+		// UNIX timestamp is number of seconds since January 1, 1970
+		// 116444736000000000 = 10000000 * 60 * 60 * 24 * 365 * 369 + 89 leap days
+		if ($round) {
+			return intval(round(($FILETIME - 116444736000000000) / 10000000));
+		}
+		return ($FILETIME - 116444736000000000) / 10000000;
+	}
+
+	function WMpictureTypeLookup($WMpictureType) {
+		static $WMpictureTypeLookup = array();
+		if (empty($WMpictureTypeLookup)) {
+			$WMpictureTypeLookup[0x03] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Front Cover');
+			$WMpictureTypeLookup[0x04] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Back Cover');
+			$WMpictureTypeLookup[0x00] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'User Defined');
+			$WMpictureTypeLookup[0x05] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Leaflet Page');
+			$WMpictureTypeLookup[0x06] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Media Label');
+			$WMpictureTypeLookup[0x07] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lead Artist');
+			$WMpictureTypeLookup[0x08] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Artist');
+			$WMpictureTypeLookup[0x09] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Conductor');
+			$WMpictureTypeLookup[0x0A] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band');
+			$WMpictureTypeLookup[0x0B] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Composer');
+			$WMpictureTypeLookup[0x0C] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Lyricist');
+			$WMpictureTypeLookup[0x0D] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Recording Location');
+			$WMpictureTypeLookup[0x0E] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Recording');
+			$WMpictureTypeLookup[0x0F] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'During Performance');
+			$WMpictureTypeLookup[0x10] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Video Screen Capture');
+			$WMpictureTypeLookup[0x12] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Illustration');
+			$WMpictureTypeLookup[0x13] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Band Logotype');
+			$WMpictureTypeLookup[0x14] = getid3_lib::iconv_fallback('ISO-8859-1', 'UTF-16LE', 'Publisher Logotype');
+		}
+		return @$WMpictureTypeLookup[$WMpictureType];
+	}
+
+
+	// Remove terminator 00 00 and convert UNICODE to Latin-1
+	function TrimConvert($string) {
+
+		// remove terminator, only if present (it should be, but...)
+		if (substr($string, strlen($string) - 2, 2) == "\x00\x00") {
+			$string = substr($string, 0, strlen($string) - 2);
+		}
+
+		// convert
+		return trim(getid3_lib::iconv_fallback('UTF-16LE', 'ISO-8859-1', $string), ' ');
+	}
+
+
+	function TrimTerm($string) {
+
+		// remove terminator, only if present (it should be, but...)
+		if (substr($string, -2) == "\x00\x00") {
+			$string = substr($string, 0, -2);
+		}
+		return $string;
+	}
+
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.bink.php b/apps/media/getID3/getid3/module.audio-video.bink.php
new file mode 100644
index 0000000000000000000000000000000000000000..2ebc6fe797cd36b90f9bba2c91d5bb766d797a19
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.bink.php
@@ -0,0 +1,70 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.bink.php                                       //
+// module for analyzing Bink or Smacker audio-video files      //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_bink
+{
+
+	function getid3_bink(&$fd, &$ThisFileInfo) {
+
+$ThisFileInfo['error'][] = 'Bink / Smacker files not properly processed by this version of getID3()';
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$fileTypeID = fread($fd, 3);
+		switch ($fileTypeID) {
+			case 'BIK':
+				return $this->ParseBink($fd, $ThisFileInfo);
+				break;
+
+			case 'SMK':
+				return $this->ParseSmacker($fd, $ThisFileInfo);
+				break;
+
+			default:
+				$ThisFileInfo['error'][] = 'Expecting "BIK" or "SMK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$fileTypeID.'"';
+				return false;
+				break;
+		}
+
+		return true;
+
+	}
+
+	function ParseBink(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']          = 'bink';
+		$ThisFileInfo['video']['dataformat'] = 'bink';
+
+		$fileData = 'BIK'.fread($fd, 13);
+
+		$ThisFileInfo['bink']['data_size']   = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
+		$ThisFileInfo['bink']['frame_count'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 2));
+
+		if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['bink']['data_size'] + 8)) {
+			$ThisFileInfo['error'][] = 'Probably truncated file: expecting '.$ThisFileInfo['bink']['data_size'].' bytes, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
+		}
+
+		return true;
+	}
+
+	function ParseSmacker(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']          = 'smacker';
+		$ThisFileInfo['video']['dataformat'] = 'smacker';
+
+		return false;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.flv.php b/apps/media/getID3/getid3/module.audio-video.flv.php
new file mode 100644
index 0000000000000000000000000000000000000000..69c48c1b31f2a7674f5dd292c79ad8e4aa959397
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.flv.php
@@ -0,0 +1,505 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+//                                                             //
+//  FLV module by Seth Kaufman <seth@whirl-i-gig.com>          //
+//                                                             //
+//  * version 0.1 (26 June 2005)                               //
+//                                                             //
+//  minor modifications by James Heinrich <info@getid3.org>    //
+//  * version 0.1.1 (15 July 2005)                             //
+//                                                             //
+//  Support for On2 VP6 codec and meta information             //
+//    by Steve Webster <steve.webster@featurecreep.com>        //
+//  * version 0.2 (22 February 2006)                           //
+//                                                             //
+//  Modified to not read entire file into memory               //
+//    by James Heinrich <info@getid3.org>                      //
+//  * version 0.3 (15 June 2006)                               //
+//                                                             //
+//  Bugfixes for incorrectly parsed FLV dimensions             //
+//    and incorrect parsing of onMetaTag                       //
+//    by Evgeny Moysevich <moysevich@gmail.com>                //
+//  * version 0.4 (07 December 2007)                           //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.flv.php                                  //
+// module for analyzing Shockwave Flash Video files            //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+define('GETID3_FLV_TAG_AUDIO', 8);
+define('GETID3_FLV_TAG_VIDEO', 9);
+define('GETID3_FLV_TAG_META', 18);
+
+define('GETID3_FLV_VIDEO_H263',   2);
+define('GETID3_FLV_VIDEO_SCREEN', 3);
+define('GETID3_FLV_VIDEO_VP6',    4);
+
+class getid3_flv
+{
+
+	function getid3_flv(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
+//$start_time = microtime(true);
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$FLVdataLength = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
+		$FLVheader = fread($fd, 5);
+
+		$ThisFileInfo['fileformat'] = 'flv';
+		$ThisFileInfo['flv']['header']['signature'] =                           substr($FLVheader, 0, 3);
+		$ThisFileInfo['flv']['header']['version']   = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
+		$TypeFlags                                  = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
+
+		if ($ThisFileInfo['flv']['header']['signature'] != 'FLV') {
+			$ThisFileInfo['error'][] = 'Expecting "FLV" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['flv']['header']['signature'].'"';
+			unset($ThisFileInfo['flv']);
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+
+		$ThisFileInfo['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
+		$ThisFileInfo['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
+
+		$FrameSizeDataLength = getid3_lib::BigEndian2Int(fread($fd, 4));
+		$FLVheaderFrameLength = 9;
+		if ($FrameSizeDataLength > $FLVheaderFrameLength) {
+			fseek($fd, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
+		}
+//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
+
+		$Duration = 0;
+		$found_video = false;
+		$found_audio = false;
+		$found_meta  = false;
+		while ((ftell($fd) + 16) < $ThisFileInfo['avdataend']) {
+			$ThisTagHeader = fread($fd, 16);
+
+			$PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  0, 4));
+			$TagType           = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  4, 1));
+			$DataLength        = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  5, 3));
+			$Timestamp         = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  8, 3));
+			$LastHeaderByte    = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
+			$NextOffset = ftell($fd) - 1 + $DataLength;
+			if ($Timestamp > $Duration) {
+				$Duration = $Timestamp;
+			}
+
+//echo __LINE__.'['.ftell($fd).']=('.$TagType.')='.number_format(microtime(true) - $start_time, 3).'<br>';
+
+			switch ($TagType) {
+				case GETID3_FLV_TAG_AUDIO:
+					if (!$found_audio) {
+						$found_audio = true;
+						$ThisFileInfo['flv']['audio']['audioFormat']     =  $LastHeaderByte & 0x07;
+						$ThisFileInfo['flv']['audio']['audioRate']       = ($LastHeaderByte & 0x30) / 0x10;
+						$ThisFileInfo['flv']['audio']['audioSampleSize'] = ($LastHeaderByte & 0x40) / 0x40;
+						$ThisFileInfo['flv']['audio']['audioType']       = ($LastHeaderByte & 0x80) / 0x80;
+					}
+					break;
+
+				case GETID3_FLV_TAG_VIDEO:
+					if (!$found_video) {
+						$found_video = true;
+						$ThisFileInfo['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
+
+						$FLVvideoHeader = fread($fd, 11);
+
+						if ($ThisFileInfo['flv']['video']['videoCodec'] != GETID3_FLV_VIDEO_VP6) {
+
+							$PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
+							$PictureSizeType = $PictureSizeType & 0x0007;
+							$ThisFileInfo['flv']['header']['videoSizeType'] = $PictureSizeType;
+							switch ($PictureSizeType) {
+								case 0:
+									//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
+									//$PictureSizeEnc <<= 1;
+									//$ThisFileInfo['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
+									//$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
+									//$PictureSizeEnc <<= 1;
+									//$ThisFileInfo['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
+
+									$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
+									$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
+									$PictureSizeEnc['x'] >>= 7;
+									$PictureSizeEnc['y'] >>= 7;
+									$ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
+									$ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
+									break;
+
+								case 1:
+									$PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
+									$PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
+									$PictureSizeEnc['x'] >>= 7;
+									$PictureSizeEnc['y'] >>= 7;
+									$ThisFileInfo['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
+									$ThisFileInfo['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
+									break;
+
+								case 2:
+									$ThisFileInfo['video']['resolution_x'] = 352;
+									$ThisFileInfo['video']['resolution_y'] = 288;
+									break;
+
+								case 3:
+									$ThisFileInfo['video']['resolution_x'] = 176;
+									$ThisFileInfo['video']['resolution_y'] = 144;
+									break;
+
+								case 4:
+									$ThisFileInfo['video']['resolution_x'] = 128;
+									$ThisFileInfo['video']['resolution_y'] = 96;
+									break;
+
+								case 5:
+									$ThisFileInfo['video']['resolution_x'] = 320;
+									$ThisFileInfo['video']['resolution_y'] = 240;
+									break;
+
+								case 6:
+									$ThisFileInfo['video']['resolution_x'] = 160;
+									$ThisFileInfo['video']['resolution_y'] = 120;
+									break;
+
+								default:
+									$ThisFileInfo['video']['resolution_x'] = 0;
+									$ThisFileInfo['video']['resolution_y'] = 0;
+									break;
+
+							}
+						}
+					}
+					break;
+
+				// Meta tag
+				case GETID3_FLV_TAG_META:
+					if (!$found_meta) {
+						$found_meta = true;
+						fseek($fd, -1, SEEK_CUR);
+						$reader = new AMFReader(new AMFStream(fread($fd, $DataLength)));
+						$eventName = $reader->readData();
+						$ThisFileInfo['meta'][$eventName] = $reader->readData();
+						unset($reader);
+
+						$ThisFileInfo['video']['frame_rate']   = @$ThisFileInfo['meta']['onMetaData']['framerate'];
+						$ThisFileInfo['video']['resolution_x'] = @$ThisFileInfo['meta']['onMetaData']['width'];
+						$ThisFileInfo['video']['resolution_y'] = @$ThisFileInfo['meta']['onMetaData']['height'];
+					}
+					break;
+
+				default:
+					// noop
+					break;
+			}
+
+			fseek($fd, $NextOffset, SEEK_SET);
+		}
+
+		if ($ThisFileInfo['playtime_seconds'] = $Duration / 1000) {
+		    $ThisFileInfo['bitrate'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds'];
+		}
+
+		if ($ThisFileInfo['flv']['header']['hasAudio']) {
+			$ThisFileInfo['audio']['codec']           =   $this->FLVaudioFormat($ThisFileInfo['flv']['audio']['audioFormat']);
+			$ThisFileInfo['audio']['sample_rate']     =     $this->FLVaudioRate($ThisFileInfo['flv']['audio']['audioRate']);
+			$ThisFileInfo['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($ThisFileInfo['flv']['audio']['audioSampleSize']);
+
+			$ThisFileInfo['audio']['channels']   = $ThisFileInfo['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
+			$ThisFileInfo['audio']['lossless']   = ($ThisFileInfo['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
+			$ThisFileInfo['audio']['dataformat'] = 'flv';
+		}
+		if (@$ThisFileInfo['flv']['header']['hasVideo']) {
+			$ThisFileInfo['video']['codec']      = $this->FLVvideoCodec($ThisFileInfo['flv']['video']['videoCodec']);
+			$ThisFileInfo['video']['dataformat'] = 'flv';
+			$ThisFileInfo['video']['lossless']   = false;
+		}
+
+		return true;
+	}
+
+
+	function FLVaudioFormat($id) {
+		$FLVaudioFormat = array(
+			0 => 'uncompressed',
+			1 => 'ADPCM',
+			2 => 'mp3',
+			5 => 'Nellymoser 8kHz mono',
+			6 => 'Nellymoser',
+		);
+		return (@$FLVaudioFormat[$id] ? @$FLVaudioFormat[$id] : false);
+	}
+
+	function FLVaudioRate($id) {
+		$FLVaudioRate = array(
+			0 =>  5500,
+			1 => 11025,
+			2 => 22050,
+			3 => 44100,
+		);
+		return (@$FLVaudioRate[$id] ? @$FLVaudioRate[$id] : false);
+	}
+
+	function FLVaudioBitDepth($id) {
+		$FLVaudioBitDepth = array(
+			0 =>  8,
+			1 => 16,
+		);
+		return (@$FLVaudioBitDepth[$id] ? @$FLVaudioBitDepth[$id] : false);
+	}
+
+	function FLVvideoCodec($id) {
+		$FLVvideoCodec = array(
+			GETID3_FLV_VIDEO_H263   => 'Sorenson H.263',
+			GETID3_FLV_VIDEO_SCREEN => 'Screen video',
+			GETID3_FLV_VIDEO_VP6    => 'On2 VP6',
+		);
+		return (@$FLVvideoCodec[$id] ? @$FLVvideoCodec[$id] : false);
+	}
+}
+
+class AMFStream {
+	var $bytes;
+	var $pos;
+
+	function AMFStream(&$bytes) {
+		$this->bytes =& $bytes;
+		$this->pos = 0;
+	}
+
+	function readByte() {
+		return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
+	}
+
+	function readInt() {
+		return ($this->readByte() << 8) + $this->readByte();
+	}
+
+	function readLong() {
+		return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
+	}
+
+	function readDouble() {
+		return getid3_lib::BigEndian2Float($this->read(8));
+	}
+
+	function readUTF() {
+		$length = $this->readInt();
+		return $this->read($length);
+	}
+
+	function readLongUTF() {
+		$length = $this->readLong();
+		return $this->read($length);
+	}
+
+	function read($length) {
+		$val = substr($this->bytes, $this->pos, $length);
+		$this->pos += $length;
+		return $val;
+	}
+
+	function peekByte() {
+		$pos = $this->pos;
+		$val = $this->readByte();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekInt() {
+		$pos = $this->pos;
+		$val = $this->readInt();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekLong() {
+		$pos = $this->pos;
+		$val = $this->readLong();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekDouble() {
+		$pos = $this->pos;
+		$val = $this->readDouble();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekUTF() {
+		$pos = $this->pos;
+		$val = $this->readUTF();
+		$this->pos = $pos;
+		return $val;
+	}
+
+	function peekLongUTF() {
+		$pos = $this->pos;
+		$val = $this->readLongUTF();
+		$this->pos = $pos;
+		return $val;
+	}
+}
+
+class AMFReader {
+	var $stream;
+
+	function AMFReader(&$stream) {
+		$this->stream =& $stream;
+	}
+
+	function readData() {
+		$value = null;
+
+		$type = $this->stream->readByte();
+		switch ($type) {
+
+			// Double
+			case 0:
+				$value = $this->readDouble();
+			break;
+
+			// Boolean
+			case 1:
+				$value = $this->readBoolean();
+				break;
+
+			// String
+			case 2:
+				$value = $this->readString();
+				break;
+
+			// Object
+			case 3:
+				$value = $this->readObject();
+				break;
+
+			// null
+			case 6:
+				return null;
+				break;
+
+			// Mixed array
+			case 8:
+				$value = $this->readMixedArray();
+				break;
+
+			// Array
+			case 10:
+				$value = $this->readArray();
+				break;
+
+			// Date
+			case 11:
+				$value = $this->readDate();
+				break;
+
+			// Long string
+			case 13:
+				$value = $this->readLongString();
+				break;
+
+			// XML (handled as string)
+			case 15:
+				$value = $this->readXML();
+				break;
+
+			// Typed object (handled as object)
+			case 16:
+				$value = $this->readTypedObject();
+				break;
+
+			// Long string
+			default:
+				$value = '(unknown or unsupported data type)';
+			break;
+		}
+
+		return $value;
+	}
+
+	function readDouble() {
+		return $this->stream->readDouble();
+	}
+
+	function readBoolean() {
+		return $this->stream->readByte() == 1;
+	}
+
+	function readString() {
+		return $this->stream->readUTF();
+	}
+
+	function readObject() {
+		// Get highest numerical index - ignored
+//		$highestIndex = $this->stream->readLong();
+
+		$data = array();
+
+		while ($key = $this->stream->readUTF()) {
+			$data[$key] = $this->readData();
+		}
+		// Mixed array record ends with empty string (0x00 0x00) and 0x09
+		if (($key == '') && ($this->stream->peekByte() == 0x09)) {
+			// Consume byte
+			$this->stream->readByte();
+		}
+		return $data;
+	}
+
+	function readMixedArray() {
+		// Get highest numerical index - ignored
+		$highestIndex = $this->stream->readLong();
+
+		$data = array();
+
+		while ($key = $this->stream->readUTF()) {
+			if (is_numeric($key)) {
+				$key = (float) $key;
+			}
+			$data[$key] = $this->readData();
+		}
+		// Mixed array record ends with empty string (0x00 0x00) and 0x09
+		if (($key == '') && ($this->stream->peekByte() == 0x09)) {
+			// Consume byte
+			$this->stream->readByte();
+		}
+
+		return $data;
+	}
+
+	function readArray() {
+		$length = $this->stream->readLong();
+		$data = array();
+
+		for ($i = 0; $i < $length; $i++) {
+			$data[] = $this->readData();
+		}
+		return $data;
+	}
+
+	function readDate() {
+		$timestamp = $this->stream->readDouble();
+		$timezone = $this->stream->readInt();
+		return $timestamp;
+	}
+
+	function readLongString() {
+		return $this->stream->readLongUTF();
+	}
+
+	function readXML() {
+		return $this->stream->readLongUTF();
+	}
+
+	function readTypedObject() {
+		$className = $this->stream->readUTF();
+		return $this->readObject();
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.matroska.php b/apps/media/getID3/getid3/module.audio-video.matroska.php
new file mode 100644
index 0000000000000000000000000000000000000000..004056edb0ee26b845a9040f2b0feffbe5b6e159
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.matroska.php
@@ -0,0 +1,1712 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.matriska.php                             //
+// module for analyzing Matroska containers                    //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+define('EBML_ID_CHAPTERS',                  0x0043A770); // [10][43][A7][70] -- A system to define basic menus and partition data. For more detailed information, look at the Chapters Explanation.
+define('EBML_ID_SEEKHEAD',                  0x014D9B74); // [11][4D][9B][74] -- Contains the position of other level 1 elements.
+define('EBML_ID_TAGS',                      0x0254C367); // [12][54][C3][67] -- Element containing elements specific to Tracks/Chapters. A list of valid tags can be found here.
+define('EBML_ID_INFO',                      0x0549A966); // [15][49][A9][66] -- Contains miscellaneous general information and statistics on the file.
+define('EBML_ID_TRACKS',                    0x0654AE6B); // [16][54][AE][6B] -- A top-level block of information with many tracks described.
+define('EBML_ID_SEGMENT',                   0x08538067); // [18][53][80][67] -- This element contains all other top-level (level 1) elements. Typically a Matroska file is composed of 1 segment.
+define('EBML_ID_ATTACHMENTS',               0x0941A469); // [19][41][A4][69] -- Contain attached files.
+define('EBML_ID_EBML',                      0x0A45DFA3); // [1A][45][DF][A3] -- Set the EBML characteristics of the data to follow. Each EBML document has to start with this.
+define('EBML_ID_CUES',                      0x0C53BB6B); // [1C][53][BB][6B] -- A top-level element to speed seeking access. All entries are local to the segment.
+define('EBML_ID_CLUSTER',                   0x0F43B675); // [1F][43][B6][75] -- The lower level element containing the (monolithic) Block structure.
+define('EBML_ID_LANGUAGE',                    0x02B59C); //     [22][B5][9C] -- Specifies the language of the track in the Matroska languages form.
+define('EBML_ID_TRACKTIMECODESCALE',          0x03314F); //     [23][31][4F] -- The scale to apply on this track to work at normal speed in relation with other tracks (mostly used to adjust video speed when the audio length differs).
+define('EBML_ID_DEFAULTDURATION',             0x03E383); //     [23][E3][83] -- Number of nanoseconds (i.e. not scaled) per frame.
+define('EBML_ID_CODECNAME',                   0x058688); //     [25][86][88] -- A human-readable string specifying the codec.
+define('EBML_ID_CODECDOWNLOADURL',            0x06B240); //     [26][B2][40] -- A URL to download about the codec used.
+define('EBML_ID_TIMECODESCALE',               0x0AD7B1); //     [2A][D7][B1] -- Timecode scale in nanoseconds (1.000.000 means all timecodes in the segment are expressed in milliseconds).
+define('EBML_ID_COLOURSPACE',                 0x0EB524); //     [2E][B5][24] -- Same value as in AVI (32 bits).
+define('EBML_ID_GAMMAVALUE',                  0x0FB523); //     [2F][B5][23] -- Gamma Value.
+define('EBML_ID_CODECSETTINGS',               0x1A9697); //     [3A][96][97] -- A string describing the encoding setting used.
+define('EBML_ID_CODECINFOURL',                0x1B4040); //     [3B][40][40] -- A URL to find information about the codec used.
+define('EBML_ID_PREVFILENAME',                0x1C83AB); //     [3C][83][AB] -- An escaped filename corresponding to the previous segment.
+define('EBML_ID_PREVUID',                     0x1CB923); //     [3C][B9][23] -- A unique ID to identify the previous chained segment (128 bits).
+define('EBML_ID_NEXTFILENAME',                0x1E83BB); //     [3E][83][BB] -- An escaped filename corresponding to the next segment.
+define('EBML_ID_NEXTUID',                     0x1EB923); //     [3E][B9][23] -- A unique ID to identify the next chained segment (128 bits).
+define('EBML_ID_CONTENTCOMPALGO',               0x0254); //         [42][54] -- The compression algorithm used. Algorithms that have been specified so far are:
+define('EBML_ID_CONTENTCOMPSETTINGS',           0x0255); //         [42][55] -- Settings that might be needed by the decompressor. For Header Stripping (ContentCompAlgo=3), the bytes that were removed from the beggining of each frames of the track.
+define('EBML_ID_DOCTYPE',                       0x0282); //         [42][82] -- A string that describes the type of document that follows this EBML header ('matroska' in our case).
+define('EBML_ID_DOCTYPEREADVERSION',            0x0285); //         [42][85] -- The minimum DocType version an interpreter has to support to read this file.
+define('EBML_ID_EBMLVERSION',                   0x0286); //         [42][86] -- The version of EBML parser used to create the file.
+define('EBML_ID_DOCTYPEVERSION',                0x0287); //         [42][87] -- The version of DocType interpreter used to create the file.
+define('EBML_ID_EBMLMAXIDLENGTH',               0x02F2); //         [42][F2] -- The maximum length of the IDs you'll find in this file (4 or less in Matroska).
+define('EBML_ID_EBMLMAXSIZELENGTH',             0x02F3); //         [42][F3] -- The maximum length of the sizes you'll find in this file (8 or less in Matroska). This does not override the element size indicated at the beginning of an element. Elements that have an indicated size which is larger than what is allowed by EBMLMaxSizeLength shall be considered invalid.
+define('EBML_ID_EBMLREADVERSION',               0x02F7); //         [42][F7] -- The minimum EBML version a parser has to support to read this file.
+define('EBML_ID_CHAPLANGUAGE',                  0x037C); //         [43][7C] -- The languages corresponding to the string, in the bibliographic ISO-639-2 form.
+define('EBML_ID_CHAPCOUNTRY',                   0x037E); //         [43][7E] -- The countries corresponding to the string, same 2 octets as in Internet domains.
+define('EBML_ID_SEGMENTFAMILY',                 0x0444); //         [44][44] -- A randomly generated unique ID that all segments related to each other must use (128 bits).
+define('EBML_ID_DATEUTC',                       0x0461); //         [44][61] -- Date of the origin of timecode (value 0), i.e. production date.
+define('EBML_ID_TAGLANGUAGE',                   0x047A); //         [44][7A] -- Specifies the language of the tag specified, in the Matroska languages form.
+define('EBML_ID_TAGDEFAULT',                    0x0484); //         [44][84] -- Indication to know if this is the default/original language to use for the given tag.
+define('EBML_ID_TAGBINARY',                     0x0485); //         [44][85] -- The values of the Tag if it is binary. Note that this cannot be used in the same SimpleTag as TagString.
+define('EBML_ID_TAGSTRING',                     0x0487); //         [44][87] -- The value of the Tag.
+define('EBML_ID_DURATION',                      0x0489); //         [44][89] -- Duration of the segment (based on TimecodeScale).
+define('EBML_ID_CHAPPROCESSPRIVATE',            0x050D); //         [45][0D] -- Some optional data attached to the ChapProcessCodecID information. For ChapProcessCodecID = 1, it is the "DVD level" equivalent.
+define('EBML_ID_CHAPTERFLAGENABLED',            0x0598); //         [45][98] -- Specify wether the chapter is enabled. It can be enabled/disabled by a Control Track. When disabled, the movie should skip all the content between the TimeStart and TimeEnd of this chapter.
+define('EBML_ID_TAGNAME',                       0x05A3); //         [45][A3] -- The name of the Tag that is going to be stored.
+define('EBML_ID_EDITIONENTRY',                  0x05B9); //         [45][B9] -- Contains all information about a segment edition.
+define('EBML_ID_EDITIONUID',                    0x05BC); //         [45][BC] -- A unique ID to identify the edition. It's useful for tagging an edition.
+define('EBML_ID_EDITIONFLAGHIDDEN',             0x05BD); //         [45][BD] -- If an edition is hidden (1), it should not be available to the user interface (but still to Control Tracks).
+define('EBML_ID_EDITIONFLAGDEFAULT',            0x05DB); //         [45][DB] -- If a flag is set (1) the edition should be used as the default one.
+define('EBML_ID_EDITIONFLAGORDERED',            0x05DD); //         [45][DD] -- Specify if the chapters can be defined multiple times and the order to play them is enforced.
+define('EBML_ID_FILEDATA',                      0x065C); //         [46][5C] -- The data of the file.
+define('EBML_ID_FILEMIMETYPE',                  0x0660); //         [46][60] -- MIME type of the file.
+define('EBML_ID_FILENAME',                      0x066E); //         [46][6E] -- Filename of the attached file.
+define('EBML_ID_FILEREFERRAL',                  0x0675); //         [46][75] -- A binary value that a track/codec can refer to when the attachment is needed.
+define('EBML_ID_FILEDESCRIPTION',               0x067E); //         [46][7E] -- A human-friendly name for the attached file.
+define('EBML_ID_FILEUID',                       0x06AE); //         [46][AE] -- Unique ID representing the file, as random as possible.
+define('EBML_ID_CONTENTENCALGO',                0x07E1); //         [47][E1] -- The encryption algorithm used. The value '0' means that the contents have not been encrypted but only signed. Predefined values:
+define('EBML_ID_CONTENTENCKEYID',               0x07E2); //         [47][E2] -- For public key algorithms this is the ID of the public key the the data was encrypted with.
+define('EBML_ID_CONTENTSIGNATURE',              0x07E3); //         [47][E3] -- A cryptographic signature of the contents.
+define('EBML_ID_CONTENTSIGKEYID',               0x07E4); //         [47][E4] -- This is the ID of the private key the data was signed with.
+define('EBML_ID_CONTENTSIGALGO',                0x07E5); //         [47][E5] -- The algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
+define('EBML_ID_CONTENTSIGHASHALGO',            0x07E6); //         [47][E6] -- The hash algorithm used for the signature. A value of '0' means that the contents have not been signed but only encrypted. Predefined values:
+define('EBML_ID_MUXINGAPP',                     0x0D80); //         [4D][80] -- Muxing application or library ("libmatroska-0.4.3").
+define('EBML_ID_SEEK',                          0x0DBB); //         [4D][BB] -- Contains a single seek entry to an EBML element.
+define('EBML_ID_CONTENTENCODINGORDER',          0x1031); //         [50][31] -- Tells when this modification was used during encoding/muxing starting with 0 and counting upwards. The decoder/demuxer has to start with the highest order number it finds and work its way down. This value has to be unique over all ContentEncodingOrder elements in the segment.
+define('EBML_ID_CONTENTENCODINGSCOPE',          0x1032); //         [50][32] -- A bit field that describes which elements have been modified in this way. Values (big endian) can be OR'ed. Possible values:
+define('EBML_ID_CONTENTENCODINGTYPE',           0x1033); //         [50][33] -- A value describing what kind of transformation has been done. Possible values:
+define('EBML_ID_CONTENTCOMPRESSION',            0x1034); //         [50][34] -- Settings describing the compression used. Must be present if the value of ContentEncodingType is 0 and absent otherwise. Each block must be decompressable even if no previous block is available in order not to prevent seeking.
+define('EBML_ID_CONTENTENCRYPTION',             0x1035); //         [50][35] -- Settings describing the encryption used. Must be present if the value of ContentEncodingType is 1 and absent otherwise.
+define('EBML_ID_CUEREFNUMBER',                  0x135F); //         [53][5F] -- Number of the referenced Block of Track X in the specified Cluster.
+define('EBML_ID_NAME',                          0x136E); //         [53][6E] -- A human-readable track name.
+define('EBML_ID_CUEBLOCKNUMBER',                0x1378); //         [53][78] -- Number of the Block in the specified Cluster.
+define('EBML_ID_TRACKOFFSET',                   0x137F); //         [53][7F] -- A value to add to the Block's Timecode. This can be used to adjust the playback offset of a track.
+define('EBML_ID_SEEKID',                        0x13AB); //         [53][AB] -- The binary ID corresponding to the element name.
+define('EBML_ID_SEEKPOSITION',                  0x13AC); //         [53][AC] -- The position of the element in the segment in octets (0 = first level 1 element).
+define('EBML_ID_STEREOMODE',                    0x13B8); //         [53][B8] -- Stereo-3D video mode on 2 bits (0: mono, 1: right eye, 2: left eye, 3: both eyes).
+define('EBML_ID_PIXELCROPBOTTOM',               0x14AA); //         [54][AA] -- The number of video pixels to remove at the bottom of the image (for HDTV content).
+define('EBML_ID_DISPLAYWIDTH',                  0x14B0); //         [54][B0] -- Width of the video frames to display.
+define('EBML_ID_DISPLAYUNIT',                   0x14B2); //         [54][B2] -- Type of the unit for DisplayWidth/Height (0: pixels, 1: centimeters, 2: inches).
+define('EBML_ID_ASPECTRATIOTYPE',               0x14B3); //         [54][B3] -- Specify the possible modifications to the aspect ratio (0: free resizing, 1: keep aspect ratio, 2: fixed).
+define('EBML_ID_DISPLAYHEIGHT',                 0x14BA); //         [54][BA] -- Height of the video frames to display.
+define('EBML_ID_PIXELCROPTOP',                  0x14BB); //         [54][BB] -- The number of video pixels to remove at the top of the image.
+define('EBML_ID_PIXELCROPLEFT',                 0x14CC); //         [54][CC] -- The number of video pixels to remove on the left of the image.
+define('EBML_ID_PIXELCROPRIGHT',                0x14DD); //         [54][DD] -- The number of video pixels to remove on the right of the image.
+define('EBML_ID_FLAGFORCED',                    0x15AA); //         [55][AA] -- Set if that track MUST be used during playback. There can be many forced track for a kind (audio, video or subs), the player should select the one which language matches the user preference or the default + forced track. Overlay MAY happen between a forced and non-forced track of the same kind.
+define('EBML_ID_MAXBLOCKADDITIONID',            0x15EE); //         [55][EE] -- The maximum value of BlockAddID. A value 0 means there is no BlockAdditions for this track.
+define('EBML_ID_WRITINGAPP',                    0x1741); //         [57][41] -- Writing application ("mkvmerge-0.3.3").
+define('EBML_ID_CLUSTERSILENTTRACKS',           0x1854); //         [58][54] -- The list of tracks that are not used in that part of the stream. It is useful when using overlay tracks on seeking. Then you should decide what track to use.
+define('EBML_ID_CLUSTERSILENTTRACKNUMBER',      0x18D7); //         [58][D7] -- One of the track number that are not used from now on in the stream. It could change later if not specified as silent in a further Cluster.
+define('EBML_ID_ATTACHEDFILE',                  0x21A7); //         [61][A7] -- An attached file.
+define('EBML_ID_CONTENTENCODING',               0x2240); //         [62][40] -- Settings for one content encoding like compression or encryption.
+define('EBML_ID_BITDEPTH',                      0x2264); //         [62][64] -- Bits per sample, mostly used for PCM.
+define('EBML_ID_CODECPRIVATE',                  0x23A2); //         [63][A2] -- Private data only known to the codec.
+define('EBML_ID_TARGETS',                       0x23C0); //         [63][C0] -- Contain all UIDs where the specified meta data apply. It is void to describe everything in the segment.
+define('EBML_ID_CHAPTERPHYSICALEQUIV',          0x23C3); //         [63][C3] -- Specify the physical equivalent of this ChapterAtom like "DVD" (60) or "SIDE" (50), see complete list of values.
+define('EBML_ID_TAGCHAPTERUID',                 0x23C4); //         [63][C4] -- A unique ID to identify the Chapter(s) the tags belong to. If the value is 0 at this level, the tags apply to all chapters in the Segment.
+define('EBML_ID_TAGTRACKUID',                   0x23C5); //         [63][C5] -- A unique ID to identify the Track(s) the tags belong to. If the value is 0 at this level, the tags apply to all tracks in the Segment.
+define('EBML_ID_ATTACHMENTUID',                 0x23C6); //         [63][C6] -- A unique ID to identify the Attachment(s) the tags belong to. If the value is 0 at this level, the tags apply to all the attachments in the Segment.
+define('EBML_ID_TAGEDITIONUID',                 0x23C9); //         [63][C9] -- A unique ID to identify the EditionEntry(s) the tags belong to. If the value is 0 at this level, the tags apply to all editions in the Segment.
+define('EBML_ID_TARGETTYPE',                    0x23CA); //         [63][CA] -- An informational string that can be used to display the logical level of the target like "ALBUM", "TRACK", "MOVIE", "CHAPTER", etc (see TargetType).
+define('EBML_ID_TRACKTRANSLATE',                0x2624); //         [66][24] -- The track identification for the given Chapter Codec.
+define('EBML_ID_TRACKTRANSLATETRACKID',         0x26A5); //         [66][A5] -- The binary value used to represent this track in the chapter codec data. The format depends on the ChapProcessCodecID used.
+define('EBML_ID_TRACKTRANSLATECODEC',           0x26BF); //         [66][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
+define('EBML_ID_TRACKTRANSLATEEDITIONUID',      0x26FC); //         [66][FC] -- Specify an edition UID on which this translation applies. When not specified, it means for all editions found in the segment.
+define('EBML_ID_SIMPLETAG',                     0x27C8); //         [67][C8] -- Contains general information about the target.
+define('EBML_ID_TARGETTYPEVALUE',               0x28CA); //         [68][CA] -- A number to indicate the logical level of the target (see TargetType).
+define('EBML_ID_CHAPPROCESSCOMMAND',            0x2911); //         [69][11] -- Contains all the commands associated to the Atom.
+define('EBML_ID_CHAPPROCESSTIME',               0x2922); //         [69][22] -- Defines when the process command should be handled (0: during the whole chapter, 1: before starting playback, 2: after playback of the chapter).
+define('EBML_ID_CHAPTERTRANSLATE',              0x2924); //         [69][24] -- A tuple of corresponding ID used by chapter codecs to represent this segment.
+define('EBML_ID_CHAPPROCESSDATA',               0x2933); //         [69][33] -- Contains the command information. The data should be interpreted depending on the ChapProcessCodecID value. For ChapProcessCodecID = 1, the data correspond to the binary DVD cell pre/post commands.
+define('EBML_ID_CHAPPROCESS',                   0x2944); //         [69][44] -- Contains all the commands associated to the Atom.
+define('EBML_ID_CHAPPROCESSCODECID',            0x2955); //         [69][55] -- Contains the type of the codec used for the processing. A value of 0 means native Matroska processing (to be defined), a value of 1 means the DVD command set is used. More codec IDs can be added later.
+define('EBML_ID_CHAPTERTRANSLATEID',            0x29A5); //         [69][A5] -- The binary value used to represent this segment in the chapter codec data. The format depends on the ChapProcessCodecID used.
+define('EBML_ID_CHAPTERTRANSLATECODEC',         0x29BF); //         [69][BF] -- The chapter codec using this ID (0: Matroska Script, 1: DVD-menu).
+define('EBML_ID_CHAPTERTRANSLATEEDITIONUID',    0x29FC); //         [69][FC] -- Specify an edition UID on which this correspondance applies. When not specified, it means for all editions found in the segment.
+define('EBML_ID_CONTENTENCODINGS',              0x2D80); //         [6D][80] -- Settings for several content encoding mechanisms like compression or encryption.
+define('EBML_ID_MINCACHE',                      0x2DE7); //         [6D][E7] -- The minimum number of frames a player should be able to cache during playback. If set to 0, the reference pseudo-cache system is not used.
+define('EBML_ID_MAXCACHE',                      0x2DF8); //         [6D][F8] -- The maximum cache size required to store referenced frames in and the current frame. 0 means no cache is needed.
+define('EBML_ID_CHAPTERSEGMENTUID',             0x2E67); //         [6E][67] -- A segment to play in place of this chapter. Edition ChapterSegmentEditionUID should be used for this segment, otherwise no edition is used.
+define('EBML_ID_CHAPTERSEGMENTEDITIONUID',      0x2EBC); //         [6E][BC] -- The edition to play from the segment linked in ChapterSegmentUID.
+define('EBML_ID_TRACKOVERLAY',                  0x2FAB); //         [6F][AB] -- Specify that this track is an overlay track for the Track specified (in the u-integer). That means when this track has a gap (see SilentTracks) the overlay track should be used instead. The order of multiple TrackOverlay matters, the first one is the one that should be used. If not found it should be the second, etc.
+define('EBML_ID_TAG',                           0x3373); //         [73][73] -- Element containing elements specific to Tracks/Chapters.
+define('EBML_ID_SEGMENTFILENAME',               0x3384); //         [73][84] -- A filename corresponding to this segment.
+define('EBML_ID_SEGMENTUID',                    0x33A4); //         [73][A4] -- A randomly generated unique ID to identify the current segment between many others (128 bits).
+define('EBML_ID_CHAPTERUID',                    0x33C4); //         [73][C4] -- A unique ID to identify the Chapter.
+define('EBML_ID_TRACKUID',                      0x33C5); //         [73][C5] -- A unique ID to identify the Track. This should be kept the same when making a direct stream copy of the Track to another file.
+define('EBML_ID_ATTACHMENTLINK',                0x3446); //         [74][46] -- The UID of an attachment that is used by this codec.
+define('EBML_ID_CLUSTERBLOCKADDITIONS',         0x35A1); //         [75][A1] -- Contain additional blocks to complete the main one. An EBML parser that has no knowledge of the Block structure could still see and use/skip these data.
+define('EBML_ID_CHANNELPOSITIONS',              0x347B); //         [7D][7B] -- Table of horizontal angles for each successive channel, see appendix.
+define('EBML_ID_OUTPUTSAMPLINGFREQUENCY',       0x38B5); //         [78][B5] -- Real output sampling frequency in Hz (used for SBR techniques).
+define('EBML_ID_TITLE',                         0x3BA9); //         [7B][A9] -- General name of the segment.
+define('EBML_ID_CHAPTERDISPLAY',                  0x00); //             [80] -- Contains all possible strings to use for the chapter display.
+define('EBML_ID_TRACKTYPE',                       0x03); //             [83] -- A set of track types coded on 8 bits (1: video, 2: audio, 3: complex, 0x10: logo, 0x11: subtitle, 0x12: buttons, 0x20: control).
+define('EBML_ID_CHAPSTRING',                      0x05); //             [85] -- Contains the string to use as the chapter atom.
+define('EBML_ID_CODECID',                         0x06); //             [86] -- An ID corresponding to the codec, see the codec page for more info.
+define('EBML_ID_FLAGDEFAULT',                     0x08); //             [88] -- Set if that track (audio, video or subs) SHOULD be used if no language found matches the user preference.
+define('EBML_ID_CHAPTERTRACKNUMBER',              0x09); //             [89] -- UID of the Track to apply this chapter too. In the absense of a control track, choosing this chapter will select the listed Tracks and deselect unlisted tracks. Absense of this element indicates that the Chapter should be applied to any currently used Tracks.
+define('EBML_ID_CLUSTERSLICES',                   0x0E); //             [8E] -- Contains slices description.
+define('EBML_ID_CHAPTERTRACK',                    0x0F); //             [8F] -- List of tracks on which the chapter applies. If this element is not present, all tracks apply
+define('EBML_ID_CHAPTERTIMESTART',                0x11); //             [91] -- Timecode of the start of Chapter (not scaled).
+define('EBML_ID_CHAPTERTIMEEND',                  0x12); //             [92] -- Timecode of the end of Chapter (timecode excluded, not scaled).
+define('EBML_ID_CUEREFTIME',                      0x16); //             [96] -- Timecode of the referenced Block.
+define('EBML_ID_CUEREFCLUSTER',                   0x17); //             [97] -- Position of the Cluster containing the referenced Block.
+define('EBML_ID_CHAPTERFLAGHIDDEN',               0x18); //             [98] -- If a chapter is hidden (1), it should not be available to the user interface (but still to Control Tracks).
+define('EBML_ID_FLAGINTERLACED',                  0x1A); //             [9A] -- Set if the video is interlaced.
+define('EBML_ID_CLUSTERBLOCKDURATION',            0x1B); //             [9B] -- The duration of the Block (based on TimecodeScale). This element is mandatory when DefaultDuration is set for the track. When not written and with no DefaultDuration, the value is assumed to be the difference between the timecode of this Block and the timecode of the next Block in "display" order (not coding order). This element can be useful at the end of a Track (as there is not other Block available), or when there is a break in a track like for subtitle tracks.
+define('EBML_ID_FLAGLACING',                      0x1C); //             [9C] -- Set if the track may contain blocks using lacing.
+define('EBML_ID_CHANNELS',                        0x1F); //             [9F] -- Numbers of channels in the track.
+define('EBML_ID_CLUSTERBLOCKGROUP',               0x20); //             [A0] -- Basic container of information containing a single Block or BlockVirtual, and information specific to that Block/VirtualBlock.
+define('EBML_ID_CLUSTERBLOCK',                    0x21); //             [A1] -- Block containing the actual data to be rendered and a timecode relative to the Cluster Timecode.
+define('EBML_ID_CLUSTERBLOCKVIRTUAL',             0x22); //             [A2] -- A Block with no data. It must be stored in the stream at the place the real Block should be in display order.
+define('EBML_ID_CLUSTERSIMPLEBLOCK',              0x23); //             [A3] -- Similar to Block but without all the extra information, mostly used to reduced overhead when no extra feature is needed.
+define('EBML_ID_CLUSTERCODECSTATE',               0x24); //             [A4] -- The new codec state to use. Data interpretation is private to the codec. This information should always be referenced by a seek entry.
+define('EBML_ID_CLUSTERBLOCKADDITIONAL',          0x25); //             [A5] -- Interpreted by the codec as it wishes (using the BlockAddID).
+define('EBML_ID_CLUSTERBLOCKMORE',                0x26); //             [A6] -- Contain the BlockAdditional and some parameters.
+define('EBML_ID_CLUSTERPOSITION',                 0x27); //             [A7] -- Position of the Cluster in the segment (0 in live broadcast streams). It might help to resynchronise offset on damaged streams.
+define('EBML_ID_CODECDECODEALL',                  0x2A); //             [AA] -- The codec can decode potentially damaged data.
+define('EBML_ID_CLUSTERPREVSIZE',                 0x2B); //             [AB] -- Size of the previous Cluster, in octets. Can be useful for backward playing.
+define('EBML_ID_TRACKENTRY',                      0x2E); //             [AE] -- Describes a track with all elements.
+define('EBML_ID_CLUSTERENCRYPTEDBLOCK',           0x2F); //             [AF] -- Similar to SimpleBlock but the data inside the Block are Transformed (encrypt and/or signed).
+define('EBML_ID_PIXELWIDTH',                      0x30); //             [B0] -- Width of the encoded video frames in pixels.
+define('EBML_ID_CUETIME',                         0x33); //             [B3] -- Absolute timecode according to the segment time base.
+define('EBML_ID_SAMPLINGFREQUENCY',               0x35); //             [B5] -- Sampling frequency in Hz.
+define('EBML_ID_CHAPTERATOM',                     0x36); //             [B6] -- Contains the atom information to use as the chapter atom (apply to all tracks).
+define('EBML_ID_CUETRACKPOSITIONS',               0x37); //             [B7] -- Contain positions for different tracks corresponding to the timecode.
+define('EBML_ID_FLAGENABLED',                     0x39); //             [B9] -- Set if the track is used.
+define('EBML_ID_PIXELHEIGHT',                     0x3A); //             [BA] -- Height of the encoded video frames in pixels.
+define('EBML_ID_CUEPOINT',                        0x3B); //             [BB] -- Contains all information relative to a seek point in the segment.
+define('EBML_ID_CRC32',                           0x3F); //             [BF] -- The CRC is computed on all the data of the Master element it's in, regardless of its position. It's recommended to put the CRC value at the beggining of the Master element for easier reading. All level 1 elements should include a CRC-32.
+define('EBML_ID_CLUSTERBLOCKADDITIONID',          0x4B); //             [CB] -- The ID of the BlockAdditional element (0 is the main Block).
+define('EBML_ID_CLUSTERLACENUMBER',               0x4C); //             [CC] -- The reverse number of the frame in the lace (0 is the last frame, 1 is the next to last, etc). While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
+define('EBML_ID_CLUSTERFRAMENUMBER',              0x4D); //             [CD] -- The number of the frame to generate from this lace with this delay (allow you to generate many frames from the same Block/Frame).
+define('EBML_ID_CLUSTERDELAY',                    0x4E); //             [CE] -- The (scaled) delay to apply to the element.
+define('EBML_ID_CLUSTERDURATION',                 0x4F); //             [CF] -- The (scaled) duration to apply to the element.
+define('EBML_ID_TRACKNUMBER',                     0x57); //             [D7] -- The track number as used in the Block Header (using more than 127 tracks is not encouraged, though the design allows an unlimited number).
+define('EBML_ID_CUEREFERENCE',                    0x5B); //             [DB] -- The Clusters containing the required referenced Blocks.
+define('EBML_ID_VIDEO',                           0x60); //             [E0] -- Video settings.
+define('EBML_ID_AUDIO',                           0x61); //             [E1] -- Audio settings.
+define('EBML_ID_CLUSTERTIMESLICE',                0x68); //             [E8] -- Contains extra time information about the data contained in the Block. While there are a few files in the wild with this element, it is no longer in use and has been deprecated. Being able to interpret this element is not required for playback.
+define('EBML_ID_CUECODECSTATE',                   0x6A); //             [EA] -- The position of the Codec State corresponding to this Cue element. 0 means that the data is taken from the initial Track Entry.
+define('EBML_ID_CUEREFCODECSTATE',                0x6B); //             [EB] -- The position of the Codec State corresponding to this referenced element. 0 means that the data is taken from the initial Track Entry.
+define('EBML_ID_VOID',                            0x6C); //             [EC] -- Used to void damaged data, to avoid unexpected behaviors when using damaged data. The content is discarded. Also used to reserve space in a sub-element for later use.
+define('EBML_ID_CLUSTERTIMECODE',                 0x67); //             [E7] -- Absolute timecode of the cluster (based on TimecodeScale).
+define('EBML_ID_CLUSTERBLOCKADDID',               0x6E); //             [EE] -- An ID to identify the BlockAdditional level.
+define('EBML_ID_CUECLUSTERPOSITION',              0x71); //             [F1] -- The position of the Cluster containing the required Block.
+define('EBML_ID_CUETRACK',                        0x77); //             [F7] -- The track for which a position is given.
+define('EBML_ID_CLUSTERREFERENCEPRIORITY',        0x7A); //             [FA] -- This frame is referenced and has the specified cache priority. In cache only a frame of the same or higher priority can replace this frame. A value of 0 means the frame is not referenced.
+define('EBML_ID_CLUSTERREFERENCEBLOCK',           0x7B); //             [FB] -- Timecode of another frame used as a reference (ie: B or P frame). The timecode is relative to the block it's attached to.
+define('EBML_ID_CLUSTERREFERENCEVIRTUAL',         0x7D); //             [FD] -- Relative position of the data that should be in position of the virtual block.
+
+
+class getid3_matroska
+{
+	var $read_buffer_size = 32768; // size of read buffer, 32kB is default
+	var $hide_clusters    = true;  // if true, do not return information about CLUSTER chunks, since there's a lot of them and they're not usually useful
+	var $warnings         = array();
+
+	function getid3_matroska(&$fd, &$ThisFileInfo) {
+
+		// http://www.matroska.org/technical/specs/index.html#EBMLBasics
+		$offset = $ThisFileInfo['avdataoffset'];
+		$EBMLdata = '';
+		$EBMLdata_offset = $offset;
+
+		if ($ThisFileInfo['avdataend'] > 2147483648) {
+			$this->warnings[] = 'This version of getID3() may or may not correctly handle Matroska files larger than 2GB';
+		}
+
+		while ($offset < $ThisFileInfo['avdataend']) {
+			$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+
+			$top_element_offset    = $offset;
+			$top_element_id        = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+			$top_element_length    = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+			if ($top_element_length === false) {
+				$this->warnings[] = 'invalid chunk length at '.$top_element_offset;
+				$offset = pow(2, 63);
+				break;
+			}
+			$top_element_endoffset = $offset + $top_element_length;
+			switch ($top_element_id) {
+				case EBML_ID_EBML:
+					$ThisFileInfo['fileformat'] = 'matroska';
+					$ThisFileInfo['matroska']['header']['offset'] = $top_element_offset;
+					$ThisFileInfo['matroska']['header']['length'] = $top_element_length;
+
+					while ($offset < $top_element_endoffset) {
+						$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+						$element_data = array();
+						$element_data_offset     = $offset;
+						$element_data['id']      = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+						$element_data['id_name'] = $this->EBMLidName($element_data['id']);
+						$element_data['length']     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+						$end_offset              = $offset + $element_data['length'];
+
+						switch ($element_data['id']) {
+							case EBML_ID_EBMLVERSION:
+							case EBML_ID_EBMLREADVERSION:
+							case EBML_ID_EBMLMAXIDLENGTH:
+							case EBML_ID_EBMLMAXSIZELENGTH:
+							case EBML_ID_DOCTYPEVERSION:
+							case EBML_ID_DOCTYPEREADVERSION:
+								$element_data['data'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']));
+								break;
+							case EBML_ID_DOCTYPE:
+								$element_data['data'] =                      trim(substr($EBMLdata, $offset - $EBMLdata_offset, $element_data['length']), "\x00");
+								break;
+							default:
+								$this->warnings[] = 'Unhandled track.video element['.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data_offset;
+								break;
+						}
+						$offset = $end_offset;
+						$ThisFileInfo['matroska']['header']['elements'][] = $element_data;
+					}
+					break;
+
+
+				case EBML_ID_SEGMENT:
+					$ThisFileInfo['matroska']['segment'][0]['offset'] = $top_element_offset;
+					$ThisFileInfo['matroska']['segment'][0]['length'] = $top_element_length;
+
+					$segment_key = -1;
+					while ($offset < $ThisFileInfo['avdataend']) {
+						$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+
+						$element_data = array();
+						$element_data['offset']  = $offset;
+						$element_data['id']      = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+						$element_data['id_name'] = $this->EBMLidName($element_data['id']);
+						$element_data['length']  = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+						if ($element_data['length'] === false) {
+							$this->warnings[] = 'invalid chunk length at '.$element_data['offset'];
+							//$offset = pow(2, 63);
+							$offset = $ThisFileInfo['avdataend'];
+							break;
+						}
+						$element_end             = $offset + $element_data['length'];
+						switch ($element_data['id']) {
+							//case EBML_ID_CLUSTER:
+							//	// too many cluster entries, probably not useful
+							//	break;
+							case false:
+								$this->warnings[] = 'invalid ID at '.$element_data['offset'];
+								$offset = $element_end;
+								continue 3;
+							default:
+								$ThisFileInfo['matroska']['segments'][] = $element_data;
+								break;
+						}
+						$segment_key++;
+
+						switch ($element_data['id']) {
+							case EBML_ID_SEEKHEAD: // Contains the position of other level 1 elements
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$seek_entry = array();
+									$seek_entry['offset']  = $offset;
+									$seek_entry['id']      = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$seek_entry['id_name'] = $this->EBMLidName($seek_entry['id']);
+									$seek_entry['length']  = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$seek_end_offset       = $offset + $seek_entry['length'];
+									switch ($seek_entry['id']) {
+										case EBML_ID_SEEK: // Contains a single seek entry to an EBML element
+											while ($offset < $seek_end_offset) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$value  =             substr($EBMLdata, $offset - $EBMLdata_offset, $length);
+												$offset += $length;
+												switch ($id) {
+													case EBML_ID_SEEKID:
+														$dummy = 0;
+														$seek_entry['target_id']   = $this->readEBMLint($value, $dummy);
+														$seek_entry['target_name'] = $this->EBMLidName($seek_entry['target_id']);
+														break;
+													case EBML_ID_SEEKPOSITION:
+														$seek_entry['target_offset'] = $element_data['offset'] + getid3_lib::BigEndian2Int($value);
+														break;
+													default:
+														$ThisFileInfo['error'][] = 'Unhandled segment['.__LINE__.'] ('.$id.') at '.$offset;
+														break;
+												}
+											}
+											$ThisFileInfo['matroska']['seek'][] = $seek_entry;
+											//switch ($seek_entry['target_id']) {
+											//	case EBML_ID_CLUSTER:
+											//		// too many cluster seek points, probably not useful
+											//		break;
+											//	default:
+											//		$ThisFileInfo['matroska']['seek'][] = $seek_entry;
+											//		break;
+											//}
+											break;
+										default:
+											$this->warnings[] = 'Unhandled seekhead element['.__LINE__.'] ('.$seek_entry['id'].') at '.$offset;
+											break;
+									}
+									$offset = $seek_end_offset;
+								}
+								break;
+
+							case EBML_ID_TRACKS: // information about all tracks in segment
+								$ThisFileInfo['matroska']['tracks'] = $element_data;
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$track_entry = array();
+									$track_entry['offset']  = $offset;
+									$track_entry['id']      = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$track_entry['id_name'] = $this->EBMLidName($track_entry['id']);
+									$track_entry['length']  = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$track_entry_endoffset  = $offset + $track_entry['length'];
+									switch ($track_entry['id']) {
+										case EBML_ID_TRACKENTRY: //subelements: Describes a track with all elements.
+											while ($offset < $track_entry_endoffset) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$subelement_offset = $offset;
+												$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$subelement_idname = $this->EBMLidName($subelement_id);
+												$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$subelement_end    = $offset + $subelement_length;
+												switch ($subelement_id) {
+													case EBML_ID_TRACKNUMBER:
+													case EBML_ID_TRACKUID:
+													case EBML_ID_TRACKTYPE:
+													case EBML_ID_MINCACHE:
+													case EBML_ID_MAXCACHE:
+													case EBML_ID_MAXBLOCKADDITIONID:
+													case EBML_ID_DEFAULTDURATION: // nanoseconds per frame
+														$track_entry[$subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+														break;
+													case EBML_ID_TRACKTIMECODESCALE:
+														$track_entry[$subelement_idname] =      getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+														break;
+													case EBML_ID_CODECID:
+													case EBML_ID_LANGUAGE:
+													case EBML_ID_NAME:
+													case EBML_ID_CODECPRIVATE:
+														$track_entry[$subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
+														break;
+													case EBML_ID_FLAGENABLED:
+													case EBML_ID_FLAGDEFAULT:
+													case EBML_ID_FLAGFORCED:
+													case EBML_ID_FLAGLACING:
+													case EBML_ID_CODECDECODEALL:
+														$track_entry[$subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+														break;
+													case EBML_ID_VIDEO:
+														while ($offset < $subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_offset = $offset;
+															$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+															$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_end    = $offset + $sub_subelement_length;
+															switch ($sub_subelement_id) {
+																case EBML_ID_PIXELWIDTH:
+																case EBML_ID_PIXELHEIGHT:
+																case EBML_ID_STEREOMODE:
+																case EBML_ID_PIXELCROPBOTTOM:
+																case EBML_ID_PIXELCROPTOP:
+																case EBML_ID_PIXELCROPLEFT:
+																case EBML_ID_PIXELCROPRIGHT:
+																case EBML_ID_DISPLAYWIDTH:
+																case EBML_ID_DISPLAYHEIGHT:
+																case EBML_ID_DISPLAYUNIT:
+																case EBML_ID_ASPECTRATIOTYPE:
+																	$track_entry[$sub_subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+																	break;
+																case EBML_ID_FLAGINTERLACED:
+																	$track_entry[$sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+																	break;
+																case EBML_ID_GAMMAVALUE:
+																	$track_entry[$sub_subelement_idname] =      getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+																	break;
+																case EBML_ID_COLOURSPACE:
+																	$track_entry[$sub_subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "\x00");
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled track.video element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+																	break;
+															}
+															$offset = $sub_subelement_end;
+														}
+
+														if ((@$track_entry[$this->EBMLidName(EBML_ID_CODECID)] == 'V_MS/VFW/FOURCC') && isset($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)])) {
+															if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, false)) {
+																$track_entry['codec_private_parsed'] = getid3_riff::ParseBITMAPINFOHEADER($track_entry[$this->EBMLidName(EBML_ID_CODECPRIVATE)]);
+															} else {
+																$this->warnings[] = 'Unable to parse codec private data['.__LINE__.'] because cannot include "module.audio-video.riff.php"';
+															}
+														}
+														break;
+													case EBML_ID_AUDIO:
+														while ($offset < $subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_offset = $offset;
+															$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+															$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_end    = $offset + $sub_subelement_length;
+															switch ($sub_subelement_id) {
+																case EBML_ID_CHANNELS:
+																case EBML_ID_BITDEPTH:
+																	$track_entry[$sub_subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+																	break;
+																case EBML_ID_SAMPLINGFREQUENCY:
+																case EBML_ID_OUTPUTSAMPLINGFREQUENCY:
+																	$track_entry[$sub_subelement_idname] =      getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+																	break;
+																case EBML_ID_CHANNELPOSITIONS:
+																	$track_entry[$sub_subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), "\x00");
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled track.video element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+																	break;
+															}
+															$offset = $sub_subelement_end;
+														}
+														break;
+
+													case EBML_ID_CONTENTENCODINGS:
+														while ($offset < $subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_offset = $offset;
+															$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+															$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_subelement_end    = $offset + $sub_subelement_length;
+															switch ($sub_subelement_id) {
+																case EBML_ID_CONTENTENCODING:
+																	while ($offset < $sub_subelement_end) {
+																		$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_subelement_offset = $offset;
+																		$sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+																		$sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_subelement_end    = $offset + $sub_sub_subelement_length;
+																		switch ($sub_sub_subelement_id) {
+																			case EBML_ID_CONTENTENCODINGORDER:
+																			case EBML_ID_CONTENTENCODINGSCOPE:
+																			case EBML_ID_CONTENTENCODINGTYPE:
+																				$track_entry[$sub_subelement_idname][$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
+																				break;
+																			case EBML_ID_CONTENTCOMPRESSION:
+																				while ($offset < $sub_sub_subelement_end) {
+																					$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+																					$sub_sub_sub_subelement_offset = $offset;
+																					$sub_sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																					$sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+																					$sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																					$sub_sub_sub_subelement_end    = $offset + $sub_sub_sub_subelement_length;
+																					switch ($sub_sub_sub_subelement_id) {
+																						case EBML_ID_CONTENTCOMPALGO:
+																							$track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
+																							break;
+																						case EBML_ID_CONTENTCOMPSETTINGS:
+																							$track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] =                           substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
+																							break;
+																						default:
+																							$this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+																							break;
+																					}
+																					$offset = $sub_sub_sub_subelement_end;
+																				}
+																				break;
+
+																			case EBML_ID_CONTENTENCRYPTION:
+																				while ($offset < $sub_sub_subelement_end) {
+																					$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+																					$sub_sub_sub_subelement_offset = $offset;
+																					$sub_sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																					$sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+																					$sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																					$sub_sub_sub_subelement_end    = $offset + $sub_sub_sub_subelement_length;
+																					switch ($sub_sub_sub_subelement_id) {
+																						case EBML_ID_CONTENTENCALGO:
+																						case EBML_ID_CONTENTSIGALGO:
+																						case EBML_ID_CONTENTSIGHASHALGO:
+																							$track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
+																							break;
+																						case EBML_ID_CONTENTENCKEYID:
+																						case EBML_ID_CONTENTSIGNATURE:
+																						case EBML_ID_CONTENTSIGKEYID:
+																							$track_entry[$sub_subelement_idname][$sub_sub_subelement_idname][$sub_sub_sub_subelement_idname] =                           substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
+																							break;
+																						default:
+																							$this->warnings[] = 'Unhandled track.contentencodings.contentencoding.contentcompression element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+																							break;
+																					}
+																					$offset = $sub_sub_sub_subelement_end;
+																				}
+																				break;
+
+																			default:
+																				$this->warnings[] = 'Unhandled track.contentencodings.contentencoding element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+																				break;
+																		}
+																		$offset = $sub_sub_subelement_end;
+																	}
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled track.contentencodings element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+																	break;
+															}
+															$offset = $sub_subelement_end;
+														}
+														break;
+
+													default:
+														$this->warnings[] = 'Unhandled track element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+														break;
+												}
+												$offset = $subelement_end;
+											}
+											break;
+										default:
+											$this->warnings[] = 'Unhandled track element['.__LINE__.'] ('.$track_entry['id'].'::'.$track_entry['id_name'].') at '.$track_entry['offset'];
+											$offset = $track_entry_endoffset;
+											break;
+									}
+									$ThisFileInfo['matroska']['tracks']['tracks'][] = $track_entry;
+								}
+								break;
+
+							case EBML_ID_INFO: // Contains the position of other level 1 elements
+								$info_entry = array();
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_offset = $offset;
+									$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_idname = $this->EBMLidName($subelement_id);
+									$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_end    = $offset + $subelement_length;
+									switch ($subelement_id) {
+										case EBML_ID_CHAPTERTRANSLATEEDITIONUID:
+										case EBML_ID_CHAPTERTRANSLATECODEC:
+										case EBML_ID_TIMECODESCALE:
+											$info_entry[$subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+											break;
+										case EBML_ID_DURATION:
+											$info_entry[$subelement_idname] =      getid3_lib::BigEndian2Float(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+											break;
+										case EBML_ID_DATEUTC:
+											$info_entry[$subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+											$info_entry[$subelement_idname.'_unix'] = $this->EBMLdate2unix($info_entry[$subelement_idname]);
+											break;
+										case EBML_ID_SEGMENTUID:
+										case EBML_ID_PREVUID:
+										case EBML_ID_NEXTUID:
+										case EBML_ID_SEGMENTFAMILY:
+										case EBML_ID_CHAPTERTRANSLATEID:
+											$info_entry[$subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
+											break;
+										case EBML_ID_SEGMENTFILENAME:
+										case EBML_ID_PREVFILENAME:
+										case EBML_ID_NEXTFILENAME:
+										case EBML_ID_TITLE:
+										case EBML_ID_MUXINGAPP:
+										case EBML_ID_WRITINGAPP:
+											$info_entry[$subelement_idname] =                             trim(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length), "\x00");
+											break;
+										default:
+											$this->warnings[] = 'Unhandled info element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+											break;
+									}
+									$offset = $subelement_end;
+								}
+								$ThisFileInfo['matroska']['info'][] = $info_entry;
+								break;
+
+							case EBML_ID_CUES:
+								$cues_entry = array();
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_offset = $offset;
+									$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_idname = $this->EBMLidName($subelement_id);
+									$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_end    = $offset + $subelement_length;
+									switch ($subelement_id) {
+										case EBML_ID_CUEPOINT:
+											$cuepoint_entry = array();
+											while ($offset < $subelement_end) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_offset = $offset;
+												$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+												$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_end    = $offset + $sub_subelement_length;
+												switch ($sub_subelement_id) {
+													case EBML_ID_CUETRACKPOSITIONS:
+														while ($offset < $sub_subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_offset = $offset;
+															$sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+															$sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_end    = $offset + $sub_sub_subelement_length;
+															switch ($sub_sub_subelement_id) {
+																case EBML_ID_CUETRACK:
+																	$cuepoint_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled cues.cuepoint.cuetrackpositions element['.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
+																	break;
+															}
+															$offset = $sub_subelement_end;
+														}
+														break;
+													case EBML_ID_CUETIME:
+														$cuepoint_entry[$subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+													default:
+														$this->warnings[] = 'Unhandled cues.cuepoint element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+														break;
+												}
+												$offset = $sub_subelement_end;
+											}
+											$cues_entry[] = $cuepoint_entry;
+											$offset = $sub_subelement_end;
+											break;
+										default:
+											$this->warnings[] = 'Unhandled cues element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+											break;
+									}
+									$offset = $subelement_end;
+								}
+								$ThisFileInfo['matroska']['cues'] = $cues_entry;
+								break;
+
+							case EBML_ID_TAGS:
+								$tags_entry = array();
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_offset = $offset;
+									$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_idname = $this->EBMLidName($subelement_id);
+									$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_end    = $offset + $subelement_length;
+									switch ($subelement_id) {
+										case EBML_ID_WRITINGAPP:
+											$tags_entry[$subelement_idname] = substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length);
+											break;
+										case EBML_ID_TAG:
+											$tag_entry = array();
+											while ($offset < $subelement_end) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_offset = $offset;
+												$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+												$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_end    = $offset + $sub_subelement_length;
+												switch ($sub_subelement_id) {
+													case EBML_ID_TARGETS:
+														$targets_entry = array();
+														while ($offset < $sub_subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_offset = $offset;
+															$sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+															$sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_end    = $offset + $sub_sub_subelement_length;
+															switch ($sub_sub_subelement_id) {
+																case EBML_ID_TARGETTYPEVALUE:
+																case EBML_ID_EDITIONUID:
+																case EBML_ID_CHAPTERUID:
+																case EBML_ID_ATTACHMENTUID:
+																case EBML_ID_TAGTRACKUID:
+																case EBML_ID_TAGCHAPTERUID:
+																	$targets_entry[$sub_sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled tag.targets element['.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
+																	break;
+															}
+															$offset = $sub_sub_subelement_end;
+														}
+														$tag_entry[$sub_subelement_idname][] = $targets_entry;
+														break;
+													case EBML_ID_SIMPLETAG:
+														$simpletag_entry = array();
+														while ($offset < $sub_subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_offset = $offset;
+															$sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+															$sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_end    = $offset + $sub_sub_subelement_length;
+															switch ($sub_sub_subelement_id) {
+																case EBML_ID_TAGNAME:
+																case EBML_ID_TAGLANGUAGE:
+																case EBML_ID_TAGSTRING:
+																case EBML_ID_TAGBINARY:
+																	$simpletag_entry[$sub_sub_subelement_idname] =                                  substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length);
+																	break;
+																case EBML_ID_TAGDEFAULT:
+																	$simpletag_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled tag.simpletag element['.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
+																	break;
+															}
+															$offset = $sub_sub_subelement_end;
+														}
+														$tag_entry[$sub_subelement_idname][] = $simpletag_entry;
+														break;
+													case EBML_ID_TARGETTYPE:
+														$tag_entry[$sub_subelement_idname] =                           substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
+														break;
+													case EBML_ID_TRACKUID:
+														$tag_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+													default:
+														$this->warnings[] = 'Unhandled tags.tag element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+														break;
+												}
+												$offset = $sub_subelement_end;
+											}
+											$tags_entry['tags'][] = $tag_entry;
+											$offset = $sub_subelement_end;
+											break;
+										default:
+											$this->warnings[] = 'Unhandled tags element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+											break;
+									}
+									$offset = $subelement_end;
+								}
+								$ThisFileInfo['matroska']['tags'] = $tags_entry;
+								break;
+
+
+							case EBML_ID_ATTACHMENTS:
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_offset = $offset;
+									$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_idname = $this->EBMLidName($subelement_id);
+									$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_end    = $offset + $subelement_length;
+									switch ($subelement_id) {
+										case EBML_ID_ATTACHEDFILE:
+											$attachedfile_entry = array();
+											while ($offset < $subelement_end) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_offset = $offset;
+												$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+												$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_end    = $offset + $sub_subelement_length;
+												switch ($sub_subelement_id) {
+													case EBML_ID_FILEDESCRIPTION:
+													case EBML_ID_FILENAME:
+													case EBML_ID_FILEMIMETYPE:
+														$attachedfile_entry[$sub_subelement_idname] =                           substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
+														break;
+
+													case EBML_ID_FILEDATA:
+														$attachedfile_entry['data_offset'] = $offset;
+														$attachedfile_entry['data_length'] = $sub_subelement_length;
+														if ($sub_subelement_length < 1024) {
+															$attachedfile_entry[$sub_subelement_idname] =                           substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length);
+														}
+														break;
+
+													case EBML_ID_FILEUID:
+														$attachedfile_entry[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+
+													default:
+														$this->warnings[] = 'Unhandled attachment.attachedfile element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+														break;
+												}
+												$offset = $sub_subelement_end;
+											}
+											$ThisFileInfo['matroska']['attachments'][] = $attachedfile_entry;
+											$offset = $sub_subelement_end;
+											break;
+										default:
+											$this->warnings[] = 'Unhandled tags element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+											break;
+									}
+									$offset = $subelement_end;
+								}
+								break;
+
+
+							case EBML_ID_CHAPTERS: // not important to us, contains mostly actual audio/video data, ignore
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_offset = $offset;
+									$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_idname = $this->EBMLidName($subelement_id);
+									$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_end    = $offset + $subelement_length;
+									switch ($subelement_id) {
+										case EBML_ID_EDITIONENTRY:
+											$editionentry_entry = array();
+											while ($offset < $subelement_end) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_offset = $offset;
+												$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+												$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_end    = $offset + $sub_subelement_length;
+												switch ($sub_subelement_id) {
+													case EBML_ID_EDITIONUID:
+														$editionentry_entry[$sub_subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+													case EBML_ID_EDITIONFLAGHIDDEN:
+													case EBML_ID_EDITIONFLAGDEFAULT:
+													case EBML_ID_EDITIONFLAGORDERED:
+														$editionentry_entry[$sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+													case EBML_ID_CHAPTERATOM:
+														$chapteratom_entry = array();
+														while ($offset < $sub_subelement_end) {
+															$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_offset = $offset;
+															$sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+															$sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+															$sub_sub_subelement_end    = $offset + $sub_sub_subelement_length;
+															switch ($sub_sub_subelement_id) {
+																case EBML_ID_CHAPTERSEGMENTUID:
+																case EBML_ID_CHAPTERSEGMENTEDITIONUID:
+																	$chapteratom_entry[$sub_sub_subelement_idname] =                                  substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length);
+																	break;
+																case EBML_ID_CHAPTERFLAGENABLED:
+																case EBML_ID_CHAPTERFLAGHIDDEN:
+																	$chapteratom_entry[$sub_sub_subelement_idname] = (bool) getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
+																	break;
+																case EBML_ID_CHAPTERUID:
+																case EBML_ID_CHAPTERTIMESTART:
+																case EBML_ID_CHAPTERTIMEEND:
+																	$chapteratom_entry[$sub_sub_subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_subelement_length));
+																	break;
+																case EBML_ID_CHAPTERTRACK:
+																	$chaptertrack_entry = array();
+																	while ($offset < $sub_sub_subelement_end) {
+																		$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_sub_subelement_offset = $offset;
+																		$sub_sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_subelement_id);
+																		$sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_sub_subelement_end    = $offset + $sub_sub_sub_subelement_length;
+																		switch ($sub_sub_sub_subelement_id) {
+																			case EBML_ID_CHAPTERTRACKNUMBER:
+																				$chaptertrack_entry[$sub_sub_sub_subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length));
+																				break;
+																			default:
+																				$this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chaptertrack element['.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset;
+																				break;
+																		}
+																		$offset = $sub_sub_sub_subelement_end;
+																	}
+																	$chapteratom_entry[$sub_sub_subelement_idname][] = $chaptertrack_entry;
+																	break;
+																case EBML_ID_CHAPTERDISPLAY:
+																	$chapterdisplay_entry = array();
+																	while ($offset < $sub_sub_subelement_end) {
+																		$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_sub_subelement_offset = $offset;
+																		$sub_sub_sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_sub_subelement_idname = $this->EBMLidName($sub_sub_sub_subelement_id);
+																		$sub_sub_sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+																		$sub_sub_sub_subelement_end    = $offset + $sub_sub_sub_subelement_length;
+																		switch ($sub_sub_sub_subelement_id) {
+																			case EBML_ID_CHAPSTRING:
+																			case EBML_ID_CHAPLANGUAGE:
+																			case EBML_ID_CHAPCOUNTRY:
+																				$chapterdisplay_entry[$sub_sub_sub_subelement_idname] =                                  substr($EBMLdata, $offset - $EBMLdata_offset, $sub_sub_sub_subelement_length);
+																				break;
+																			default:
+																				$this->warnings[] = 'Unhandled chapters.editionentry.chapteratom.chapterdisplay element['.__LINE__.'] ('.$sub_sub_sub_subelement_id.'::'.$sub_sub_sub_subelement_idname.') at '.$sub_sub_sub_subelement_offset;
+																				break;
+																		}
+																		$offset = $sub_sub_sub_subelement_end;
+																	}
+																	$chapteratom_entry[$sub_sub_subelement_idname][] = $chapterdisplay_entry;
+																	break;
+																default:
+																	$this->warnings[] = 'Unhandled chapters.editionentry.chapteratom element['.__LINE__.'] ('.$sub_sub_subelement_id.'::'.$sub_sub_subelement_idname.') at '.$sub_sub_subelement_offset;
+																	break;
+															}
+															$offset = $sub_sub_subelement_end;
+														}
+														$editionentry_entry[$sub_subelement_idname][] = $chapteratom_entry;
+														break;
+													default:
+														$this->warnings[] = 'Unhandled chapters.editionentry element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+														break;
+												}
+												$offset = $sub_subelement_end;
+											}
+											$ThisFileInfo['matroska']['chapters'][] = $editionentry_entry;
+											$offset = $sub_subelement_end;
+											break;
+										default:
+											$this->warnings[] = 'Unhandled chapters element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+											break;
+									}
+									$offset = $subelement_end;
+								}
+								break;
+
+
+							case EBML_ID_VOID:    // padding, ignore
+								$void_entry = array();
+								$void_entry['offset'] = $offset;
+								$ThisFileInfo['matroska']['void'][] = $void_entry;
+								break;
+
+							case EBML_ID_CLUSTER: // not important to us, contains mostly actual audio/video data, ignore
+								$cluster_entry = array();
+								while ($offset < $element_end) {
+									$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+									$subelement_offset = $offset;
+//var_dump($offset);
+									$subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+//var_dump($subelement_id);
+//echo '<br>';
+									$subelement_idname = $this->EBMLidName($subelement_id);
+									$subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+//var_dump($subelement_length);
+									$subelement_end    = $offset + $subelement_length;
+//exit;
+									switch ($subelement_id) {
+										case EBML_ID_CLUSTERTIMECODE:
+										case EBML_ID_CLUSTERPOSITION:
+										case EBML_ID_CLUSTERPREVSIZE:
+											$cluster_entry[$subelement_idname] =        getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $subelement_length));
+											break;
+
+										case EBML_ID_CLUSTERSILENTTRACKS:
+											$cluster_silent_tracks = array();
+											while ($offset < $subelement_end) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_offset = $offset;
+												$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+												$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_end    = $offset + $sub_subelement_length;
+												switch ($sub_subelement_id) {
+													case EBML_ID_CLUSTERSILENTTRACKNUMBER:
+														$cluster_silent_tracks[] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+													default:
+														$this->warnings[] = 'Unhandled clusters.silenttracks element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+														break;
+												}
+												$offset = $sub_subelement_end;
+											}
+											$cluster_entry[$subelement_idname][] = $cluster_silent_tracks;
+											$offset = $sub_subelement_end;
+											break;
+
+										case EBML_ID_CLUSTERBLOCKGROUP:
+											$cluster_block_group = array('offset'=>$offset);
+											while ($offset < $subelement_end) {
+												$this->EnsureBufferHasEnoughData($fd, $EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_offset = $offset;
+												$sub_subelement_id     = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_idname = $this->EBMLidName($sub_subelement_id);
+												$sub_subelement_length = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+												$sub_subelement_end    = $offset + $sub_subelement_length;
+												switch ($sub_subelement_id) {
+													case EBML_ID_CLUSTERBLOCK:
+														$cluster_block_data = array();
+														$cluster_block_data['tracknumber'] = $this->readEBMLint($EBMLdata, $offset, $EBMLdata_offset);
+														$cluster_block_data['timecode'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 2));
+														$offset += 2;
+														// unsure whether this is 1 octect or 2 octets? (http://matroska.org/technical/specs/index.html#block_structure)
+														$cluster_block_data['flags_raw'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1));
+														$offset += 1;
+														//$cluster_block_data['flags']['reserved1'] =      (($cluster_block_data['flags_raw'] & 0xF0) >> 4);
+														$cluster_block_data['flags']['invisible'] = (bool) (($cluster_block_data['flags_raw'] & 0x08) >> 3);
+														$cluster_block_data['flags']['lacing']    =        (($cluster_block_data['flags_raw'] & 0x06) >> 1);
+														//$cluster_block_data['flags']['reserved2'] =      (($cluster_block_data['flags_raw'] & 0x01) >> 0);
+														$cluster_block_data['flags']['lacing_type'] = $this->MatroskaBlockLacingType($cluster_block_data['flags']['lacing']);
+														if ($cluster_block_data['flags']['lacing'] != 0) {
+															$cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1)); // Number of frames in the lace-1 (uint8)
+															$offset += 1;
+															if ($cluster_block_data['flags']['lacing'] != 2) {
+																$cluster_block_data['lace_frames'] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset, 1)); // Lace-coded size of each frame of the lace, except for the last one (multiple uint8). *This is not used with Fixed-size lacing as it is calculated automatically from (total size of lace) / (number of frames in lace).
+																$offset += 1;
+															}
+														}
+														if (!isset($ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']])) {
+															$ThisFileInfo['matroska']['track_data_offsets'][$cluster_block_data['tracknumber']] = $offset;
+														}
+														$cluster_block_group[$sub_subelement_idname] = $cluster_block_data;
+														break;
+
+													case EBML_ID_CLUSTERREFERENCEPRIORITY: // unsigned-int
+													case EBML_ID_CLUSTERBLOCKDURATION:     // unsigned-int
+														$cluster_block_group[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length));
+														break;
+
+													case EBML_ID_CLUSTERREFERENCEBLOCK:    // signed-int
+														$cluster_block_group[$sub_subelement_idname] = getid3_lib::BigEndian2Int(substr($EBMLdata, $offset - $EBMLdata_offset, $sub_subelement_length), false, true);
+														break;
+
+													default:
+														$this->warnings[] = 'Unhandled clusters.blockgroup element['.__LINE__.'] ('.$sub_subelement_id.'::'.$sub_subelement_idname.') at '.$sub_subelement_offset;
+														break;
+												}
+												$offset = $sub_subelement_end;
+											}
+											$cluster_entry[$subelement_idname][] = $cluster_block_group;
+											$offset = $sub_subelement_end;
+											break;
+
+										default:
+											$this->warnings[] = 'Unhandled cluster element['.__LINE__.'] ('.$subelement_id.'::'.$subelement_idname.' ['.$subelement_length.' bytes]) at '.$subelement_offset;
+											break;
+									}
+									$offset = $subelement_end;
+								}
+								$ThisFileInfo['matroska']['cluster'][] = $cluster_entry;
+
+								// check to see if all the data we need exists already, if so, break out of the loop
+								if (isset($ThisFileInfo['matroska']['info']) && is_array($ThisFileInfo['matroska']['info'])) {
+									if (isset($ThisFileInfo['matroska']['tracks']['tracks']) && is_array($ThisFileInfo['matroska']['tracks']['tracks'])) {
+										break 2;
+									}
+								}
+								break;
+
+							default:
+								if ($element_data['id_name'] == dechex($element_data['id'])) {
+									$ThisFileInfo['error'][] = 'Unhandled segment['.__LINE__.'] ('.$element_data['id'].') at '.$element_data_offset;
+								} else {
+									$this->warnings[] = 'Unhandled segment['.__LINE__.'] ('.$element_data['id'].'::'.$element_data['id_name'].') at '.$element_data['offset'];
+								}
+								break;
+						}
+						$offset = $element_end;
+					}
+					break;
+
+
+				default:
+					$ThisFileInfo['error'][] = 'Unhandled chunk['.__LINE__.'] ('.$top_element_id.') at '.$offset;
+					break;
+			}
+			$offset = $top_element_endoffset;
+		}
+
+
+
+		if (isset($ThisFileInfo['matroska']['info']) && is_array($ThisFileInfo['matroska']['info'])) {
+			foreach ($ThisFileInfo['matroska']['info'] as $key => $infoarray) {
+				if (isset($infoarray['Duration'])) {
+					// TimecodeScale is how many nanoseconds each Duration unit is
+					$ThisFileInfo['playtime_seconds'] = $infoarray['Duration'] * ((isset($infoarray['TimecodeScale']) ? $infoarray['TimecodeScale'] : 1000000) / 1000000000);
+					break;
+				}
+			}
+		}
+		if (isset($ThisFileInfo['matroska']['tracks']['tracks']) && is_array($ThisFileInfo['matroska']['tracks']['tracks'])) {
+			foreach ($ThisFileInfo['matroska']['tracks']['tracks'] as $key => $trackarray) {
+				$track_info = array();
+				switch (@$trackarray['TrackType']) {
+					case 1: // Video
+						if (@$trackarray['PixelWidth'])      { $track_info['resolution_x']  =                                    $trackarray['PixelWidth'];          }
+						if (@$trackarray['PixelHeight'])     { $track_info['resolution_y']  =                                    $trackarray['PixelHeight'];         }
+						if (@$trackarray['DisplayWidth'])    { $track_info['display_x']     =                                    $trackarray['DisplayWidth'];        }
+						if (@$trackarray['DisplayHeight'])   { $track_info['display_y']     =                                    $trackarray['DisplayHeight'];       }
+						if (@$trackarray['DefaultDuration']) { $track_info['frame_rate']    =                 round(1000000000 / $trackarray['DefaultDuration'], 3); }
+						if (@$trackarray['CodecID'])         { $track_info['dataformat']    = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']);            }
+						if (!empty($trackarray['codec_private_parsed']['fourcc'])) {
+							$track_info['fourcc'] = $trackarray['codec_private_parsed']['fourcc'];
+						}
+						$ThisFileInfo['video']['streams'][] = $track_info;
+						if (isset($track_info['resolution_x']) && empty($ThisFileInfo['video']['resolution_x'])) {
+							foreach ($track_info as $key => $value) {
+								$ThisFileInfo['video'][$key] = $value;
+							}
+						}
+						break;
+					case 2: // Audio
+						if (@$trackarray['CodecID'])           { $track_info['dataformat']      = $this->MatroskaCodecIDtoCommonName($trackarray['CodecID']);          }
+						if (@$trackarray['SamplingFrequency']) { $track_info['sample_rate']     =                                    $trackarray['SamplingFrequency']; }
+						if (@$trackarray['Channels'])          { $track_info['channels']        =                                    $trackarray['Channels'];          }
+						if (@$trackarray['BitDepth'])          { $track_info['bits_per_sample'] =                                    $trackarray['BitDepth'];          }
+						switch (@$trackarray[$this->EBMLidName(EBML_ID_CODECID)]) {
+							case 'A_PCM/INT/LIT':
+							case 'A_PCM/INT/BIG':
+								$track_info['bitrate'] = $trackarray['SamplingFrequency'] * $trackarray['Channels'] * $trackarray['BitDepth'];
+								break;
+
+							case 'A_AC3':
+								if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) {
+									$ac3_thisfileinfo = array('avdataoffset'=>$ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]);
+									$getid3_ac3 = new getid3_ac3($fd, $ac3_thisfileinfo);
+									$ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $ac3_thisfileinfo;
+									if (!empty($ac3_thisfileinfo['error'])) {
+										foreach ($ac3_thisfileinfo['error'] as $newerror) {
+											$this->warnings[] = 'getid3_ac3() says: ['.$newerror.']';
+										}
+									}
+									if (!empty($ac3_thisfileinfo['warning'])) {
+										foreach ($ac3_thisfileinfo['warning'] as $newerror) {
+											$this->warnings[] = 'getid3_ac3() says: ['.$newerror.']';
+										}
+									}
+									if (isset($ac3_thisfileinfo['audio']) && is_array($ac3_thisfileinfo['audio'])) {
+										foreach ($ac3_thisfileinfo['audio'] as $key => $value) {
+											$track_info[$key] = $value;
+										}
+									}
+									unset($ac3_thisfileinfo);
+									unset($getid3_ac3);
+								} else {
+									$this->warnings[] = 'Unable to parse audio data['.__LINE__.'] because cannot include "module.audio.ac3.php"';
+								}
+								break;
+
+							case 'A_DTS':
+								$dts_offset = $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']];
+								// this is a NASTY hack, but sometimes audio data is off by a byte or two and not sure why, email info@getid3.org if you can explain better
+								fseek($fd, $dts_offset, SEEK_SET);
+								$magic_test = fread($fd, 8);
+								for ($i = 0; $i < 4; $i++) {
+									// look to see if DTS "magic" is here, if so adjust offset by that many bytes
+									if (substr($magic_test, $i, 4) == "\x7F\xFE\x80\x01") {
+										$dts_offset += $i;
+										break;
+									}
+								}
+								if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.dts.php', __FILE__, false)) {
+									$dts_thisfileinfo = array('avdataoffset'=>$dts_offset);
+									$getid3_dts = new getid3_dts($fd, $dts_thisfileinfo);
+									$ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $dts_thisfileinfo;
+									if (!empty($dts_thisfileinfo['error'])) {
+										foreach ($dts_thisfileinfo['error'] as $newerror) {
+											$this->warnings[] = 'getid3_dts() says: ['.$newerror.']';
+										}
+									}
+									if (!empty($dts_thisfileinfo['warning'])) {
+										foreach ($dts_thisfileinfo['warning'] as $newerror) {
+											$this->warnings[] = 'getid3_dts() says: ['.$newerror.']';
+										}
+									}
+									if (isset($dts_thisfileinfo['audio']) && is_array($dts_thisfileinfo['audio'])) {
+										foreach ($dts_thisfileinfo['audio'] as $key => $value) {
+											$track_info[$key] = $value;
+										}
+									}
+									unset($dts_thisfileinfo);
+									unset($getid3_dts);
+								} else {
+									$this->warnings[] = 'Unable to parse audio data['.__LINE__.'] because cannot include "module.audio.dts.php"';
+								}
+								break;
+
+							//case 'A_AAC':
+							//	if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.aac.php', __FILE__, false)) {
+							//		$aac_thisfileinfo = array('avdataoffset'=>$ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']]);
+							//		$getid3_aac = new getid3_aac($fd, $aac_thisfileinfo);
+							//		$ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $aac_thisfileinfo;
+							//		if (isset($aac_thisfileinfo['audio']) && is_array($aac_thisfileinfo['audio'])) {
+							//			foreach ($aac_thisfileinfo['audio'] as $key => $value) {
+							//				$track_info[$key] = $value;
+							//			}
+							//		}
+							//		unset($aac_thisfileinfo);
+							//		unset($getid3_aac);
+							//	} else {
+							//		$this->warnings[] = 'Unable to parse audio data['.__LINE__.'] because cannot include "module.audio.aac.php"';
+							//	}
+							//	break;
+
+							case 'A_MPEG/L3':
+								if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, false)) {
+									$mp3_thisfileinfo = array(
+										'avdataoffset' => $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']],
+										'avdataend'    => $ThisFileInfo['matroska']['track_data_offsets'][$trackarray['TrackNumber']] + 1024,
+									);
+									$getid3_mp3 = new getid3_mp3($fd, $mp3_thisfileinfo);
+									$getid3_mp3->allow_bruteforce = true;
+									//getid3_mp3::getOnlyMPEGaudioInfo($fd, $mp3_thisfileinfo, $offset, false);
+									$ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $mp3_thisfileinfo;
+									if (!empty($mp3_thisfileinfo['error'])) {
+										foreach ($mp3_thisfileinfo['error'] as $newerror) {
+											$this->warnings[] = 'getid3_mp3() says: ['.$newerror.']';
+										}
+									}
+									if (!empty($mp3_thisfileinfo['warning'])) {
+										foreach ($mp3_thisfileinfo['warning'] as $newerror) {
+											$this->warnings[] = 'getid3_mp3() says: ['.$newerror.']';
+										}
+									}
+									if (isset($mp3_thisfileinfo['audio']) && is_array($mp3_thisfileinfo['audio'])) {
+										foreach ($mp3_thisfileinfo['audio'] as $key => $value) {
+											$track_info[$key] = $value;
+										}
+									}
+									unset($mp3_thisfileinfo);
+									unset($getid3_mp3);
+								} else {
+									$this->warnings[] = 'Unable to parse audio data['.__LINE__.'] because cannot include "module.audio.mp3.php"';
+								}
+								break;
+
+							case 'A_VORBIS':
+								if (isset($trackarray['CodecPrivate'])) {
+									// this is a NASTY hack, email info@getid3.org if you have a better idea how to get this info out
+									$found_vorbis = false;
+									for ($vorbis_offset = 1; $vorbis_offset < 16; $vorbis_offset++) {
+										if (substr($trackarray['CodecPrivate'], $vorbis_offset, 6) == 'vorbis') {
+											$vorbis_offset--;
+											$found_vorbis = true;
+											break;
+										}
+									}
+									if ($found_vorbis) {
+										if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, false)) {
+											$vorbis_fileinfo = array();
+											$oggpageinfo['page_seqno'] = 0;
+											getid3_ogg::ParseVorbisPageHeader($trackarray['CodecPrivate'], $vorbis_offset, $vorbis_fileinfo, $oggpageinfo);
+											$ThisFileInfo['matroska']['track_codec_parsed'][$trackarray['TrackNumber']] = $vorbis_fileinfo;
+											if (!empty($vorbis_fileinfo['error'])) {
+												foreach ($vorbis_fileinfo['error'] as $newerror) {
+													$this->warnings[] = 'getid3_ogg() says: ['.$newerror.']';
+												}
+											}
+											if (!empty($vorbis_fileinfo['warning'])) {
+												foreach ($vorbis_fileinfo['warning'] as $newerror) {
+													$this->warnings[] = 'getid3_ogg() says: ['.$newerror.']';
+												}
+											}
+											if (isset($vorbis_fileinfo['audio']) && is_array($vorbis_fileinfo['audio'])) {
+												foreach ($vorbis_fileinfo['audio'] as $key => $value) {
+													$track_info[$key] = $value;
+												}
+											}
+											if (@$vorbis_fileinfo['ogg']['bitrate_average']) {
+												$track_info['bitrate'] = $vorbis_fileinfo['ogg']['bitrate_average'];
+											} elseif (@$vorbis_fileinfo['ogg']['bitrate_nominal']) {
+												$track_info['bitrate'] = $vorbis_fileinfo['ogg']['bitrate_nominal'];
+											}
+											unset($vorbis_fileinfo);
+											unset($oggpageinfo);
+										} else {
+											$this->warnings[] = 'Unable to parse audio data['.__LINE__.'] because cannot include "module.audio.ogg.php"';
+										}
+									} else {
+									}
+								} else {
+								}
+								break;
+
+							default:
+								$this->warnings[] = 'Unhandled audio type "'.@$trackarray[$this->EBMLidName(EBML_ID_CODECID)].'"';
+								break;
+						}
+
+
+						$ThisFileInfo['audio']['streams'][] = $track_info;
+						if (isset($track_info['dataformat']) && empty($ThisFileInfo['audio']['dataformat'])) {
+							foreach ($track_info as $key => $value) {
+								$ThisFileInfo['audio'][$key] = $value;
+							}
+						}
+						break;
+					default:
+						// ignore, do nothing
+						break;
+				}
+			}
+		}
+
+		if ($this->hide_clusters) {
+			// too much data returned that is usually not useful
+			if (isset($ThisFileInfo['matroska']['segments']) && is_array($ThisFileInfo['matroska']['segments'])) {
+				foreach ($ThisFileInfo['matroska']['segments'] as $key => $segmentsarray) {
+					if ($segmentsarray['id'] == EBML_ID_CLUSTER) {
+						unset($ThisFileInfo['matroska']['segments'][$key]);
+					}
+				}
+			}
+			if (isset($ThisFileInfo['matroska']['seek']) && is_array($ThisFileInfo['matroska']['seek'])) {
+				foreach ($ThisFileInfo['matroska']['seek'] as $key => $seekarray) {
+					if ($seekarray['target_id'] == EBML_ID_CLUSTER) {
+						unset($ThisFileInfo['matroska']['seek'][$key]);
+					}
+				}
+			}
+			//unset($ThisFileInfo['matroska']['cluster']);
+			//unset($ThisFileInfo['matroska']['track_data_offsets']);
+		}
+
+		if (!empty($ThisFileInfo['video']['streams'])) {
+			$ThisFileInfo['mime_type'] = 'video/x-matroska';
+		} elseif (!empty($ThisFileInfo['video']['streams'])) {
+			$ThisFileInfo['mime_type'] = 'audio/x-matroska';
+		} elseif (isset($ThisFileInfo['mime_type'])) {
+			unset($ThisFileInfo['mime_type']);
+		}
+
+		foreach ($this->warnings as $key => $value) {
+			$ThisFileInfo['warning'][] = $value;
+		}
+
+		return true;
+	}
+
+
+///////////////////////////////////////
+
+
+	function EnsureBufferHasEnoughData(&$fd, &$EBMLdata, &$offset, &$EBMLdata_offset) {
+		$min_data = 1024;
+		if ($offset > 2147450880) { // 2^31 - 2^15 (2G-32k)
+			$offset = pow(2,63);
+			return false;
+		} elseif (($offset - $EBMLdata_offset) >= (strlen($EBMLdata) - $min_data)) {
+			fseek($fd, $offset, SEEK_SET);
+			$EBMLdata_offset = ftell($fd);
+			$EBMLdata = fread($fd, $this->read_buffer_size);
+		}
+		return true;
+	}
+
+	function readEBMLint(&$string, &$offset, $dataoffset=0) {
+		$actual_offset = $offset - $dataoffset;
+		if ($offset > 2147450880) { // 2^31 - 2^15 (2G-32k)
+			$this->warnings[] = 'aborting readEBMLint() because $offset larger than 2GB';
+			return false;
+		} elseif ($actual_offset >= strlen($string)) {
+			$this->warnings[] = '$actual_offset > $string in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')';
+			return false;
+		} elseif ($actual_offset < 0) {
+			$this->warnings[] = '$actual_offset < 0 in readEBMLint($string['.strlen($string).'], '.$offset.', '.$dataoffset.')';
+			return false;
+		}
+		$first_byte_int = ord($string{$actual_offset});
+		if (0x80 & $first_byte_int) {
+			$length = 1;
+		} elseif (0x40 & $first_byte_int) {
+			$length = 2;
+		} elseif (0x20 & $first_byte_int) {
+			$length = 3;
+		} elseif (0x10 & $first_byte_int) {
+			$length = 4;
+		} elseif (0x08 & $first_byte_int) {
+			$length = 5;
+		} elseif (0x04 & $first_byte_int) {
+			$length = 6;
+		} elseif (0x02 & $first_byte_int) {
+			$length = 7;
+		} elseif (0x01 & $first_byte_int) {
+			$length = 8;
+		} else {
+			$offset = pow(2,63); // abort processing, skip to end of file
+			$this->warnings[] = 'invalid EBML integer (leading 0x00) at '.$offset;
+			return false;
+		}
+		$int_value = $this->EBML2Int(substr($string, $actual_offset, $length));
+		$offset += $length;
+		return $int_value;
+	}
+
+	function EBML2Int($EBMLstring) {
+		// http://matroska.org/specs/
+
+		// Element ID coded with an UTF-8 like system:
+		// 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
+		// 01xx xxxx  xxxx xxxx                       - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
+		// 001x xxxx  xxxx xxxx  xxxx xxxx            - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
+		// 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
+		// Values with all x at 0 and 1 are reserved (hence the -2).
+
+		// Data size, in octets, is also coded with an UTF-8 like system :
+		// 1xxx xxxx                                                                              - value 0 to  2^7-2
+		// 01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
+		// 001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
+		// 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
+		// 0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
+		// 0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
+		// 0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
+		// 0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
+
+		$first_byte_int = ord($EBMLstring{0});
+		if (0x80 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x7F);
+		} elseif (0x40 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x3F);
+		} elseif (0x20 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x1F);
+		} elseif (0x10 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x0F);
+		} elseif (0x08 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x07);
+		} elseif (0x04 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x03);
+		} elseif (0x02 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x01);
+		} elseif (0x01 & $first_byte_int) {
+			$EBMLstring{0} = chr($first_byte_int & 0x00);
+		} else {
+			return false;
+		}
+		return getid3_lib::BigEndian2Int($EBMLstring);
+	}
+
+
+	function EBMLdate2unix($EBMLdatestamp) {
+		// Date - signed 8 octets integer in nanoseconds with 0 indicating the precise beginning of the millennium (at 2001-01-01T00:00:00,000000000 UTC)
+		// 978307200 == mktime(0, 0, 0, 1, 1, 2001) == January 1, 2001 12:00:00am UTC
+		return round(($EBMLdatestamp / 1000000000) + 978307200);
+	}
+
+
+	function MatroskaBlockLacingType($lacingtype) {
+		// http://matroska.org/technical/specs/index.html#block_structure
+		static $MatroskaBlockLacingType = array();
+		if (empty($MatroskaBlockLacingType)) {
+			$MatroskaBlockLacingType[0x00] = 'no lacing';
+			$MatroskaBlockLacingType[0x01] = 'Xiph lacing';
+			$MatroskaBlockLacingType[0x02] = 'fixed-size lacing';
+			$MatroskaBlockLacingType[0x03] = 'EBML lacing';
+		}
+		return (isset($MatroskaBlockLacingType[$lacingtype]) ? $MatroskaBlockLacingType[$lacingtype] : $lacingtype);
+	}
+
+	function MatroskaCodecIDtoCommonName($codecid) {
+		// http://www.matroska.org/technical/specs/codecid/index.html
+		static $MatroskaCodecIDlist = array();
+		if (empty($MatroskaCodecIDlist)) {
+			$MatroskaCodecIDlist['A_AAC']            = 'aac';
+			$MatroskaCodecIDlist['A_AAC/MPEG2/LC']   = 'aac';
+			$MatroskaCodecIDlist['A_AC3']            = 'ac3';
+			$MatroskaCodecIDlist['A_DTS']            = 'dts';
+			$MatroskaCodecIDlist['A_FLAC']           = 'flac';
+			$MatroskaCodecIDlist['A_MPEG/L1']        = 'mp1';
+			$MatroskaCodecIDlist['A_MPEG/L2']        = 'mp2';
+			$MatroskaCodecIDlist['A_MPEG/L3']        = 'mp3';
+			$MatroskaCodecIDlist['A_PCM/INT/LIT']    = 'pcm';       // PCM Integer Little Endian
+			$MatroskaCodecIDlist['A_PCM/INT/BIG']    = 'pcm';       // PCM Integer Big Endian
+			$MatroskaCodecIDlist['A_QUICKTIME/QDMC'] = 'quicktime'; // Quicktime: QDesign Music
+			$MatroskaCodecIDlist['A_QUICKTIME/QDM2'] = 'quicktime'; // Quicktime: QDesign Music v2
+			$MatroskaCodecIDlist['A_VORBIS']         = 'vorbis';
+			$MatroskaCodecIDlist['V_MPEG1']          = 'mpeg';
+			$MatroskaCodecIDlist['V_THEORA']         = 'theora';
+			$MatroskaCodecIDlist['V_REAL/RV40']      = 'real';
+			$MatroskaCodecIDlist['V_REAL/RV10']      = 'real';
+			$MatroskaCodecIDlist['V_REAL/RV20']      = 'real';
+			$MatroskaCodecIDlist['V_REAL/RV30']      = 'real';
+			$MatroskaCodecIDlist['V_QUICKTIME']      = 'quicktime'; // Quicktime
+			$MatroskaCodecIDlist['V_MPEG4/ISO/AP']   = 'mpeg4';
+			$MatroskaCodecIDlist['V_MPEG4/ISO/ASP']  = 'mpeg4';
+			$MatroskaCodecIDlist['V_MPEG4/ISO/AVC']  = 'h264';
+			$MatroskaCodecIDlist['V_MPEG4/ISO/SP']   = 'mpeg4';
+		}
+		return (isset($MatroskaCodecIDlist[$codecid]) ? $MatroskaCodecIDlist[$codecid] : $codecid);
+	}
+
+	function EBMLidName($value) {
+		static $EBMLidList = array();
+		if (empty($EBMLidList)) {
+			$EBMLidList[EBML_ID_ASPECTRATIOTYPE]            = 'AspectRatioType';
+			$EBMLidList[EBML_ID_ATTACHEDFILE]               = 'AttachedFile';
+			$EBMLidList[EBML_ID_ATTACHMENTLINK]             = 'AttachmentLink';
+			$EBMLidList[EBML_ID_ATTACHMENTS]                = 'Attachments';
+			$EBMLidList[EBML_ID_ATTACHMENTUID]              = 'AttachmentUID';
+			$EBMLidList[EBML_ID_AUDIO]                      = 'Audio';
+			$EBMLidList[EBML_ID_BITDEPTH]                   = 'BitDepth';
+			$EBMLidList[EBML_ID_CHANNELPOSITIONS]           = 'ChannelPositions';
+			$EBMLidList[EBML_ID_CHANNELS]                   = 'Channels';
+			$EBMLidList[EBML_ID_CHAPCOUNTRY]                = 'ChapCountry';
+			$EBMLidList[EBML_ID_CHAPLANGUAGE]               = 'ChapLanguage';
+			$EBMLidList[EBML_ID_CHAPPROCESS]                = 'ChapProcess';
+			$EBMLidList[EBML_ID_CHAPPROCESSCODECID]         = 'ChapProcessCodecID';
+			$EBMLidList[EBML_ID_CHAPPROCESSCOMMAND]         = 'ChapProcessCommand';
+			$EBMLidList[EBML_ID_CHAPPROCESSDATA]            = 'ChapProcessData';
+			$EBMLidList[EBML_ID_CHAPPROCESSPRIVATE]         = 'ChapProcessPrivate';
+			$EBMLidList[EBML_ID_CHAPPROCESSTIME]            = 'ChapProcessTime';
+			$EBMLidList[EBML_ID_CHAPSTRING]                 = 'ChapString';
+			$EBMLidList[EBML_ID_CHAPTERATOM]                = 'ChapterAtom';
+			$EBMLidList[EBML_ID_CHAPTERDISPLAY]             = 'ChapterDisplay';
+			$EBMLidList[EBML_ID_CHAPTERFLAGENABLED]         = 'ChapterFlagEnabled';
+			$EBMLidList[EBML_ID_CHAPTERFLAGHIDDEN]          = 'ChapterFlagHidden';
+			$EBMLidList[EBML_ID_CHAPTERPHYSICALEQUIV]       = 'ChapterPhysicalEquiv';
+			$EBMLidList[EBML_ID_CHAPTERS]                   = 'Chapters';
+			$EBMLidList[EBML_ID_CHAPTERSEGMENTEDITIONUID]   = 'ChapterSegmentEditionUID';
+			$EBMLidList[EBML_ID_CHAPTERSEGMENTUID]          = 'ChapterSegmentUID';
+			$EBMLidList[EBML_ID_CHAPTERTIMEEND]             = 'ChapterTimeEnd';
+			$EBMLidList[EBML_ID_CHAPTERTIMESTART]           = 'ChapterTimeStart';
+			$EBMLidList[EBML_ID_CHAPTERTRACK]               = 'ChapterTrack';
+			$EBMLidList[EBML_ID_CHAPTERTRACKNUMBER]         = 'ChapterTrackNumber';
+			$EBMLidList[EBML_ID_CHAPTERTRANSLATE]           = 'ChapterTranslate';
+			$EBMLidList[EBML_ID_CHAPTERTRANSLATECODEC]      = 'ChapterTranslateCodec';
+			$EBMLidList[EBML_ID_CHAPTERTRANSLATEEDITIONUID] = 'ChapterTranslateEditionUID';
+			$EBMLidList[EBML_ID_CHAPTERTRANSLATEID]         = 'ChapterTranslateID';
+			$EBMLidList[EBML_ID_CHAPTERUID]                 = 'ChapterUID';
+			$EBMLidList[EBML_ID_CLUSTER]                    = 'Cluster';
+			$EBMLidList[EBML_ID_CLUSTERBLOCK]               = 'ClusterBlock';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKADDID]          = 'ClusterBlockAddID';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONAL]     = 'ClusterBlockAdditional';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONID]     = 'ClusterBlockAdditionID';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKADDITIONS]      = 'ClusterBlockAdditions';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKDURATION]       = 'ClusterBlockDuration';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKGROUP]          = 'ClusterBlockGroup';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKMORE]           = 'ClusterBlockMore';
+			$EBMLidList[EBML_ID_CLUSTERBLOCKVIRTUAL]        = 'ClusterBlockVirtual';
+			$EBMLidList[EBML_ID_CLUSTERCODECSTATE]          = 'ClusterCodecState';
+			$EBMLidList[EBML_ID_CLUSTERDELAY]               = 'ClusterDelay';
+			$EBMLidList[EBML_ID_CLUSTERDURATION]            = 'ClusterDuration';
+			$EBMLidList[EBML_ID_CLUSTERENCRYPTEDBLOCK]      = 'ClusterEncryptedBlock';
+			$EBMLidList[EBML_ID_CLUSTERFRAMENUMBER]         = 'ClusterFrameNumber';
+			$EBMLidList[EBML_ID_CLUSTERLACENUMBER]          = 'ClusterLaceNumber';
+			$EBMLidList[EBML_ID_CLUSTERPOSITION]            = 'ClusterPosition';
+			$EBMLidList[EBML_ID_CLUSTERPREVSIZE]            = 'ClusterPrevSize';
+			$EBMLidList[EBML_ID_CLUSTERREFERENCEBLOCK]      = 'ClusterReferenceBlock';
+			$EBMLidList[EBML_ID_CLUSTERREFERENCEPRIORITY]   = 'ClusterReferencePriority';
+			$EBMLidList[EBML_ID_CLUSTERREFERENCEVIRTUAL]    = 'ClusterReferenceVirtual';
+			$EBMLidList[EBML_ID_CLUSTERSILENTTRACKNUMBER]   = 'ClusterSilentTrackNumber';
+			$EBMLidList[EBML_ID_CLUSTERSILENTTRACKS]        = 'ClusterSilentTracks';
+			$EBMLidList[EBML_ID_CLUSTERSIMPLEBLOCK]         = 'ClusterSimpleBlock';
+			$EBMLidList[EBML_ID_CLUSTERTIMECODE]            = 'ClusterTimecode';
+			$EBMLidList[EBML_ID_CLUSTERTIMESLICE]           = 'ClusterTimeSlice';
+			$EBMLidList[EBML_ID_CODECDECODEALL]             = 'CodecDecodeAll';
+			$EBMLidList[EBML_ID_CODECDOWNLOADURL]           = 'CodecDownloadURL';
+			$EBMLidList[EBML_ID_CODECID]                    = 'CodecID';
+			$EBMLidList[EBML_ID_CODECINFOURL]               = 'CodecInfoURL';
+			$EBMLidList[EBML_ID_CODECNAME]                  = 'CodecName';
+			$EBMLidList[EBML_ID_CODECPRIVATE]               = 'CodecPrivate';
+			$EBMLidList[EBML_ID_CODECSETTINGS]              = 'CodecSettings';
+			$EBMLidList[EBML_ID_COLOURSPACE]                = 'ColourSpace';
+			$EBMLidList[EBML_ID_CONTENTCOMPALGO]            = 'ContentCompAlgo';
+			$EBMLidList[EBML_ID_CONTENTCOMPRESSION]         = 'ContentCompression';
+			$EBMLidList[EBML_ID_CONTENTCOMPSETTINGS]        = 'ContentCompSettings';
+			$EBMLidList[EBML_ID_CONTENTENCALGO]             = 'ContentEncAlgo';
+			$EBMLidList[EBML_ID_CONTENTENCKEYID]            = 'ContentEncKeyID';
+			$EBMLidList[EBML_ID_CONTENTENCODING]            = 'ContentEncoding';
+			$EBMLidList[EBML_ID_CONTENTENCODINGORDER]       = 'ContentEncodingOrder';
+			$EBMLidList[EBML_ID_CONTENTENCODINGS]           = 'ContentEncodings';
+			$EBMLidList[EBML_ID_CONTENTENCODINGSCOPE]       = 'ContentEncodingScope';
+			$EBMLidList[EBML_ID_CONTENTENCODINGTYPE]        = 'ContentEncodingType';
+			$EBMLidList[EBML_ID_CONTENTENCRYPTION]          = 'ContentEncryption';
+			$EBMLidList[EBML_ID_CONTENTSIGALGO]             = 'ContentSigAlgo';
+			$EBMLidList[EBML_ID_CONTENTSIGHASHALGO]         = 'ContentSigHashAlgo';
+			$EBMLidList[EBML_ID_CONTENTSIGKEYID]            = 'ContentSigKeyID';
+			$EBMLidList[EBML_ID_CONTENTSIGNATURE]           = 'ContentSignature';
+			$EBMLidList[EBML_ID_CRC32]                      = 'CRC32';
+			$EBMLidList[EBML_ID_CUEBLOCKNUMBER]             = 'CueBlockNumber';
+			$EBMLidList[EBML_ID_CUECLUSTERPOSITION]         = 'CueClusterPosition';
+			$EBMLidList[EBML_ID_CUECODECSTATE]              = 'CueCodecState';
+			$EBMLidList[EBML_ID_CUEPOINT]                   = 'CuePoint';
+			$EBMLidList[EBML_ID_CUEREFCLUSTER]              = 'CueRefCluster';
+			$EBMLidList[EBML_ID_CUEREFCODECSTATE]           = 'CueRefCodecState';
+			$EBMLidList[EBML_ID_CUEREFERENCE]               = 'CueReference';
+			$EBMLidList[EBML_ID_CUEREFNUMBER]               = 'CueRefNumber';
+			$EBMLidList[EBML_ID_CUEREFTIME]                 = 'CueRefTime';
+			$EBMLidList[EBML_ID_CUES]                       = 'Cues';
+			$EBMLidList[EBML_ID_CUETIME]                    = 'CueTime';
+			$EBMLidList[EBML_ID_CUETRACK]                   = 'CueTrack';
+			$EBMLidList[EBML_ID_CUETRACKPOSITIONS]          = 'CueTrackPositions';
+			$EBMLidList[EBML_ID_DATEUTC]                    = 'DateUTC';
+			$EBMLidList[EBML_ID_DEFAULTDURATION]            = 'DefaultDuration';
+			$EBMLidList[EBML_ID_DISPLAYHEIGHT]              = 'DisplayHeight';
+			$EBMLidList[EBML_ID_DISPLAYUNIT]                = 'DisplayUnit';
+			$EBMLidList[EBML_ID_DISPLAYWIDTH]               = 'DisplayWidth';
+			$EBMLidList[EBML_ID_DOCTYPE]                    = 'DocType';
+			$EBMLidList[EBML_ID_DOCTYPEREADVERSION]         = 'DocTypeReadVersion';
+			$EBMLidList[EBML_ID_DOCTYPEVERSION]             = 'DocTypeVersion';
+			$EBMLidList[EBML_ID_DURATION]                   = 'Duration';
+			$EBMLidList[EBML_ID_EBMLMAXIDLENGTH]            = 'EBMLMaxIDLength';
+			$EBMLidList[EBML_ID_EBMLMAXSIZELENGTH]          = 'EBMLMaxSizeLength';
+			$EBMLidList[EBML_ID_EBMLREADVERSION]            = 'EBMLReadVersion';
+			$EBMLidList[EBML_ID_EBMLVERSION]                = 'EBMLVersion';
+			$EBMLidList[EBML_ID_EDITIONENTRY]               = 'EditionEntry';
+			$EBMLidList[EBML_ID_EDITIONFLAGDEFAULT]         = 'EditionFlagDefault';
+			$EBMLidList[EBML_ID_EDITIONFLAGHIDDEN]          = 'EditionFlagHidden';
+			$EBMLidList[EBML_ID_EDITIONFLAGORDERED]         = 'EditionFlagOrdered';
+			$EBMLidList[EBML_ID_EDITIONUID]                 = 'EditionUID';
+			$EBMLidList[EBML_ID_FILEDATA]                   = 'FileData';
+			$EBMLidList[EBML_ID_FILEDESCRIPTION]            = 'FileDescription';
+			$EBMLidList[EBML_ID_FILEMIMETYPE]               = 'FileMimeType';
+			$EBMLidList[EBML_ID_FILENAME]                   = 'FileName';
+			$EBMLidList[EBML_ID_FILEREFERRAL]               = 'FileReferral';
+			$EBMLidList[EBML_ID_FILEUID]                    = 'FileUID';
+			$EBMLidList[EBML_ID_FLAGDEFAULT]                = 'FlagDefault';
+			$EBMLidList[EBML_ID_FLAGENABLED]                = 'FlagEnabled';
+			$EBMLidList[EBML_ID_FLAGFORCED]                 = 'FlagForced';
+			$EBMLidList[EBML_ID_FLAGINTERLACED]             = 'FlagInterlaced';
+			$EBMLidList[EBML_ID_FLAGLACING]                 = 'FlagLacing';
+			$EBMLidList[EBML_ID_GAMMAVALUE]                 = 'GammaValue';
+			$EBMLidList[EBML_ID_INFO]                       = 'Info';
+			$EBMLidList[EBML_ID_LANGUAGE]                   = 'Language';
+			$EBMLidList[EBML_ID_MAXBLOCKADDITIONID]         = 'MaxBlockAdditionID';
+			$EBMLidList[EBML_ID_MAXCACHE]                   = 'MaxCache';
+			$EBMLidList[EBML_ID_MINCACHE]                   = 'MinCache';
+			$EBMLidList[EBML_ID_MUXINGAPP]                  = 'MuxingApp';
+			$EBMLidList[EBML_ID_NAME]                       = 'Name';
+			$EBMLidList[EBML_ID_NEXTFILENAME]               = 'NextFilename';
+			$EBMLidList[EBML_ID_NEXTUID]                    = 'NextUID';
+			$EBMLidList[EBML_ID_OUTPUTSAMPLINGFREQUENCY]    = 'OutputSamplingFrequency';
+			$EBMLidList[EBML_ID_PIXELCROPBOTTOM]            = 'PixelCropBottom';
+			$EBMLidList[EBML_ID_PIXELCROPLEFT]              = 'PixelCropLeft';
+			$EBMLidList[EBML_ID_PIXELCROPRIGHT]             = 'PixelCropRight';
+			$EBMLidList[EBML_ID_PIXELCROPTOP]               = 'PixelCropTop';
+			$EBMLidList[EBML_ID_PIXELHEIGHT]                = 'PixelHeight';
+			$EBMLidList[EBML_ID_PIXELWIDTH]                 = 'PixelWidth';
+			$EBMLidList[EBML_ID_PREVFILENAME]               = 'PrevFilename';
+			$EBMLidList[EBML_ID_PREVUID]                    = 'PrevUID';
+			$EBMLidList[EBML_ID_SAMPLINGFREQUENCY]          = 'SamplingFrequency';
+			$EBMLidList[EBML_ID_SEEK]                       = 'Seek';
+			$EBMLidList[EBML_ID_SEEKHEAD]                   = 'SeekHead';
+			$EBMLidList[EBML_ID_SEEKID]                     = 'SeekID';
+			$EBMLidList[EBML_ID_SEEKPOSITION]               = 'SeekPosition';
+			$EBMLidList[EBML_ID_SEGMENTFAMILY]              = 'SegmentFamily';
+			$EBMLidList[EBML_ID_SEGMENTFILENAME]            = 'SegmentFilename';
+			$EBMLidList[EBML_ID_SEGMENTUID]                 = 'SegmentUID';
+			$EBMLidList[EBML_ID_SIMPLETAG]                  = 'SimpleTag';
+			$EBMLidList[EBML_ID_CLUSTERSLICES]              = 'ClusterSlices';
+			$EBMLidList[EBML_ID_STEREOMODE]                 = 'StereoMode';
+			$EBMLidList[EBML_ID_TAG]                        = 'Tag';
+			$EBMLidList[EBML_ID_TAGBINARY]                  = 'TagBinary';
+			$EBMLidList[EBML_ID_TAGCHAPTERUID]              = 'TagChapterUID';
+			$EBMLidList[EBML_ID_TAGDEFAULT]                 = 'TagDefault';
+			$EBMLidList[EBML_ID_TAGEDITIONUID]              = 'TagEditionUID';
+			$EBMLidList[EBML_ID_TAGLANGUAGE]                = 'TagLanguage';
+			$EBMLidList[EBML_ID_TAGNAME]                    = 'TagName';
+			$EBMLidList[EBML_ID_TAGTRACKUID]                = 'TagTrackUID';
+			$EBMLidList[EBML_ID_TAGS]                       = 'Tags';
+			$EBMLidList[EBML_ID_TAGSTRING]                  = 'TagString';
+			$EBMLidList[EBML_ID_TARGETS]                    = 'Targets';
+			$EBMLidList[EBML_ID_TARGETTYPE]                 = 'TargetType';
+			$EBMLidList[EBML_ID_TARGETTYPEVALUE]            = 'TargetTypeValue';
+			$EBMLidList[EBML_ID_TIMECODESCALE]              = 'TimecodeScale';
+			$EBMLidList[EBML_ID_TITLE]                      = 'Title';
+			$EBMLidList[EBML_ID_TRACKENTRY]                 = 'TrackEntry';
+			$EBMLidList[EBML_ID_TRACKNUMBER]                = 'TrackNumber';
+			$EBMLidList[EBML_ID_TRACKOFFSET]                = 'TrackOffset';
+			$EBMLidList[EBML_ID_TRACKOVERLAY]               = 'TrackOverlay';
+			$EBMLidList[EBML_ID_TRACKS]                     = 'Tracks';
+			$EBMLidList[EBML_ID_TRACKTIMECODESCALE]         = 'TrackTimecodeScale';
+			$EBMLidList[EBML_ID_TRACKTRANSLATE]             = 'TrackTranslate';
+			$EBMLidList[EBML_ID_TRACKTRANSLATECODEC]        = 'TrackTranslateCodec';
+			$EBMLidList[EBML_ID_TRACKTRANSLATEEDITIONUID]   = 'TrackTranslateEditionUID';
+			$EBMLidList[EBML_ID_TRACKTRANSLATETRACKID]      = 'TrackTranslateTrackID';
+			$EBMLidList[EBML_ID_TRACKTYPE]                  = 'TrackType';
+			$EBMLidList[EBML_ID_TRACKUID]                   = 'TrackUID';
+			$EBMLidList[EBML_ID_VIDEO]                      = 'Video';
+			$EBMLidList[EBML_ID_VOID]                       = 'Void';
+			$EBMLidList[EBML_ID_WRITINGAPP]                 = 'WritingApp';
+		}
+		return (isset($EBMLidList[$value]) ? $EBMLidList[$value] : dechex($value));
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.mpeg.php b/apps/media/getID3/getid3/module.audio-video.mpeg.php
new file mode 100644
index 0000000000000000000000000000000000000000..8f487848495062013030e5017b970386207ee8cc
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.mpeg.php
@@ -0,0 +1,292 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.mpeg.php                                 //
+// module for analyzing MPEG files                             //
+// dependencies: module.audio.mp3.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
+
+define('GETID3_MPEG_VIDEO_PICTURE_START',   "\x00\x00\x01\x00");
+define('GETID3_MPEG_VIDEO_USER_DATA_START', "\x00\x00\x01\xB2");
+define('GETID3_MPEG_VIDEO_SEQUENCE_HEADER', "\x00\x00\x01\xB3");
+define('GETID3_MPEG_VIDEO_SEQUENCE_ERROR',  "\x00\x00\x01\xB4");
+define('GETID3_MPEG_VIDEO_EXTENSION_START', "\x00\x00\x01\xB5");
+define('GETID3_MPEG_VIDEO_SEQUENCE_END',    "\x00\x00\x01\xB7");
+define('GETID3_MPEG_VIDEO_GROUP_START',     "\x00\x00\x01\xB8");
+define('GETID3_MPEG_AUDIO_START',           "\x00\x00\x01\xC0");
+
+
+class getid3_mpeg
+{
+
+	function getid3_mpeg(&$fd, &$ThisFileInfo) {
+		if ($ThisFileInfo['avdataend'] <= $ThisFileInfo['avdataoffset']) {
+			$ThisFileInfo['error'][] = '"avdataend" ('.$ThisFileInfo['avdataend'].') is unexpectedly less-than-or-equal-to "avdataoffset" ('.$ThisFileInfo['avdataoffset'].')';
+			return false;
+		}
+		$ThisFileInfo['fileformat'] = 'mpeg';
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$MPEGstreamData       = fread($fd, min(100000, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']));
+		$MPEGstreamDataLength = strlen($MPEGstreamData);
+
+		$foundVideo = true;
+		$VideoChunkOffset = 0;
+		while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
+			if ($VideoChunkOffset >= $MPEGstreamDataLength) {
+				$foundVideo = false;
+				break;
+			}
+		}
+		if ($foundVideo) {
+
+			// Start code                       32 bits
+			// horizontal frame size            12 bits
+			// vertical frame size              12 bits
+			// pixel aspect ratio                4 bits
+			// frame rate                        4 bits
+			// bitrate                          18 bits
+			// marker bit                        1 bit
+			// VBV buffer size                  10 bits
+			// constrained parameter flag        1 bit
+			// intra quant. matrix flag          1 bit
+			// intra quant. matrix values      512 bits (present if matrix flag == 1)
+			// non-intra quant. matrix flag      1 bit
+			// non-intra quant. matrix values  512 bits (present if matrix flag == 1)
+
+			$ThisFileInfo['video']['dataformat'] = 'mpeg';
+
+			$VideoChunkOffset += (strlen(GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1);
+
+			$FrameSizeDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
+			$VideoChunkOffset += 3;
+
+			$AspectRatioFrameRateDWORD = getid3_lib::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
+			$VideoChunkOffset += 1;
+
+			$assortedinformation = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
+			$VideoChunkOffset += 4;
+
+			$ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xFFF000) >> 12; // 12 bits for horizontal frame size
+			$ThisFileInfo['mpeg']['video']['raw']['framesize_vertical']   = ($FrameSizeDWORD & 0x000FFF);       // 12 bits for vertical frame size
+			$ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']   = ($AspectRatioFrameRateDWORD & 0xF0) >> 4;
+			$ThisFileInfo['mpeg']['video']['raw']['frame_rate']           = ($AspectRatioFrameRateDWORD & 0x0F);
+
+			$ThisFileInfo['mpeg']['video']['framesize_horizontal'] = $ThisFileInfo['mpeg']['video']['raw']['framesize_horizontal'];
+			$ThisFileInfo['mpeg']['video']['framesize_vertical']   = $ThisFileInfo['mpeg']['video']['raw']['framesize_vertical'];
+
+			$ThisFileInfo['mpeg']['video']['pixel_aspect_ratio']      = $this->MPEGvideoAspectRatioLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']);
+			$ThisFileInfo['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($ThisFileInfo['mpeg']['video']['raw']['pixel_aspect_ratio']);
+			$ThisFileInfo['mpeg']['video']['frame_rate']              = $this->MPEGvideoFramerateLookup($ThisFileInfo['mpeg']['video']['raw']['frame_rate']);
+
+			$ThisFileInfo['mpeg']['video']['raw']['bitrate']                =        getid3_lib::Bin2Dec(substr($assortedinformation,  0, 18));
+			$ThisFileInfo['mpeg']['video']['raw']['marker_bit']             = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 18,  1));
+			$ThisFileInfo['mpeg']['video']['raw']['vbv_buffer_size']        =        getid3_lib::Bin2Dec(substr($assortedinformation, 19, 10));
+			$ThisFileInfo['mpeg']['video']['raw']['constrained_param_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 29,  1));
+			$ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']       = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 30,  1));
+			if ($ThisFileInfo['mpeg']['video']['raw']['intra_quant_flag']) {
+
+				// read 512 bits
+				$ThisFileInfo['mpeg']['video']['raw']['intra_quant']          = getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
+				$VideoChunkOffset += 64;
+
+				$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($ThisFileInfo['mpeg']['video']['raw']['intra_quant'], 511,  1));
+				$ThisFileInfo['mpeg']['video']['raw']['intra_quant']          =        getid3_lib::Bin2Dec(substr($assortedinformation, 31,  1)).substr(getid3_lib::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
+
+				if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
+					$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
+					$VideoChunkOffset += 64;
+				}
+
+			} else {
+
+				$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) getid3_lib::Bin2Dec(substr($assortedinformation, 31,  1));
+				if ($ThisFileInfo['mpeg']['video']['raw']['non_intra_quant_flag']) {
+					$ThisFileInfo['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
+					$VideoChunkOffset += 64;
+				}
+
+			}
+
+			if ($ThisFileInfo['mpeg']['video']['raw']['bitrate'] == 0x3FFFF) { // 18 set bits
+
+				$ThisFileInfo['warning'][] = 'This version of getID3() ['.GETID3_VERSION.'] cannot determine average bitrate of VBR MPEG video files';
+				$ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'vbr';
+
+			} else {
+
+				$ThisFileInfo['mpeg']['video']['bitrate']      = $ThisFileInfo['mpeg']['video']['raw']['bitrate'] * 400;
+				$ThisFileInfo['mpeg']['video']['bitrate_mode'] = 'cbr';
+				$ThisFileInfo['video']['bitrate']              = $ThisFileInfo['mpeg']['video']['bitrate'];
+
+			}
+
+			$ThisFileInfo['video']['resolution_x']       = $ThisFileInfo['mpeg']['video']['framesize_horizontal'];
+			$ThisFileInfo['video']['resolution_y']       = $ThisFileInfo['mpeg']['video']['framesize_vertical'];
+			$ThisFileInfo['video']['frame_rate']         = $ThisFileInfo['mpeg']['video']['frame_rate'];
+			$ThisFileInfo['video']['bitrate_mode']       = $ThisFileInfo['mpeg']['video']['bitrate_mode'];
+			$ThisFileInfo['video']['pixel_aspect_ratio'] = $ThisFileInfo['mpeg']['video']['pixel_aspect_ratio'];
+			$ThisFileInfo['video']['lossless']           = false;
+			$ThisFileInfo['video']['bits_per_sample']    = 24;
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?';
+
+		}
+
+		//0x000001B3 begins the sequence_header of every MPEG video stream.
+		//But in MPEG-2, this header must immediately be followed by an
+		//extension_start_code (0x000001B5) with a sequence_extension ID (1).
+		//(This extension contains all the additional MPEG-2 stuff.)
+		//MPEG-1 doesn't have this extension, so that's a sure way to tell the
+		//difference between MPEG-1 and MPEG-2 video streams.
+
+		if (substr($MPEGstreamData, $VideoChunkOffset, 4) == GETID3_MPEG_VIDEO_EXTENSION_START) {
+			$ThisFileInfo['video']['codec'] = 'MPEG-2';
+		} else {
+			$ThisFileInfo['video']['codec'] = 'MPEG-1';
+		}
+
+
+		$AudioChunkOffset = 0;
+		while (true) {
+			while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== GETID3_MPEG_AUDIO_START) {
+				if ($AudioChunkOffset >= $MPEGstreamDataLength) {
+					break 2;
+				}
+			}
+
+			for ($i = 0; $i <= 7; $i++) {
+				// some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
+				// I have no idea why or what the difference is, so this is a stupid hack.
+				// If anybody has any better idea of what's going on, please let me know - info@getid3.org
+
+				$dummy = $ThisFileInfo;
+				if (getid3_mp3::decodeMPEGaudioHeader($fd, ($AudioChunkOffset + 3) + 8 + $i, $dummy, false)) {
+					$ThisFileInfo = $dummy;
+					$ThisFileInfo['audio']['bitrate_mode']    = 'cbr';
+					$ThisFileInfo['audio']['lossless']        = false;
+					break 2;
+
+				}
+			}
+		}
+
+		// Temporary hack to account for interleaving overhead:
+		if (!empty($ThisFileInfo['video']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate'])) {
+			$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['video']['bitrate'] + $ThisFileInfo['audio']['bitrate']);
+
+			// Interleaved MPEG audio/video files have a certain amount of overhead that varies
+			// by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
+			// Use interpolated lookup tables to approximately guess how much is overhead, because
+			// playtime is calculated as filesize / total-bitrate
+			$ThisFileInfo['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($ThisFileInfo['video']['bitrate'], $ThisFileInfo['audio']['bitrate']);
+
+			//switch ($ThisFileInfo['video']['bitrate']) {
+			//	case('5000000'):
+			//		$multiplier = 0.93292642112380355828048824319889;
+			//		break;
+			//	case('5500000'):
+			//		$multiplier = 0.93582895375200989965359777343219;
+			//		break;
+			//	case('6000000'):
+			//		$multiplier = 0.93796247714820932532911373859139;
+			//		break;
+			//	case('7000000'):
+			//		$multiplier = 0.9413264083635103463010117778776;
+			//		break;
+			//	default:
+			//		$multiplier = 1;
+			//		break;
+			//}
+			//$ThisFileInfo['playtime_seconds'] *= $multiplier;
+			//$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
+			if ($ThisFileInfo['video']['bitrate'] < 50000) {
+				$ThisFileInfo['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.';
+			}
+		}
+
+		return true;
+	}
+
+
+	function MPEGsystemNonOverheadPercentage($VideoBitrate, $AudioBitrate) {
+		$OverheadPercentage = 0;
+
+		$AudioBitrate = max(min($AudioBitrate / 1000,   384), 32); // limit to range of 32kbps - 384kbps (should be only legal bitrates, but maybe VBR?)
+		$VideoBitrate = max(min($VideoBitrate / 1000, 10000), 10); // limit to range of 10kbps -  10Mbps (beyond that curves flatten anyways, no big loss)
+
+
+		//OMBB[audiobitrate]              = array(video-10kbps,       video-100kbps,      video-1000kbps,     video-10000kbps)
+		$OverheadMultiplierByBitrate[32]  = array(0, 0.9676287944368530, 0.9802276264360310, 0.9844916183244460, 0.9852821845179940);
+		$OverheadMultiplierByBitrate[48]  = array(0, 0.9779100089209830, 0.9787770035359320, 0.9846738664076130, 0.9852683013799960);
+		$OverheadMultiplierByBitrate[56]  = array(0, 0.9731249855367600, 0.9776624308938040, 0.9832606361852130, 0.9843922606633340);
+		$OverheadMultiplierByBitrate[64]  = array(0, 0.9755642683275760, 0.9795256705493390, 0.9836573009193170, 0.9851122539404470);
+		$OverheadMultiplierByBitrate[96]  = array(0, 0.9788025247497290, 0.9798553314148700, 0.9822956869792560, 0.9834815119124690);
+		$OverheadMultiplierByBitrate[128] = array(0, 0.9816940050925480, 0.9821675936072120, 0.9829756927470870, 0.9839763420152050);
+		$OverheadMultiplierByBitrate[160] = array(0, 0.9825894094561180, 0.9820913399073960, 0.9823907143253970, 0.9832821783651570);
+		$OverheadMultiplierByBitrate[192] = array(0, 0.9832038474336260, 0.9825731694317960, 0.9821028622712400, 0.9828262076447620);
+		$OverheadMultiplierByBitrate[224] = array(0, 0.9836516298538770, 0.9824718601823890, 0.9818302180625380, 0.9823735101626480);
+		$OverheadMultiplierByBitrate[256] = array(0, 0.9845863022094920, 0.9837229411967540, 0.9824521662210830, 0.9828645172100790);
+		$OverheadMultiplierByBitrate[320] = array(0, 0.9849565280263180, 0.9837683142805110, 0.9822885275960400, 0.9824424382727190);
+		$OverheadMultiplierByBitrate[384] = array(0, 0.9856094774357600, 0.9844573394432720, 0.9825970399837330, 0.9824673808303890);
+
+		$BitrateToUseMin = 32;
+		$BitrateToUseMax = 32;
+		$previousBitrate = 32;
+		foreach ($OverheadMultiplierByBitrate as $key => $value) {
+			if ($AudioBitrate >= $previousBitrate) {
+				$BitrateToUseMin = $previousBitrate;
+			}
+			if ($AudioBitrate < $key) {
+				$BitrateToUseMax = $key;
+				break;
+			}
+			$previousBitrate = $key;
+		}
+		$FactorA = ($BitrateToUseMax - $AudioBitrate) / ($BitrateToUseMax - $BitrateToUseMin);
+
+		$VideoBitrateLog10 = log10($VideoBitrate);
+		$VideoFactorMin1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][floor($VideoBitrateLog10)];
+		$VideoFactorMin2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][floor($VideoBitrateLog10)];
+		$VideoFactorMax1 = $OverheadMultiplierByBitrate[$BitrateToUseMin][ceil($VideoBitrateLog10)];
+		$VideoFactorMax2 = $OverheadMultiplierByBitrate[$BitrateToUseMax][ceil($VideoBitrateLog10)];
+		$FactorV = $VideoBitrateLog10 - floor($VideoBitrateLog10);
+
+		$OverheadPercentage  = $VideoFactorMin1 *      $FactorA  *      $FactorV;
+		$OverheadPercentage += $VideoFactorMin2 * (1 - $FactorA) *      $FactorV;
+		$OverheadPercentage += $VideoFactorMax1 *      $FactorA  * (1 - $FactorV);
+		$OverheadPercentage += $VideoFactorMax2 * (1 - $FactorA) * (1 - $FactorV);
+
+		return $OverheadPercentage;
+	}
+
+
+	function MPEGvideoFramerateLookup($rawframerate) {
+		$MPEGvideoFramerateLookup = array(0, 23.976, 24, 25, 29.97, 30, 50, 59.94, 60);
+		return (isset($MPEGvideoFramerateLookup[$rawframerate]) ? (float) $MPEGvideoFramerateLookup[$rawframerate] : (float) 0);
+	}
+
+	function MPEGvideoAspectRatioLookup($rawaspectratio) {
+		$MPEGvideoAspectRatioLookup = array(0, 1, 0.6735, 0.7031, 0.7615, 0.8055, 0.8437, 0.8935, 0.9157, 0.9815, 1.0255, 1.0695, 1.0950, 1.1575, 1.2015, 0);
+		return (isset($MPEGvideoAspectRatioLookup[$rawaspectratio]) ? (float) $MPEGvideoAspectRatioLookup[$rawaspectratio] : (float) 0);
+	}
+
+	function MPEGvideoAspectRatioTextLookup($rawaspectratio) {
+		$MPEGvideoAspectRatioTextLookup = array('forbidden', 'square pixels', '0.6735', '16:9, 625 line, PAL', '0.7615', '0.8055', '16:9, 525 line, NTSC', '0.8935', '4:3, 625 line, PAL, CCIR601', '0.9815', '1.0255', '1.0695', '4:3, 525 line, NTSC, CCIR601', '1.1575', '1.2015', 'reserved');
+		return (isset($MPEGvideoAspectRatioTextLookup[$rawaspectratio]) ? $MPEGvideoAspectRatioTextLookup[$rawaspectratio] : '');
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.nsv.php b/apps/media/getID3/getid3/module.audio-video.nsv.php
new file mode 100644
index 0000000000000000000000000000000000000000..dab03389b577941ac633bd3d77dccfc553e5f0b6
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.nsv.php
@@ -0,0 +1,224 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.nsv.php                                        //
+// module for analyzing Nullsoft NSV files                     //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_nsv
+{
+
+	function getid3_nsv(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$NSVheader = fread($fd, 4);
+
+		switch ($NSVheader) {
+			case 'NSVs':
+				if ($this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, 0)) {
+					$ThisFileInfo['fileformat']          = 'nsv';
+					$ThisFileInfo['audio']['dataformat'] = 'nsv';
+					$ThisFileInfo['video']['dataformat'] = 'nsv';
+					$ThisFileInfo['audio']['lossless']   = false;
+					$ThisFileInfo['video']['lossless']   = false;
+				}
+				break;
+
+			case 'NSVf':
+				if ($this->getNSVfHeaderFilepointer($fd, $ThisFileInfo, 0)) {
+					$ThisFileInfo['fileformat']          = 'nsv';
+					$ThisFileInfo['audio']['dataformat'] = 'nsv';
+					$ThisFileInfo['video']['dataformat'] = 'nsv';
+					$ThisFileInfo['audio']['lossless']   = false;
+					$ThisFileInfo['video']['lossless']   = false;
+					$this->getNSVsHeaderFilepointer($fd, $ThisFileInfo, $ThisFileInfo['nsv']['NSVf']['header_length']);
+				}
+				break;
+
+			default:
+				$ThisFileInfo['error'][] = 'Expecting "NSVs" or "NSVf" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$NSVheader.'"';
+				return false;
+				break;
+		}
+
+		if (!isset($ThisFileInfo['nsv']['NSVf'])) {
+			$ThisFileInfo['warning'][] = 'NSVf header not present - cannot calculate playtime or bitrate';
+		}
+
+		return true;
+	}
+
+	function getNSVsHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset) {
+		fseek($fd, $fileoffset, SEEK_SET);
+		$NSVsheader = fread($fd, 28);
+		$offset = 0;
+
+		$ThisFileInfo['nsv']['NSVs']['identifier']      =                  substr($NSVsheader, $offset, 4);
+		$offset += 4;
+
+		if ($ThisFileInfo['nsv']['NSVs']['identifier'] != 'NSVs') {
+			$ThisFileInfo['error'][] = 'expected "NSVs" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVs']['identifier'].'" instead';
+			unset($ThisFileInfo['nsv']['NSVs']);
+			return false;
+		}
+
+		$ThisFileInfo['nsv']['NSVs']['offset']          = $fileoffset;
+
+		$ThisFileInfo['nsv']['NSVs']['video_codec']     =                              substr($NSVsheader, $offset, 4);
+		$offset += 4;
+		$ThisFileInfo['nsv']['NSVs']['audio_codec']     =                              substr($NSVsheader, $offset, 4);
+		$offset += 4;
+		$ThisFileInfo['nsv']['NSVs']['resolution_x']    = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
+		$offset += 2;
+		$ThisFileInfo['nsv']['NSVs']['resolution_y']    = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
+		$offset += 2;
+
+		$ThisFileInfo['nsv']['NSVs']['framerate_index'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown1b']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown1c']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown1d']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown2a']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown2b']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown2c']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+		//$ThisFileInfo['nsv']['NSVs']['unknown2d']       = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+		$offset += 1;
+
+		switch ($ThisFileInfo['nsv']['NSVs']['audio_codec']) {
+			case 'PCM ':
+				$ThisFileInfo['nsv']['NSVs']['bits_channel'] = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+				$offset += 1;
+				$ThisFileInfo['nsv']['NSVs']['channels']     = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 1));
+				$offset += 1;
+				$ThisFileInfo['nsv']['NSVs']['sample_rate']  = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 2));
+				$offset += 2;
+
+				$ThisFileInfo['audio']['sample_rate']        = $ThisFileInfo['nsv']['NSVs']['sample_rate'];
+				break;
+
+			case 'MP3 ':
+			case 'NONE':
+			default:
+				//$ThisFileInfo['nsv']['NSVs']['unknown3']     = getid3_lib::LittleEndian2Int(substr($NSVsheader, $offset, 4));
+				$offset += 4;
+				break;
+		}
+
+		$ThisFileInfo['video']['resolution_x']       = $ThisFileInfo['nsv']['NSVs']['resolution_x'];
+		$ThisFileInfo['video']['resolution_y']       = $ThisFileInfo['nsv']['NSVs']['resolution_y'];
+		$ThisFileInfo['nsv']['NSVs']['frame_rate']   = $this->NSVframerateLookup($ThisFileInfo['nsv']['NSVs']['framerate_index']);
+		$ThisFileInfo['video']['frame_rate']         = $ThisFileInfo['nsv']['NSVs']['frame_rate'];
+		$ThisFileInfo['video']['bits_per_sample']    = 24;
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		return true;
+	}
+
+	function getNSVfHeaderFilepointer(&$fd, &$ThisFileInfo, $fileoffset, $getTOCoffsets=false) {
+		fseek($fd, $fileoffset, SEEK_SET);
+		$NSVfheader = fread($fd, 28);
+		$offset = 0;
+
+		$ThisFileInfo['nsv']['NSVf']['identifier']    =                  substr($NSVfheader, $offset, 4);
+		$offset += 4;
+
+		if ($ThisFileInfo['nsv']['NSVf']['identifier'] != 'NSVf') {
+			$ThisFileInfo['error'][] = 'expected "NSVf" at offset ('.$fileoffset.'), found "'.$ThisFileInfo['nsv']['NSVf']['identifier'].'" instead';
+			unset($ThisFileInfo['nsv']['NSVf']);
+			return false;
+		}
+
+		$ThisFileInfo['nsv']['NSVs']['offset']        = $fileoffset;
+
+		$ThisFileInfo['nsv']['NSVf']['header_length'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+		$offset += 4;
+		$ThisFileInfo['nsv']['NSVf']['file_size']     = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+		$offset += 4;
+
+		if ($ThisFileInfo['nsv']['NSVf']['file_size'] > $ThisFileInfo['avdataend']) {
+			$ThisFileInfo['warning'][] = 'truncated file - NSVf header indicates '.$ThisFileInfo['nsv']['NSVf']['file_size'].' bytes, file actually '.$ThisFileInfo['avdataend'].' bytes';
+		}
+
+		$ThisFileInfo['nsv']['NSVf']['playtime_ms']   = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+		$offset += 4;
+		$ThisFileInfo['nsv']['NSVf']['meta_size']     = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+		$offset += 4;
+		$ThisFileInfo['nsv']['NSVf']['TOC_entries_1'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+		$offset += 4;
+		$ThisFileInfo['nsv']['NSVf']['TOC_entries_2'] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+		$offset += 4;
+
+		if ($ThisFileInfo['nsv']['NSVf']['playtime_ms'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
+			return false;
+		}
+
+		$NSVfheader .= fread($fd, $ThisFileInfo['nsv']['NSVf']['meta_size'] + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) + (4 * $ThisFileInfo['nsv']['NSVf']['TOC_entries_2']));
+		$NSVfheaderlength = strlen($NSVfheader);
+		$ThisFileInfo['nsv']['NSVf']['metadata']      =                  substr($NSVfheader, $offset, $ThisFileInfo['nsv']['NSVf']['meta_size']);
+		$offset += $ThisFileInfo['nsv']['NSVf']['meta_size'];
+
+		if ($getTOCoffsets) {
+			$TOCcounter = 0;
+			while ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
+				if ($TOCcounter < $ThisFileInfo['nsv']['NSVf']['TOC_entries_1']) {
+					$ThisFileInfo['nsv']['NSVf']['TOC_1'][$TOCcounter] = getid3_lib::LittleEndian2Int(substr($NSVfheader, $offset, 4));
+					$offset += 4;
+					$TOCcounter++;
+				}
+			}
+		}
+
+		if (trim($ThisFileInfo['nsv']['NSVf']['metadata']) != '') {
+			$ThisFileInfo['nsv']['NSVf']['metadata'] = str_replace('`', "\x01", $ThisFileInfo['nsv']['NSVf']['metadata']);
+			$CommentPairArray = explode("\x01".' ', $ThisFileInfo['nsv']['NSVf']['metadata']);
+			foreach ($CommentPairArray as $CommentPair) {
+				if (strstr($CommentPair, '='."\x01")) {
+					list($key, $value) = explode('='."\x01", $CommentPair, 2);
+					$ThisFileInfo['nsv']['comments'][strtolower($key)][] = trim(str_replace("\x01", '', $value));
+				}
+			}
+		}
+
+		$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['nsv']['NSVf']['playtime_ms'] / 1000;
+		$ThisFileInfo['bitrate']          = ($ThisFileInfo['nsv']['NSVf']['file_size'] * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+	}
+
+
+	function NSVframerateLookup($framerateindex) {
+		if ($framerateindex <= 127) {
+			return (float) $framerateindex;
+		}
+
+		static $NSVframerateLookup = array();
+		if (empty($NSVframerateLookup)) {
+			$NSVframerateLookup[129] = (float) 29.970;
+			$NSVframerateLookup[131] = (float) 23.976;
+			$NSVframerateLookup[133] = (float) 14.985;
+			$NSVframerateLookup[197] = (float) 59.940;
+			$NSVframerateLookup[199] = (float) 47.952;
+		}
+		return (isset($NSVframerateLookup[$framerateindex]) ? $NSVframerateLookup[$framerateindex] : false);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.quicktime.php b/apps/media/getID3/getid3/module.audio-video.quicktime.php
new file mode 100644
index 0000000000000000000000000000000000000000..681daf7ec297633d1676401d75095f40db4a6c1d
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.quicktime.php
@@ -0,0 +1,1382 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.quicktime.php                            //
+// module for analyzing Quicktime and MP3-in-MP4 files         //
+// dependencies: module.audio.mp3.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
+
+class getid3_quicktime
+{
+
+	function getid3_quicktime(&$fd, &$ThisFileInfo, $ReturnAtomData=true, $ParseAllPossibleAtoms=false) {
+
+		$ThisFileInfo['fileformat'] = 'quicktime';
+		$ThisFileInfo['quicktime']['hinting'] = false;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$offset      = 0;
+		$atomcounter = 0;
+
+		while ($offset < $ThisFileInfo['avdataend']) {
+			if ($offset >= pow(2, 31)) {
+				$ThisFileInfo['error'][] = 'Unable to parse atom at offset '.$offset.' because beyond 2GB limit of PHP filesystem functions';
+				break;
+			}
+			fseek($fd, $offset, SEEK_SET);
+			$AtomHeader = fread($fd, 8);
+
+			$atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
+			$atomname = substr($AtomHeader, 4, 4);
+
+			// 64-bit MOV patch by jlegateØktnc*com
+			if ($atomsize == 1) {
+				$atomsize = getid3_lib::BigEndian2Int(fread($fd, 8));
+			}
+
+			$ThisFileInfo['quicktime'][$atomname]['name']   = $atomname;
+			$ThisFileInfo['quicktime'][$atomname]['size']   = $atomsize;
+			$ThisFileInfo['quicktime'][$atomname]['offset'] = $offset;
+
+			if (($offset + $atomsize) > $ThisFileInfo['avdataend']) {
+				$ThisFileInfo['error'][] = 'Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)';
+				return false;
+			}
+
+			if ($atomsize == 0) {
+				// Furthermore, for historical reasons the list of atoms is optionally
+				// terminated by a 32-bit integer set to 0. If you are writing a program
+				// to read user data atoms, you should allow for the terminating 0.
+				break;
+			}
+			switch ($atomname) {
+				case 'mdat': // Media DATa atom
+					// 'mdat' contains the actual data for the audio/video
+					if (($atomsize > 8) && (!isset($ThisFileInfo['avdataend_tmp']) || ($ThisFileInfo['quicktime'][$atomname]['size'] > ($ThisFileInfo['avdataend_tmp'] - $ThisFileInfo['avdataoffset'])))) {
+
+						$ThisFileInfo['avdataoffset'] = $ThisFileInfo['quicktime'][$atomname]['offset'] + 8;
+						$OldAVDataEnd                 = $ThisFileInfo['avdataend'];
+						$ThisFileInfo['avdataend']    = $ThisFileInfo['quicktime'][$atomname]['offset'] + $ThisFileInfo['quicktime'][$atomname]['size'];
+
+						if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($fd, 4)))) {
+							getid3_mp3::getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'], false);
+							if (isset($ThisFileInfo['mpeg']['audio'])) {
+								$ThisFileInfo['audio']['dataformat']   = 'mp3';
+								$ThisFileInfo['audio']['codec']        = (!empty($ThisFileInfo['mpeg']['audio']['encoder']) ? $ThisFileInfo['mpeg']['audio']['encoder'] : (!empty($ThisFileInfo['mpeg']['audio']['codec']) ? $ThisFileInfo['mpeg']['audio']['codec'] : (!empty($ThisFileInfo['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
+								$ThisFileInfo['audio']['sample_rate']  = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+								$ThisFileInfo['audio']['channels']     = $ThisFileInfo['mpeg']['audio']['channels'];
+								$ThisFileInfo['audio']['bitrate']      = $ThisFileInfo['mpeg']['audio']['bitrate'];
+								$ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
+								$ThisFileInfo['bitrate']               = $ThisFileInfo['audio']['bitrate'];
+							}
+						}
+						$ThisFileInfo['avdataend'] = $OldAVDataEnd;
+						unset($OldAVDataEnd);
+
+					}
+					break;
+
+				case 'free': // FREE space atom
+				case 'skip': // SKIP atom
+				case 'wide': // 64-bit expansion placeholder atom
+					// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
+					break;
+
+				default:
+					$atomHierarchy = array();
+					$ThisFileInfo['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, fread($fd, $atomsize), $ThisFileInfo, $offset, $atomHierarchy, $ParseAllPossibleAtoms);
+					break;
+			}
+
+			$offset += $atomsize;
+			$atomcounter++;
+		}
+
+		if (!empty($ThisFileInfo['avdataend_tmp'])) {
+			// this value is assigned to a temp value and then erased because
+			// otherwise any atoms beyond the 'mdat' atom would not get parsed
+			$ThisFileInfo['avdataend'] = $ThisFileInfo['avdataend_tmp'];
+			unset($ThisFileInfo['avdataend_tmp']);
+		}
+
+		if (!isset($ThisFileInfo['bitrate']) && isset($ThisFileInfo['playtime_seconds'])) {
+			$ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+		}
+		if (isset($ThisFileInfo['bitrate']) && !isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['quicktime']['video'])) {
+			$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['bitrate'];
+		}
+		if (@$ThisFileInfo['playtime_seconds'] && !isset($ThisFileInfo['video']['frame_rate']) && !empty($ThisFileInfo['quicktime']['stts_framecount'])) {
+			foreach ($ThisFileInfo['quicktime']['stts_framecount'] as $key => $samples_count) {
+				$samples_per_second = $samples_count / $ThisFileInfo['playtime_seconds'];
+				if ($samples_per_second > 240) {
+					// has to be audio samples
+				} else {
+					$ThisFileInfo['video']['frame_rate'] = $samples_per_second;
+					break;
+				}
+			}
+		}
+		if (($ThisFileInfo['audio']['dataformat'] == 'mp4') && empty($ThisFileInfo['video']['resolution_x'])) {
+			$ThisFileInfo['fileformat'] = 'mp4';
+			$ThisFileInfo['mime_type']  = 'audio/mp4';
+			unset($ThisFileInfo['video']['dataformat']);
+		}
+
+		if (!$ReturnAtomData) {
+			unset($ThisFileInfo['quicktime']['moov']);
+		}
+
+		if (empty($ThisFileInfo['audio']['dataformat']) && !empty($ThisFileInfo['quicktime']['audio'])) {
+			$ThisFileInfo['audio']['dataformat'] = 'quicktime';
+		}
+		if (empty($ThisFileInfo['video']['dataformat']) && !empty($ThisFileInfo['quicktime']['video'])) {
+			$ThisFileInfo['video']['dataformat'] = 'quicktime';
+		}
+
+		return true;
+	}
+
+	function QuicktimeParseAtom($atomname, $atomsize, $atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
+		// http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
+
+		array_push($atomHierarchy, $atomname);
+		$atomstructure['hierarchy'] = implode(' ', $atomHierarchy);
+		$atomstructure['name']      = $atomname;
+		$atomstructure['size']      = $atomsize;
+		$atomstructure['offset']    = $baseoffset;
+
+		switch ($atomname) {
+			case 'moov': // MOVie container atom
+			case 'trak': // TRAcK container atom
+			case 'clip': // CLIPping container atom
+			case 'matt': // track MATTe container atom
+			case 'edts': // EDiTS container atom
+			case 'tref': // Track REFerence container atom
+			case 'mdia': // MeDIA container atom
+			case 'minf': // Media INFormation container atom
+			case 'dinf': // Data INFormation container atom
+			case 'udta': // User DaTA container atom
+			case 'cmov': // Compressed MOVie container atom
+			case 'rmra': // Reference Movie Record Atom
+			case 'rmda': // Reference Movie Descriptor Atom
+			case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
+				$atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($atomdata, $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
+				break;
+
+			case 'stbl': // Sample TaBLe container atom
+				$atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($atomdata, $ThisFileInfo, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
+				$isVideo = false;
+				$framerate  = 0;
+				$framecount = 0;
+				foreach ($atomstructure['subatoms'] as $key => $value_array) {
+					if (isset($value_array['sample_description_table'])) {
+						foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
+							if (isset($value_array2['data_format'])) {
+								switch ($value_array2['data_format']) {
+									case 'avc1':
+									case 'mp4v':
+										// video data
+										$isVideo = true;
+										break;
+									case 'mp4a':
+										// audio data
+										break;
+								}
+							}
+						}
+					} elseif (isset($value_array['time_to_sample_table'])) {
+						foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
+							if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration'])) {
+								$framerate  = round($ThisFileInfo['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
+								$framecount = $value_array2['sample_count'];
+							}
+						}
+					}
+				}
+				if ($isVideo && $framerate) {
+					$ThisFileInfo['quicktime']['video']['frame_rate'] = $framerate;
+					$ThisFileInfo['video']['frame_rate'] = $ThisFileInfo['quicktime']['video']['frame_rate'];
+				}
+				if ($isVideo && $framecount) {
+					$ThisFileInfo['quicktime']['video']['frame_count'] = $framecount;
+				}
+				break;
+
+
+			case '©cpy':
+			case '©day':
+			case '©dir':
+			case '©ed1':
+			case '©ed2':
+			case '©ed3':
+			case '©ed4':
+			case '©ed5':
+			case '©ed6':
+			case '©ed7':
+			case '©ed8':
+			case '©ed9':
+			case '©fmt':
+			case '©inf':
+			case '©prd':
+			case '©prf':
+			case '©req':
+			case '©src':
+			case '©wrt':
+			case '©nam':
+			case '©cmt':
+			case '©wrn':
+			case '©hst':
+			case '©mak':
+			case '©mod':
+			case '©PRD':
+			case '©swr':
+			case '©aut':
+			case '©ART':
+			case '©trk':
+			case '©alb':
+			case '©com':
+			case '©gen':
+			case '©ope':
+			case '©url':
+			case '©enc':
+				$atomstructure['data_length'] = getid3_lib::BigEndian2Int(substr($atomdata,  0, 2));
+				$atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata,  2, 2));
+				$atomstructure['data']        =                           substr($atomdata,  4);
+
+				$atomstructure['language']    = $this->QuicktimeLanguageLookup($atomstructure['language_id']);
+				if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) {
+					$ThisFileInfo['comments']['language'][] = $atomstructure['language'];
+				}
+				$this->CopyToAppropriateCommentsSection($atomname, $atomstructure['data'], $ThisFileInfo);
+				break;
+
+
+			case 'play': // auto-PLAY atom
+				$atomstructure['autoplay']             = (bool) getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+
+				$ThisFileInfo['quicktime']['autoplay'] = $atomstructure['autoplay'];
+				break;
+
+
+			case 'WLOC': // Window LOCation atom
+				$atomstructure['location_x']  = getid3_lib::BigEndian2Int(substr($atomdata,  0, 2));
+				$atomstructure['location_y']  = getid3_lib::BigEndian2Int(substr($atomdata,  2, 2));
+				break;
+
+
+			case 'LOOP': // LOOPing atom
+			case 'SelO': // play SELection Only atom
+			case 'AllF': // play ALL Frames atom
+				$atomstructure['data'] = getid3_lib::BigEndian2Int($atomdata);
+				break;
+
+
+			case 'name': //
+			case 'MCPS': // Media Cleaner PRo
+			case '@PRM': // adobe PReMiere version
+			case '@PRQ': // adobe PRemiere Quicktime version
+				$atomstructure['data'] = $atomdata;
+				break;
+
+
+			case 'cmvd': // Compressed MooV Data atom
+				// Code by ubergeekØubergeek*tv based on information from
+				// http://developer.apple.com/quicktime/icefloe/dispatch012.html
+				$atomstructure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atomdata, 0, 4));
+
+				$CompressedFileData = substr($atomdata, 4);
+				if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
+					$atomstructure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, $ThisFileInfo, 0, $atomHierarchy, $ParseAllPossibleAtoms);
+				} else {
+					$ThisFileInfo['warning'][] = 'Error decompressing compressed MOV atom at offset '.$atomstructure['offset'];
+				}
+				break;
+
+
+			case 'dcom': // Data COMpression atom
+				$atomstructure['compression_id']   = $atomdata;
+				$atomstructure['compression_text'] = $this->QuicktimeDCOMLookup($atomdata);
+				break;
+
+
+			case 'rdrf': // Reference movie Data ReFerence atom
+				$atomstructure['version']                = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3));
+				$atomstructure['flags']['internal_data'] = (bool) ($atomstructure['flags_raw'] & 0x000001);
+
+				$atomstructure['reference_type_name']    =                           substr($atomdata,  4, 4);
+				$atomstructure['reference_length']       = getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+				switch ($atomstructure['reference_type_name']) {
+					case 'url ':
+						$atomstructure['url']            =       $this->NoNullString(substr($atomdata, 12));
+						break;
+
+					case 'alis':
+						$atomstructure['file_alias']     =                           substr($atomdata, 12);
+						break;
+
+					case 'rsrc':
+						$atomstructure['resource_alias'] =                           substr($atomdata, 12);
+						break;
+
+					default:
+						$atomstructure['data']           =                           substr($atomdata, 12);
+						break;
+				}
+				break;
+
+
+			case 'rmqu': // Reference Movie QUality atom
+				$atomstructure['movie_quality'] = getid3_lib::BigEndian2Int($atomdata);
+				break;
+
+
+			case 'rmcs': // Reference Movie Cpu Speed atom
+				$atomstructure['version']          = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']        = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2));
+				break;
+
+
+			case 'rmvc': // Reference Movie Version Check atom
+				$atomstructure['version']            = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']          = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['gestalt_selector']   =                           substr($atomdata,  4, 4);
+				$atomstructure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+				$atomstructure['gestalt_value']      = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4));
+				$atomstructure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atomdata, 14, 2));
+				break;
+
+
+			case 'rmcd': // Reference Movie Component check atom
+				$atomstructure['version']                = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['component_type']         =                           substr($atomdata,  4, 4);
+				$atomstructure['component_subtype']      =                           substr($atomdata,  8, 4);
+				$atomstructure['component_manufacturer'] =                           substr($atomdata, 12, 4);
+				$atomstructure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4));
+				$atomstructure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4));
+				$atomstructure['component_min_version']  = getid3_lib::BigEndian2Int(substr($atomdata, 24, 4));
+				break;
+
+
+			case 'rmdr': // Reference Movie Data Rate atom
+				$atomstructure['version']       = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']     = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['data_rate']     = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+
+				$atomstructure['data_rate_bps'] = $atomstructure['data_rate'] * 10;
+				break;
+
+
+			case 'rmla': // Reference Movie Language Atom
+				$atomstructure['version']     = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']   = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['language_id'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2));
+
+				$atomstructure['language']    = $this->QuicktimeLanguageLookup($atomstructure['language_id']);
+				if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) {
+					$ThisFileInfo['comments']['language'][] = $atomstructure['language'];
+				}
+				break;
+
+
+			case 'rmla': // Reference Movie Language Atom
+				$atomstructure['version']   = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['track_id']  = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2));
+				break;
+
+
+			case 'ptv ': // Print To Video - defines a movie's full screen mode
+				// http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
+				$atomstructure['display_size_raw']  = getid3_lib::BigEndian2Int(substr($atomdata, 0, 2));
+				$atomstructure['reserved_1']        = getid3_lib::BigEndian2Int(substr($atomdata, 2, 2)); // hardcoded: 0x0000
+				$atomstructure['reserved_2']        = getid3_lib::BigEndian2Int(substr($atomdata, 4, 2)); // hardcoded: 0x0000
+				$atomstructure['slide_show_flag']   = getid3_lib::BigEndian2Int(substr($atomdata, 6, 1));
+				$atomstructure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atomdata, 7, 1));
+
+				$atomstructure['flags']['play_on_open'] = (bool) $atomstructure['play_on_open_flag'];
+				$atomstructure['flags']['slide_show']   = (bool) $atomstructure['slide_show_flag'];
+
+				$ptv_lookup[0] = 'normal';
+				$ptv_lookup[1] = 'double';
+				$ptv_lookup[2] = 'half';
+				$ptv_lookup[3] = 'full';
+				$ptv_lookup[4] = 'current';
+				if (isset($ptv_lookup[$atomstructure['display_size_raw']])) {
+					$atomstructure['display_size'] = $ptv_lookup[$atomstructure['display_size_raw']];
+				} else {
+					$ThisFileInfo['warning'][] = 'unknown "ptv " display constant ('.$atomstructure['display_size_raw'].')';
+				}
+				break;
+
+
+			case 'stsd': // Sample Table Sample Description atom
+				$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$stsdEntriesDataOffset = 8;
+				for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+					$atomstructure['sample_description_table'][$i]['size']             = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 4));
+					$stsdEntriesDataOffset += 4;
+					$atomstructure['sample_description_table'][$i]['data_format']      =                           substr($atomdata, $stsdEntriesDataOffset, 4);
+					$stsdEntriesDataOffset += 4;
+					$atomstructure['sample_description_table'][$i]['reserved']         = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 6));
+					$stsdEntriesDataOffset += 6;
+					$atomstructure['sample_description_table'][$i]['reference_index']  = getid3_lib::BigEndian2Int(substr($atomdata, $stsdEntriesDataOffset, 2));
+					$stsdEntriesDataOffset += 2;
+					$atomstructure['sample_description_table'][$i]['data']             =                           substr($atomdata, $stsdEntriesDataOffset, ($atomstructure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
+					$stsdEntriesDataOffset += ($atomstructure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
+
+					$atomstructure['sample_description_table'][$i]['encoder_version']  = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'],  0, 2));
+					$atomstructure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'],  2, 2));
+					$atomstructure['sample_description_table'][$i]['encoder_vendor']   =                           substr($atomstructure['sample_description_table'][$i]['data'],  4, 4);
+
+					switch ($atomstructure['sample_description_table'][$i]['encoder_vendor']) {
+
+						case "\x00\x00\x00\x00":
+							// audio atom
+							$atomstructure['sample_description_table'][$i]['audio_channels']       =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'],  8,  2));
+							$atomstructure['sample_description_table'][$i]['audio_bit_depth']      =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 10,  2));
+							$atomstructure['sample_description_table'][$i]['audio_compression_id'] =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 12,  2));
+							$atomstructure['sample_description_table'][$i]['audio_packet_size']    =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 14,  2));
+							$atomstructure['sample_description_table'][$i]['audio_sample_rate']    = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 16,  4));
+
+							switch ($atomstructure['sample_description_table'][$i]['data_format']) {
+								case 'avc1':
+								case 'mp4v':
+									$ThisFileInfo['fileformat'] = 'mp4';
+									$ThisFileInfo['video']['fourcc'] = $atomstructure['sample_description_table'][$i]['data_format'];
+									$ThisFileInfo['warning'][] = 'This version ('.GETID3_VERSION.') of getID3() does not fully support MPEG-4 audio/video streams';
+									break;
+
+								case 'qtvr':
+									$ThisFileInfo['video']['dataformat'] = 'quicktimevr';
+									break;
+
+								case 'mp4a':
+								default:
+									$ThisFileInfo['quicktime']['audio']['codec']       = $this->QuicktimeAudioCodecLookup($atomstructure['sample_description_table'][$i]['data_format']);
+									$ThisFileInfo['quicktime']['audio']['sample_rate'] = $atomstructure['sample_description_table'][$i]['audio_sample_rate'];
+									$ThisFileInfo['quicktime']['audio']['channels']    = $atomstructure['sample_description_table'][$i]['audio_channels'];
+									$ThisFileInfo['quicktime']['audio']['bit_depth']   = $atomstructure['sample_description_table'][$i]['audio_bit_depth'];
+									$ThisFileInfo['audio']['codec']                    = $ThisFileInfo['quicktime']['audio']['codec'];
+									$ThisFileInfo['audio']['sample_rate']              = $ThisFileInfo['quicktime']['audio']['sample_rate'];
+									$ThisFileInfo['audio']['channels']                 = $ThisFileInfo['quicktime']['audio']['channels'];
+									$ThisFileInfo['audio']['bits_per_sample']          = $ThisFileInfo['quicktime']['audio']['bit_depth'];
+									switch ($atomstructure['sample_description_table'][$i]['data_format']) {
+										case 'raw ': // PCM
+										case 'alac': // Apple Lossless Audio Codec
+											$ThisFileInfo['audio']['lossless'] = true;
+											break;
+										default:
+											$ThisFileInfo['audio']['lossless'] = false;
+											break;
+									}
+									break;
+							}
+							break;
+
+						default:
+							switch ($atomstructure['sample_description_table'][$i]['data_format']) {
+								case 'mp4s':
+									$ThisFileInfo['fileformat'] = 'mp4';
+									break;
+
+								default:
+									// video atom
+									$atomstructure['sample_description_table'][$i]['video_temporal_quality']  =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'],  8,  4));
+									$atomstructure['sample_description_table'][$i]['video_spatial_quality']   =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 12,  4));
+									$atomstructure['sample_description_table'][$i]['video_frame_width']       =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 16,  2));
+									$atomstructure['sample_description_table'][$i]['video_frame_height']      =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 18,  2));
+									$atomstructure['sample_description_table'][$i]['video_resolution_x']      = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 20,  4));
+									$atomstructure['sample_description_table'][$i]['video_resolution_y']      = getid3_lib::FixedPoint16_16(substr($atomstructure['sample_description_table'][$i]['data'], 24,  4));
+									$atomstructure['sample_description_table'][$i]['video_data_size']         =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 28,  4));
+									$atomstructure['sample_description_table'][$i]['video_frame_count']       =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 32,  2));
+									$atomstructure['sample_description_table'][$i]['video_encoder_name_len']  =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 34,  1));
+									$atomstructure['sample_description_table'][$i]['video_encoder_name']      =                             substr($atomstructure['sample_description_table'][$i]['data'], 35, $atomstructure['sample_description_table'][$i]['video_encoder_name_len']);
+									$atomstructure['sample_description_table'][$i]['video_pixel_color_depth'] =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 66,  2));
+									$atomstructure['sample_description_table'][$i]['video_color_table_id']    =   getid3_lib::BigEndian2Int(substr($atomstructure['sample_description_table'][$i]['data'], 68,  2));
+
+									$atomstructure['sample_description_table'][$i]['video_pixel_color_type']  = (($atomstructure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
+									$atomstructure['sample_description_table'][$i]['video_pixel_color_name']  = $this->QuicktimeColorNameLookup($atomstructure['sample_description_table'][$i]['video_pixel_color_depth']);
+
+									if ($atomstructure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
+										$ThisFileInfo['quicktime']['video']['codec_fourcc']        = $atomstructure['sample_description_table'][$i]['data_format'];
+										$ThisFileInfo['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atomstructure['sample_description_table'][$i]['data_format']);
+										$ThisFileInfo['quicktime']['video']['codec']               = $atomstructure['sample_description_table'][$i]['video_encoder_name'];
+										$ThisFileInfo['quicktime']['video']['color_depth']         = $atomstructure['sample_description_table'][$i]['video_pixel_color_depth'];
+										$ThisFileInfo['quicktime']['video']['color_depth_name']    = $atomstructure['sample_description_table'][$i]['video_pixel_color_name'];
+
+										$ThisFileInfo['video']['codec']           = $ThisFileInfo['quicktime']['video']['codec'];
+										$ThisFileInfo['video']['bits_per_sample'] = $ThisFileInfo['quicktime']['video']['color_depth'];
+									}
+									$ThisFileInfo['video']['lossless']           = false;
+									$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+									break;
+							}
+							break;
+					}
+					switch (strtolower($atomstructure['sample_description_table'][$i]['data_format'])) {
+						case 'mp4a':
+							$ThisFileInfo['audio']['dataformat']         = 'mp4';
+							$ThisFileInfo['quicktime']['audio']['codec'] = 'mp4';
+							break;
+
+						case '3ivx':
+						case '3iv1':
+						case '3iv2':
+							$ThisFileInfo['video']['dataformat'] = '3ivx';
+							break;
+
+						case 'xvid':
+							$ThisFileInfo['video']['dataformat'] = 'xvid';
+							break;
+
+						case 'mp4v':
+							$ThisFileInfo['video']['dataformat'] = 'mpeg4';
+							break;
+
+						case 'divx':
+						case 'div1':
+						case 'div2':
+						case 'div3':
+						case 'div4':
+						case 'div5':
+						case 'div6':
+							$TDIVXileInfo['video']['dataformat'] = 'divx';
+							break;
+
+						default:
+							// do nothing
+							break;
+					}
+					unset($atomstructure['sample_description_table'][$i]['data']);
+				}
+				break;
+
+
+			case 'stts': // Sample Table Time-to-Sample atom
+				$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$sttsEntriesDataOffset = 8;
+				//$FrameRateCalculatorArray = array();
+				$frames_count = 0;
+				for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+					$atomstructure['time_to_sample_table'][$i]['sample_count']    = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4));
+					$sttsEntriesDataOffset += 4;
+					$atomstructure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atomdata, $sttsEntriesDataOffset, 4));
+					$sttsEntriesDataOffset += 4;
+
+					$frames_count += $atomstructure['time_to_sample_table'][$i]['sample_count'];
+
+					// THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
+					//if (!empty($ThisFileInfo['quicktime']['time_scale']) && (@$atomstructure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
+					//	$stts_new_framerate = $ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration'];
+					//	if ($stts_new_framerate <= 60) {
+					//		// some atoms have durations of "1" giving a very large framerate, which probably is not right
+					//		$ThisFileInfo['video']['frame_rate'] = max(@$ThisFileInfo['video']['frame_rate'], $stts_new_framerate);
+					//	}
+					//}
+                    //
+					//@$FrameRateCalculatorArray[($ThisFileInfo['quicktime']['time_scale'] / $atomstructure['time_to_sample_table'][$i]['sample_duration'])] += $atomstructure['time_to_sample_table'][$i]['sample_count'];
+				}
+				$ThisFileInfo['quicktime']['stts_framecount'][] = $frames_count;
+				//$sttsFramesTotal  = 0;
+				//$sttsSecondsTotal = 0;
+				//foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
+				//	if (($frames_per_second > 60) || ($frames_per_second < 1)) {
+				//		// not video FPS information, probably audio information
+				//		$sttsFramesTotal  = 0;
+				//		$sttsSecondsTotal = 0;
+				//		break;
+				//	}
+				//	$sttsFramesTotal  += $frame_count;
+				//	$sttsSecondsTotal += $frame_count / $frames_per_second;
+				//}
+				//if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
+				//	if (($sttsFramesTotal / $sttsSecondsTotal) > @$ThisFileInfo['video']['frame_rate']) {
+				//		$ThisFileInfo['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
+				//	}
+				//}
+				break;
+
+
+			case 'stss': // Sample Table Sync Sample (key frames) atom
+				if ($ParseAllPossibleAtoms) {
+					$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+					$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+					$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+					$stssEntriesDataOffset = 8;
+					for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+						$atomstructure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stssEntriesDataOffset, 4));
+						$stssEntriesDataOffset += 4;
+					}
+				}
+				break;
+
+
+			case 'stsc': // Sample Table Sample-to-Chunk atom
+				if ($ParseAllPossibleAtoms) {
+					$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+					$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+					$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+					$stscEntriesDataOffset = 8;
+					for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+						$atomstructure['sample_to_chunk_table'][$i]['first_chunk']        = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4));
+						$stscEntriesDataOffset += 4;
+						$atomstructure['sample_to_chunk_table'][$i]['samples_per_chunk']  = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4));
+						$stscEntriesDataOffset += 4;
+						$atomstructure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atomdata, $stscEntriesDataOffset, 4));
+						$stscEntriesDataOffset += 4;
+					}
+				}
+				break;
+
+
+			case 'stsz': // Sample Table SiZe atom
+				if ($ParseAllPossibleAtoms) {
+					$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+					$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+					$atomstructure['sample_size']    = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+					$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+					$stszEntriesDataOffset = 12;
+					if ($atomstructure['sample_size'] == 0) {
+						for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+							$atomstructure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stszEntriesDataOffset, 4));
+							$stszEntriesDataOffset += 4;
+						}
+					}
+				}
+				break;
+
+
+			case 'stco': // Sample Table Chunk Offset atom
+				if ($ParseAllPossibleAtoms) {
+					$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+					$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+					$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+					$stcoEntriesDataOffset = 8;
+					for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+						$atomstructure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stcoEntriesDataOffset, 4));
+						$stcoEntriesDataOffset += 4;
+					}
+				}
+				break;
+
+
+			case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
+				if ($ParseAllPossibleAtoms) {
+					$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+					$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+					$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+					$stcoEntriesDataOffset = 8;
+					for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+						$atomstructure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $stcoEntriesDataOffset, 8));
+						$stcoEntriesDataOffset += 8;
+					}
+				}
+				break;
+
+
+			case 'dref': // Data REFerence atom
+				$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$drefDataOffset = 8;
+				for ($i = 0; $i < $atomstructure['number_entries']; $i++) {
+					$atomstructure['data_references'][$i]['size']                    = getid3_lib::BigEndian2Int(substr($atomdata, $drefDataOffset, 4));
+					$drefDataOffset += 4;
+					$atomstructure['data_references'][$i]['type']                    =               substr($atomdata, $drefDataOffset, 4);
+					$drefDataOffset += 4;
+					$atomstructure['data_references'][$i]['version']                 = getid3_lib::BigEndian2Int(substr($atomdata,  $drefDataOffset, 1));
+					$drefDataOffset += 1;
+					$atomstructure['data_references'][$i]['flags_raw']               = getid3_lib::BigEndian2Int(substr($atomdata,  $drefDataOffset, 3)); // hardcoded: 0x0000
+					$drefDataOffset += 3;
+					$atomstructure['data_references'][$i]['data']                    =               substr($atomdata, $drefDataOffset, ($atomstructure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
+					$drefDataOffset += ($atomstructure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
+
+					$atomstructure['data_references'][$i]['flags']['self_reference'] = (bool) ($atomstructure['data_references'][$i]['flags_raw'] & 0x001);
+				}
+				break;
+
+
+			case 'gmin': // base Media INformation atom
+				$atomstructure['version']                = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2));
+				$atomstructure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atomdata,  6, 2));
+				$atomstructure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atomdata,  8, 2));
+				$atomstructure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2));
+				$atomstructure['balance']                = getid3_lib::BigEndian2Int(substr($atomdata, 12, 2));
+				$atomstructure['reserved']               = getid3_lib::BigEndian2Int(substr($atomdata, 14, 2));
+				break;
+
+
+			case 'smhd': // Sound Media information HeaDer atom
+				$atomstructure['version']                = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['balance']                = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2));
+				$atomstructure['reserved']               = getid3_lib::BigEndian2Int(substr($atomdata,  6, 2));
+				break;
+
+
+			case 'vmhd': // Video Media information HeaDer atom
+				$atomstructure['version']                = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3));
+				$atomstructure['graphics_mode']          = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2));
+				$atomstructure['opcolor_red']            = getid3_lib::BigEndian2Int(substr($atomdata,  6, 2));
+				$atomstructure['opcolor_green']          = getid3_lib::BigEndian2Int(substr($atomdata,  8, 2));
+				$atomstructure['opcolor_blue']           = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2));
+
+				$atomstructure['flags']['no_lean_ahead'] = (bool) ($atomstructure['flags_raw'] & 0x001);
+				break;
+
+
+			case 'hdlr': // HanDLeR reference atom
+				$atomstructure['version']                = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']              = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['component_type']         =                           substr($atomdata,  4, 4);
+				$atomstructure['component_subtype']      =                           substr($atomdata,  8, 4);
+				$atomstructure['component_manufacturer'] =                           substr($atomdata, 12, 4);
+				$atomstructure['component_flags_raw']    = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4));
+				$atomstructure['component_flags_mask']   = getid3_lib::BigEndian2Int(substr($atomdata, 20, 4));
+				$atomstructure['component_name']         =      $this->Pascal2String(substr($atomdata, 24));
+
+				if (($atomstructure['component_subtype'] == 'STpn') && ($atomstructure['component_manufacturer'] == 'zzzz')) {
+					$ThisFileInfo['video']['dataformat'] = 'quicktimevr';
+				}
+				break;
+
+
+			case 'mdhd': // MeDia HeaDer atom
+				$atomstructure['version']               = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']             = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['creation_time']         = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$atomstructure['modify_time']           = getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+				$atomstructure['time_scale']            = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4));
+				$atomstructure['duration']              = getid3_lib::BigEndian2Int(substr($atomdata, 16, 4));
+				$atomstructure['language_id']           = getid3_lib::BigEndian2Int(substr($atomdata, 20, 2));
+				$atomstructure['quality']               = getid3_lib::BigEndian2Int(substr($atomdata, 22, 2));
+
+				if ($atomstructure['time_scale'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt Quicktime file: mdhd.time_scale == zero';
+					return false;
+				}
+                $ThisFileInfo['quicktime']['time_scale'] = max(@$ThisFileInfo['quicktime']['time_scale'], $atomstructure['time_scale']);
+
+				$atomstructure['creation_time_unix']    = getid3_lib::DateMac2Unix($atomstructure['creation_time']);
+				$atomstructure['modify_time_unix']      = getid3_lib::DateMac2Unix($atomstructure['modify_time']);
+				$atomstructure['playtime_seconds']      = $atomstructure['duration'] / $atomstructure['time_scale'];
+				$atomstructure['language']              = $this->QuicktimeLanguageLookup($atomstructure['language_id']);
+				if (empty($ThisFileInfo['comments']['language']) || (!in_array($atomstructure['language'], $ThisFileInfo['comments']['language']))) {
+					$ThisFileInfo['comments']['language'][] = $atomstructure['language'];
+				}
+				break;
+
+
+			case 'pnot': // Preview atom
+				$atomstructure['modification_date']      = getid3_lib::BigEndian2Int(substr($atomdata,  0, 4)); // "standard Macintosh format"
+				$atomstructure['version_number']         = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2)); // hardcoded: 0x00
+				$atomstructure['atom_type']              =               substr($atomdata,  6, 4);        // usually: 'PICT'
+				$atomstructure['atom_index']             = getid3_lib::BigEndian2Int(substr($atomdata, 10, 2)); // usually: 0x01
+
+				$atomstructure['modification_date_unix'] = getid3_lib::DateMac2Unix($atomstructure['modification_date']);
+				break;
+
+
+			case 'crgn': // Clipping ReGioN atom
+				$atomstructure['region_size']   = getid3_lib::BigEndian2Int(substr($atomdata,  0, 2)); // The Region size, Region boundary box,
+				$atomstructure['boundary_box']  = getid3_lib::BigEndian2Int(substr($atomdata,  2, 8)); // and Clipping region data fields
+				$atomstructure['clipping_data'] =               substr($atomdata, 10);           // constitute a QuickDraw region.
+				break;
+
+
+			case 'load': // track LOAD settings atom
+				$atomstructure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atomdata,  0, 4));
+				$atomstructure['preload_duration']   = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$atomstructure['preload_flags_raw']  = getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+				$atomstructure['default_hints_raw']  = getid3_lib::BigEndian2Int(substr($atomdata, 12, 4));
+
+				$atomstructure['default_hints']['double_buffer'] = (bool) ($atomstructure['default_hints_raw'] & 0x0020);
+				$atomstructure['default_hints']['high_quality']  = (bool) ($atomstructure['default_hints_raw'] & 0x0100);
+				break;
+
+
+			case 'tmcd': // TiMe CoDe atom
+			case 'chap': // CHAPter list atom
+			case 'sync': // SYNChronization atom
+			case 'scpt': // tranSCriPT atom
+			case 'ssrc': // non-primary SouRCe atom
+				for ($i = 0; $i < (strlen($atomdata) % 4); $i++) {
+					$atomstructure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atomdata, $i * 4, 4));
+				}
+				break;
+
+
+			case 'elst': // Edit LiST atom
+				$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['number_entries'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				for ($i = 0; $i < $atomstructure['number_entries']; $i++ ) {
+					$atomstructure['edit_list'][$i]['track_duration'] =   getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($i * 12) + 0, 4));
+					$atomstructure['edit_list'][$i]['media_time']     =   getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($i * 12) + 4, 4));
+					$atomstructure['edit_list'][$i]['media_rate']     = getid3_lib::FixedPoint16_16(substr($atomdata, 8 + ($i * 12) + 8, 4));
+				}
+				break;
+
+
+			case 'kmat': // compressed MATte atom
+				$atomstructure['version']        = getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']      = getid3_lib::BigEndian2Int(substr($atomdata,  1, 3)); // hardcoded: 0x0000
+				$atomstructure['matte_data_raw'] =               substr($atomdata,  4);
+				break;
+
+
+			case 'ctab': // Color TABle atom
+				$atomstructure['color_table_seed']   = getid3_lib::BigEndian2Int(substr($atomdata,  0, 4)); // hardcoded: 0x00000000
+				$atomstructure['color_table_flags']  = getid3_lib::BigEndian2Int(substr($atomdata,  4, 2)); // hardcoded: 0x8000
+				$atomstructure['color_table_size']   = getid3_lib::BigEndian2Int(substr($atomdata,  6, 2)) + 1;
+				for ($colortableentry = 0; $colortableentry < $atomstructure['color_table_size']; $colortableentry++) {
+					$atomstructure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 0, 2));
+					$atomstructure['color_table'][$colortableentry]['red']   = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 2, 2));
+					$atomstructure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 4, 2));
+					$atomstructure['color_table'][$colortableentry]['blue']  = getid3_lib::BigEndian2Int(substr($atomdata, 8 + ($colortableentry * 8) + 6, 2));
+				}
+				break;
+
+
+			case 'mvhd': // MoVie HeaDer atom
+				$atomstructure['version']            =   getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']          =   getid3_lib::BigEndian2Int(substr($atomdata,  1, 3));
+				$atomstructure['creation_time']      =   getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$atomstructure['modify_time']        =   getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+				$atomstructure['time_scale']         =   getid3_lib::BigEndian2Int(substr($atomdata, 12, 4));
+				$atomstructure['duration']           =   getid3_lib::BigEndian2Int(substr($atomdata, 16, 4));
+				$atomstructure['preferred_rate']     = getid3_lib::FixedPoint16_16(substr($atomdata, 20, 4));
+				$atomstructure['preferred_volume']   =   getid3_lib::FixedPoint8_8(substr($atomdata, 24, 2));
+				$atomstructure['reserved']           =                             substr($atomdata, 26, 10);
+				$atomstructure['matrix_a']           = getid3_lib::FixedPoint16_16(substr($atomdata, 36, 4));
+				$atomstructure['matrix_b']           = getid3_lib::FixedPoint16_16(substr($atomdata, 40, 4));
+				$atomstructure['matrix_u']           =  getid3_lib::FixedPoint2_30(substr($atomdata, 44, 4));
+				$atomstructure['matrix_c']           = getid3_lib::FixedPoint16_16(substr($atomdata, 48, 4));
+				$atomstructure['matrix_d']           = getid3_lib::FixedPoint16_16(substr($atomdata, 52, 4));
+				$atomstructure['matrix_v']           =  getid3_lib::FixedPoint2_30(substr($atomdata, 56, 4));
+				$atomstructure['matrix_x']           = getid3_lib::FixedPoint16_16(substr($atomdata, 60, 4));
+				$atomstructure['matrix_y']           = getid3_lib::FixedPoint16_16(substr($atomdata, 64, 4));
+				$atomstructure['matrix_w']           =  getid3_lib::FixedPoint2_30(substr($atomdata, 68, 4));
+				$atomstructure['preview_time']       =   getid3_lib::BigEndian2Int(substr($atomdata, 72, 4));
+				$atomstructure['preview_duration']   =   getid3_lib::BigEndian2Int(substr($atomdata, 76, 4));
+				$atomstructure['poster_time']        =   getid3_lib::BigEndian2Int(substr($atomdata, 80, 4));
+				$atomstructure['selection_time']     =   getid3_lib::BigEndian2Int(substr($atomdata, 84, 4));
+				$atomstructure['selection_duration'] =   getid3_lib::BigEndian2Int(substr($atomdata, 88, 4));
+				$atomstructure['current_time']       =   getid3_lib::BigEndian2Int(substr($atomdata, 92, 4));
+				$atomstructure['next_track_id']      =   getid3_lib::BigEndian2Int(substr($atomdata, 96, 4));
+
+				if ($atomstructure['time_scale'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt Quicktime file: mvhd.time_scale == zero';
+					return false;
+				}
+				$atomstructure['creation_time_unix']        = getid3_lib::DateMac2Unix($atomstructure['creation_time']);
+				$atomstructure['modify_time_unix']          = getid3_lib::DateMac2Unix($atomstructure['modify_time']);
+				$ThisFileInfo['quicktime']['time_scale'] = max(@$ThisFileInfo['quicktime']['time_scale'], $atomstructure['time_scale']);
+				$ThisFileInfo['quicktime']['display_scale'] = $atomstructure['matrix_a'];
+				$ThisFileInfo['playtime_seconds']           = $atomstructure['duration'] / $atomstructure['time_scale'];
+				break;
+
+
+			case 'tkhd': // TracK HeaDer atom
+				$atomstructure['version']             =   getid3_lib::BigEndian2Int(substr($atomdata,  0, 1));
+				$atomstructure['flags_raw']           =   getid3_lib::BigEndian2Int(substr($atomdata,  1, 3));
+				$atomstructure['creation_time']       =   getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$atomstructure['modify_time']         =   getid3_lib::BigEndian2Int(substr($atomdata,  8, 4));
+				$atomstructure['trackid']             =   getid3_lib::BigEndian2Int(substr($atomdata, 12, 4));
+				$atomstructure['reserved1']           =   getid3_lib::BigEndian2Int(substr($atomdata, 16, 4));
+				$atomstructure['duration']            =   getid3_lib::BigEndian2Int(substr($atomdata, 20, 4));
+				$atomstructure['reserved2']           =   getid3_lib::BigEndian2Int(substr($atomdata, 24, 8));
+				$atomstructure['layer']               =   getid3_lib::BigEndian2Int(substr($atomdata, 32, 2));
+				$atomstructure['alternate_group']     =   getid3_lib::BigEndian2Int(substr($atomdata, 34, 2));
+				$atomstructure['volume']              =   getid3_lib::FixedPoint8_8(substr($atomdata, 36, 2));
+				$atomstructure['reserved3']           =   getid3_lib::BigEndian2Int(substr($atomdata, 38, 2));
+				$atomstructure['matrix_a']            = getid3_lib::FixedPoint16_16(substr($atomdata, 40, 4));
+				$atomstructure['matrix_b']            = getid3_lib::FixedPoint16_16(substr($atomdata, 44, 4));
+				$atomstructure['matrix_u']            = getid3_lib::FixedPoint16_16(substr($atomdata, 48, 4));
+				$atomstructure['matrix_c']            = getid3_lib::FixedPoint16_16(substr($atomdata, 52, 4));
+				$atomstructure['matrix_d']            = getid3_lib::FixedPoint16_16(substr($atomdata, 56, 4));
+				$atomstructure['matrix_v']            = getid3_lib::FixedPoint16_16(substr($atomdata, 60, 4));
+				$atomstructure['matrix_x']            =  getid3_lib::FixedPoint2_30(substr($atomdata, 64, 4));
+				$atomstructure['matrix_y']            =  getid3_lib::FixedPoint2_30(substr($atomdata, 68, 4));
+				$atomstructure['matrix_w']            =  getid3_lib::FixedPoint2_30(substr($atomdata, 72, 4));
+				$atomstructure['width']               = getid3_lib::FixedPoint16_16(substr($atomdata, 76, 4));
+				$atomstructure['height']              = getid3_lib::FixedPoint16_16(substr($atomdata, 80, 4));
+
+				$atomstructure['flags']['enabled']    = (bool) ($atomstructure['flags_raw'] & 0x0001);
+				$atomstructure['flags']['in_movie']   = (bool) ($atomstructure['flags_raw'] & 0x0002);
+				$atomstructure['flags']['in_preview'] = (bool) ($atomstructure['flags_raw'] & 0x0004);
+				$atomstructure['flags']['in_poster']  = (bool) ($atomstructure['flags_raw'] & 0x0008);
+				$atomstructure['creation_time_unix']  = getid3_lib::DateMac2Unix($atomstructure['creation_time']);
+				$atomstructure['modify_time_unix']    = getid3_lib::DateMac2Unix($atomstructure['modify_time']);
+
+				if (!isset($ThisFileInfo['video']['resolution_x']) || !isset($ThisFileInfo['video']['resolution_y'])) {
+					$ThisFileInfo['video']['resolution_x'] = $atomstructure['width'];
+					$ThisFileInfo['video']['resolution_y'] = $atomstructure['height'];
+				}
+				if ($atomstructure['flags']['enabled'] == 1) {
+					$ThisFileInfo['video']['resolution_x'] = max($ThisFileInfo['video']['resolution_x'], $atomstructure['width']);
+					$ThisFileInfo['video']['resolution_y'] = max($ThisFileInfo['video']['resolution_y'], $atomstructure['height']);
+				}
+				if (!empty($ThisFileInfo['video']['resolution_x']) && !empty($ThisFileInfo['video']['resolution_y'])) {
+					$ThisFileInfo['quicktime']['video']['resolution_x'] = $ThisFileInfo['video']['resolution_x'];
+					$ThisFileInfo['quicktime']['video']['resolution_y'] = $ThisFileInfo['video']['resolution_y'];
+				} else {
+					unset($ThisFileInfo['video']['resolution_x']);
+					unset($ThisFileInfo['video']['resolution_y']);
+					unset($ThisFileInfo['quicktime']['video']);
+				}
+				break;
+
+
+			case 'meta': // METAdata atom
+				// http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
+				$NextTagPosition = strpos($atomdata, '©');
+				while ($NextTagPosition < strlen($atomdata)) {
+					$metaItemSize = getid3_lib::BigEndian2Int(substr($atomdata, $NextTagPosition - 4, 4)) - 4;
+					if ($metaItemSize == -4) {
+					    break;
+					}
+					$metaItemRaw  = substr($atomdata, $NextTagPosition, $metaItemSize);
+					$metaItemKey  = substr($metaItemRaw, 0, 4);
+					$metaItemData = substr($metaItemRaw, 20);
+					$NextTagPosition += $metaItemSize + 4;
+
+					$this->CopyToAppropriateCommentsSection($metaItemKey, $metaItemData, $ThisFileInfo);
+				}
+				break;
+
+			case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
+				$atomstructure['signature'] =                           substr($atomdata,  0, 4);
+				$atomstructure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atomdata,  4, 4));
+				$atomstructure['fourcc']    =                           substr($atomdata,  8, 4);
+				break;
+
+			case 'mdat': // Media DATa atom
+			case 'free': // FREE space atom
+			case 'skip': // SKIP atom
+			case 'wide': // 64-bit expansion placeholder atom
+				// 'mdat' data is too big to deal with, contains no useful metadata
+				// 'free', 'skip' and 'wide' are just padding, contains no useful data at all
+
+				// When writing QuickTime files, it is sometimes necessary to update an atom's size.
+				// It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
+				// is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
+				// puts an 8-byte placeholder atom before any atoms it may have to update the size of.
+				// In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
+				// placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
+				// The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
+				break;
+
+
+			case 'nsav': // NoSAVe atom
+				// http://developer.apple.com/technotes/tn/tn2038.html
+				$atomstructure['data'] = getid3_lib::BigEndian2Int(substr($atomdata,  0, 4));
+				break;
+
+			case 'ctyp': // Controller TYPe atom (seen on QTVR)
+				// http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
+				// some controller names are:
+				//   0x00 + 'std' for linear movie
+				//   'none' for no controls
+				$atomstructure['ctyp'] = substr($atomdata, 0, 4);
+				switch ($atomstructure['ctyp']) {
+					case 'qtvr':
+						$ThisFileInfo['video']['dataformat'] = 'quicktimevr';
+						break;
+				}
+				break;
+
+			case 'pano': // PANOrama track (seen on QTVR)
+				$atomstructure['pano'] = getid3_lib::BigEndian2Int(substr($atomdata,  0, 4));
+				break;
+
+			case 'hint': // HINT track
+			case 'hinf': //
+			case 'hinv': //
+			case 'hnti': //
+				$ThisFileInfo['quicktime']['hinting'] = true;
+				break;
+
+			case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
+				for ($i = 0; $i < ($atomstructure['size'] - 8); $i += 4) {
+					$atomstructure['imgt'][] = getid3_lib::BigEndian2Int(substr($atomdata, $i, 4));
+				}
+				break;
+
+			case 'FXTC': // Something to do with Adobe After Effects (?)
+			case 'PrmA':
+			case 'code':
+			case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
+				// Observed-but-not-handled atom types are just listed here
+				// to prevent warnings being generated
+				$atomstructure['data'] = $atomdata;
+				break;
+
+			default:
+				$ThisFileInfo['warning'][] = 'Unknown QuickTime atom type: "'.$atomname.'" at offset '.$baseoffset;
+				$atomstructure['data'] = $atomdata;
+				break;
+		}
+		array_pop($atomHierarchy);
+		return $atomstructure;
+	}
+
+	function QuicktimeParseContainerAtom($atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
+		$atomstructure  = false;
+		$subatomoffset  = 0;
+		$subatomcounter = 0;
+		if ((strlen($atomdata) == 4) && (getid3_lib::BigEndian2Int($atomdata) == 0x00000000)) {
+			return false;
+		}
+		while ($subatomoffset < strlen($atomdata)) {
+			$subatomsize = getid3_lib::BigEndian2Int(substr($atomdata, $subatomoffset + 0, 4));
+			$subatomname =               substr($atomdata, $subatomoffset + 4, 4);
+			$subatomdata =               substr($atomdata, $subatomoffset + 8, $subatomsize - 8);
+			if ($subatomsize == 0) {
+				// Furthermore, for historical reasons the list of atoms is optionally
+				// terminated by a 32-bit integer set to 0. If you are writing a program
+				// to read user data atoms, you should allow for the terminating 0.
+				return $atomstructure;
+			}
+
+			$atomstructure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $ThisFileInfo, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
+
+			$subatomoffset += $subatomsize;
+			$subatomcounter++;
+		}
+		return $atomstructure;
+	}
+
+
+	function QuicktimeLanguageLookup($languageid) {
+		static $QuicktimeLanguageLookup = array();
+		if (empty($QuicktimeLanguageLookup)) {
+			$QuicktimeLanguageLookup[0]   = 'English';
+			$QuicktimeLanguageLookup[1]   = 'French';
+			$QuicktimeLanguageLookup[2]   = 'German';
+			$QuicktimeLanguageLookup[3]   = 'Italian';
+			$QuicktimeLanguageLookup[4]   = 'Dutch';
+			$QuicktimeLanguageLookup[5]   = 'Swedish';
+			$QuicktimeLanguageLookup[6]   = 'Spanish';
+			$QuicktimeLanguageLookup[7]   = 'Danish';
+			$QuicktimeLanguageLookup[8]   = 'Portuguese';
+			$QuicktimeLanguageLookup[9]   = 'Norwegian';
+			$QuicktimeLanguageLookup[10]  = 'Hebrew';
+			$QuicktimeLanguageLookup[11]  = 'Japanese';
+			$QuicktimeLanguageLookup[12]  = 'Arabic';
+			$QuicktimeLanguageLookup[13]  = 'Finnish';
+			$QuicktimeLanguageLookup[14]  = 'Greek';
+			$QuicktimeLanguageLookup[15]  = 'Icelandic';
+			$QuicktimeLanguageLookup[16]  = 'Maltese';
+			$QuicktimeLanguageLookup[17]  = 'Turkish';
+			$QuicktimeLanguageLookup[18]  = 'Croatian';
+			$QuicktimeLanguageLookup[19]  = 'Chinese (Traditional)';
+			$QuicktimeLanguageLookup[20]  = 'Urdu';
+			$QuicktimeLanguageLookup[21]  = 'Hindi';
+			$QuicktimeLanguageLookup[22]  = 'Thai';
+			$QuicktimeLanguageLookup[23]  = 'Korean';
+			$QuicktimeLanguageLookup[24]  = 'Lithuanian';
+			$QuicktimeLanguageLookup[25]  = 'Polish';
+			$QuicktimeLanguageLookup[26]  = 'Hungarian';
+			$QuicktimeLanguageLookup[27]  = 'Estonian';
+			$QuicktimeLanguageLookup[28]  = 'Lettish';
+			$QuicktimeLanguageLookup[28]  = 'Latvian';
+			$QuicktimeLanguageLookup[29]  = 'Saamisk';
+			$QuicktimeLanguageLookup[29]  = 'Lappish';
+			$QuicktimeLanguageLookup[30]  = 'Faeroese';
+			$QuicktimeLanguageLookup[31]  = 'Farsi';
+			$QuicktimeLanguageLookup[31]  = 'Persian';
+			$QuicktimeLanguageLookup[32]  = 'Russian';
+			$QuicktimeLanguageLookup[33]  = 'Chinese (Simplified)';
+			$QuicktimeLanguageLookup[34]  = 'Flemish';
+			$QuicktimeLanguageLookup[35]  = 'Irish';
+			$QuicktimeLanguageLookup[36]  = 'Albanian';
+			$QuicktimeLanguageLookup[37]  = 'Romanian';
+			$QuicktimeLanguageLookup[38]  = 'Czech';
+			$QuicktimeLanguageLookup[39]  = 'Slovak';
+			$QuicktimeLanguageLookup[40]  = 'Slovenian';
+			$QuicktimeLanguageLookup[41]  = 'Yiddish';
+			$QuicktimeLanguageLookup[42]  = 'Serbian';
+			$QuicktimeLanguageLookup[43]  = 'Macedonian';
+			$QuicktimeLanguageLookup[44]  = 'Bulgarian';
+			$QuicktimeLanguageLookup[45]  = 'Ukrainian';
+			$QuicktimeLanguageLookup[46]  = 'Byelorussian';
+			$QuicktimeLanguageLookup[47]  = 'Uzbek';
+			$QuicktimeLanguageLookup[48]  = 'Kazakh';
+			$QuicktimeLanguageLookup[49]  = 'Azerbaijani';
+			$QuicktimeLanguageLookup[50]  = 'AzerbaijanAr';
+			$QuicktimeLanguageLookup[51]  = 'Armenian';
+			$QuicktimeLanguageLookup[52]  = 'Georgian';
+			$QuicktimeLanguageLookup[53]  = 'Moldavian';
+			$QuicktimeLanguageLookup[54]  = 'Kirghiz';
+			$QuicktimeLanguageLookup[55]  = 'Tajiki';
+			$QuicktimeLanguageLookup[56]  = 'Turkmen';
+			$QuicktimeLanguageLookup[57]  = 'Mongolian';
+			$QuicktimeLanguageLookup[58]  = 'MongolianCyr';
+			$QuicktimeLanguageLookup[59]  = 'Pashto';
+			$QuicktimeLanguageLookup[60]  = 'Kurdish';
+			$QuicktimeLanguageLookup[61]  = 'Kashmiri';
+			$QuicktimeLanguageLookup[62]  = 'Sindhi';
+			$QuicktimeLanguageLookup[63]  = 'Tibetan';
+			$QuicktimeLanguageLookup[64]  = 'Nepali';
+			$QuicktimeLanguageLookup[65]  = 'Sanskrit';
+			$QuicktimeLanguageLookup[66]  = 'Marathi';
+			$QuicktimeLanguageLookup[67]  = 'Bengali';
+			$QuicktimeLanguageLookup[68]  = 'Assamese';
+			$QuicktimeLanguageLookup[69]  = 'Gujarati';
+			$QuicktimeLanguageLookup[70]  = 'Punjabi';
+			$QuicktimeLanguageLookup[71]  = 'Oriya';
+			$QuicktimeLanguageLookup[72]  = 'Malayalam';
+			$QuicktimeLanguageLookup[73]  = 'Kannada';
+			$QuicktimeLanguageLookup[74]  = 'Tamil';
+			$QuicktimeLanguageLookup[75]  = 'Telugu';
+			$QuicktimeLanguageLookup[76]  = 'Sinhalese';
+			$QuicktimeLanguageLookup[77]  = 'Burmese';
+			$QuicktimeLanguageLookup[78]  = 'Khmer';
+			$QuicktimeLanguageLookup[79]  = 'Lao';
+			$QuicktimeLanguageLookup[80]  = 'Vietnamese';
+			$QuicktimeLanguageLookup[81]  = 'Indonesian';
+			$QuicktimeLanguageLookup[82]  = 'Tagalog';
+			$QuicktimeLanguageLookup[83]  = 'MalayRoman';
+			$QuicktimeLanguageLookup[84]  = 'MalayArabic';
+			$QuicktimeLanguageLookup[85]  = 'Amharic';
+			$QuicktimeLanguageLookup[86]  = 'Tigrinya';
+			$QuicktimeLanguageLookup[87]  = 'Galla';
+			$QuicktimeLanguageLookup[87]  = 'Oromo';
+			$QuicktimeLanguageLookup[88]  = 'Somali';
+			$QuicktimeLanguageLookup[89]  = 'Swahili';
+			$QuicktimeLanguageLookup[90]  = 'Ruanda';
+			$QuicktimeLanguageLookup[91]  = 'Rundi';
+			$QuicktimeLanguageLookup[92]  = 'Chewa';
+			$QuicktimeLanguageLookup[93]  = 'Malagasy';
+			$QuicktimeLanguageLookup[94]  = 'Esperanto';
+			$QuicktimeLanguageLookup[128] = 'Welsh';
+			$QuicktimeLanguageLookup[129] = 'Basque';
+			$QuicktimeLanguageLookup[130] = 'Catalan';
+			$QuicktimeLanguageLookup[131] = 'Latin';
+			$QuicktimeLanguageLookup[132] = 'Quechua';
+			$QuicktimeLanguageLookup[133] = 'Guarani';
+			$QuicktimeLanguageLookup[134] = 'Aymara';
+			$QuicktimeLanguageLookup[135] = 'Tatar';
+			$QuicktimeLanguageLookup[136] = 'Uighur';
+			$QuicktimeLanguageLookup[137] = 'Dzongkha';
+			$QuicktimeLanguageLookup[138] = 'JavaneseRom';
+		}
+		return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
+	}
+
+	function QuicktimeVideoCodecLookup($codecid) {
+		static $QuicktimeVideoCodecLookup = array();
+		if (empty($QuicktimeVideoCodecLookup)) {
+			$QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
+			$QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
+			$QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
+			$QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
+			$QuicktimeVideoCodecLookup['base'] = 'Base';
+			$QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
+			$QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
+			$QuicktimeVideoCodecLookup['clou'] = 'Cloud';
+			$QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
+			$QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
+			$QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
+			$QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
+			$QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
+			$QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
+			$QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
+			$QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
+			$QuicktimeVideoCodecLookup['fire'] = 'Fire';
+			$QuicktimeVideoCodecLookup['flic'] = 'FLC';
+			$QuicktimeVideoCodecLookup['b48r'] = '48RGB';
+			$QuicktimeVideoCodecLookup['gif '] = 'GIF';
+			$QuicktimeVideoCodecLookup['smc '] = 'Graphics';
+			$QuicktimeVideoCodecLookup['h261'] = 'H261';
+			$QuicktimeVideoCodecLookup['h263'] = 'H263';
+			$QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
+			$QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
+			$QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
+			$QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
+			$QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
+			$QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
+			$QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
+			$QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
+			$QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
+			$QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
+			$QuicktimeVideoCodecLookup['png '] = 'PNG';
+			$QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
+			$QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
+			$QuicktimeVideoCodecLookup['raw '] = 'RAW';
+			$QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
+			$QuicktimeVideoCodecLookup['b16g'] = '16Gray';
+			$QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
+			$QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
+			$QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
+			$QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
+			$QuicktimeVideoCodecLookup['tga '] = 'Targa';
+			$QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
+			$QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
+			$QuicktimeVideoCodecLookup['path'] = 'Vector';
+			$QuicktimeVideoCodecLookup['rpza'] = 'Video';
+			$QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
+			$QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
+			$QuicktimeVideoCodecLookup['y420'] = 'YUV420';
+		}
+		return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
+	}
+
+	function QuicktimeAudioCodecLookup($codecid) {
+		static $QuicktimeAudioCodecLookup = array();
+		if (empty($QuicktimeAudioCodecLookup)) {
+			$QuicktimeAudioCodecLookup['.mp3']          = 'Fraunhofer MPEG Layer-III alias';
+			$QuicktimeAudioCodecLookup['aac ']          = 'ISO/IEC 14496-3 AAC';
+			$QuicktimeAudioCodecLookup['agsm']          = 'Apple GSM 10:1';
+			$QuicktimeAudioCodecLookup['alac']          = 'Apple Lossless Audio Codec';
+			$QuicktimeAudioCodecLookup['alaw']          = 'A-law 2:1';
+			$QuicktimeAudioCodecLookup['conv']          = 'Sample Format';
+			$QuicktimeAudioCodecLookup['dvca']          = 'DV';
+			$QuicktimeAudioCodecLookup['dvi ']          = 'DV 4:1';
+			$QuicktimeAudioCodecLookup['eqal']          = 'Frequency Equalizer';
+			$QuicktimeAudioCodecLookup['fl32']          = '32-bit Floating Point';
+			$QuicktimeAudioCodecLookup['fl64']          = '64-bit Floating Point';
+			$QuicktimeAudioCodecLookup['ima4']          = 'Interactive Multimedia Association 4:1';
+			$QuicktimeAudioCodecLookup['in24']          = '24-bit Integer';
+			$QuicktimeAudioCodecLookup['in32']          = '32-bit Integer';
+			$QuicktimeAudioCodecLookup['lpc ']          = 'LPC 23:1';
+			$QuicktimeAudioCodecLookup['MAC3']          = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
+			$QuicktimeAudioCodecLookup['MAC6']          = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
+			$QuicktimeAudioCodecLookup['mixb']          = '8-bit Mixer';
+			$QuicktimeAudioCodecLookup['mixw']          = '16-bit Mixer';
+			$QuicktimeAudioCodecLookup['mp4a']          = 'ISO/IEC 14496-3 AAC';
+			$QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
+			$QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
+			$QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
+			$QuicktimeAudioCodecLookup['NONE']          = 'No Encoding';
+			$QuicktimeAudioCodecLookup['Qclp']          = 'Qualcomm PureVoice';
+			$QuicktimeAudioCodecLookup['QDM2']          = 'QDesign Music 2';
+			$QuicktimeAudioCodecLookup['QDMC']          = 'QDesign Music 1';
+			$QuicktimeAudioCodecLookup['ratb']          = '8-bit Rate';
+			$QuicktimeAudioCodecLookup['ratw']          = '16-bit Rate';
+			$QuicktimeAudioCodecLookup['raw ']          = 'raw PCM';
+			$QuicktimeAudioCodecLookup['sour']          = 'Sound Source';
+			$QuicktimeAudioCodecLookup['sowt']          = 'signed/two\'s complement (Little Endian)';
+			$QuicktimeAudioCodecLookup['str1']          = 'Iomega MPEG layer II';
+			$QuicktimeAudioCodecLookup['str2']          = 'Iomega MPEG *layer II';
+			$QuicktimeAudioCodecLookup['str3']          = 'Iomega MPEG **layer II';
+			$QuicktimeAudioCodecLookup['str4']          = 'Iomega MPEG ***layer II';
+			$QuicktimeAudioCodecLookup['twos']          = 'signed/two\'s complement (Big Endian)';
+			$QuicktimeAudioCodecLookup['ulaw']          = 'mu-law 2:1';
+		}
+		return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
+	}
+
+	function QuicktimeDCOMLookup($compressionid) {
+		static $QuicktimeDCOMLookup = array();
+		if (empty($QuicktimeDCOMLookup)) {
+			$QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
+			$QuicktimeDCOMLookup['adec'] = 'Apple Compression';
+		}
+		return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
+	}
+
+	function QuicktimeColorNameLookup($colordepthid) {
+		static $QuicktimeColorNameLookup = array();
+		if (empty($QuicktimeColorNameLookup)) {
+			$QuicktimeColorNameLookup[1]  = '2-color (monochrome)';
+			$QuicktimeColorNameLookup[2]  = '4-color';
+			$QuicktimeColorNameLookup[4]  = '16-color';
+			$QuicktimeColorNameLookup[8]  = '256-color';
+			$QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
+			$QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
+			$QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
+			$QuicktimeColorNameLookup[33] = 'black & white';
+			$QuicktimeColorNameLookup[34] = '4-gray';
+			$QuicktimeColorNameLookup[36] = '16-gray';
+			$QuicktimeColorNameLookup[40] = '256-gray';
+		}
+		return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
+	}
+
+	function CopyToAppropriateCommentsSection($keyname, $data, &$ThisFileInfo) {
+		static $handyatomtranslatorarray = array();
+		if (empty($handyatomtranslatorarray)) {
+			$handyatomtranslatorarray['©cpy'] = 'copyright';
+			$handyatomtranslatorarray['©day'] = 'creation_date';
+			$handyatomtranslatorarray['©dir'] = 'director';
+			$handyatomtranslatorarray['©ed1'] = 'edit1';
+			$handyatomtranslatorarray['©ed2'] = 'edit2';
+			$handyatomtranslatorarray['©ed3'] = 'edit3';
+			$handyatomtranslatorarray['©ed4'] = 'edit4';
+			$handyatomtranslatorarray['©ed5'] = 'edit5';
+			$handyatomtranslatorarray['©ed6'] = 'edit6';
+			$handyatomtranslatorarray['©ed7'] = 'edit7';
+			$handyatomtranslatorarray['©ed8'] = 'edit8';
+			$handyatomtranslatorarray['©ed9'] = 'edit9';
+			$handyatomtranslatorarray['©fmt'] = 'format';
+			$handyatomtranslatorarray['©inf'] = 'information';
+			$handyatomtranslatorarray['©prd'] = 'producer';
+			$handyatomtranslatorarray['©prf'] = 'performers';
+			$handyatomtranslatorarray['©req'] = 'system_requirements';
+			$handyatomtranslatorarray['©src'] = 'source_credit';
+			$handyatomtranslatorarray['©wrt'] = 'writer';
+
+			// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
+			$handyatomtranslatorarray['©nam'] = 'title';
+			$handyatomtranslatorarray['©cmt'] = 'comment';
+			$handyatomtranslatorarray['©wrn'] = 'warning';
+			$handyatomtranslatorarray['©hst'] = 'host_computer';
+			$handyatomtranslatorarray['©mak'] = 'make';
+			$handyatomtranslatorarray['©mod'] = 'model';
+			$handyatomtranslatorarray['©PRD'] = 'product';
+			$handyatomtranslatorarray['©swr'] = 'software';
+			$handyatomtranslatorarray['©aut'] = 'author';
+			$handyatomtranslatorarray['©ART'] = 'artist';
+			$handyatomtranslatorarray['©trk'] = 'track';
+			$handyatomtranslatorarray['©alb'] = 'album';
+			$handyatomtranslatorarray['©com'] = 'comment';
+			$handyatomtranslatorarray['©gen'] = 'genre';
+			$handyatomtranslatorarray['©ope'] = 'composer';
+			$handyatomtranslatorarray['©url'] = 'url';
+			$handyatomtranslatorarray['©enc'] = 'encoder';
+		}
+		if (isset($handyatomtranslatorarray[$keyname])) {
+			$ThisFileInfo['quicktime']['comments'][$handyatomtranslatorarray[$keyname]][] = $data;
+		}
+
+		return true;
+	}
+
+	function NoNullString($nullterminatedstring) {
+		// remove the single null terminator on null terminated strings
+		if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
+			return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
+		}
+		return $nullterminatedstring;
+	}
+
+	function Pascal2String($pascalstring) {
+		// Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
+		return substr($pascalstring, 1);
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.real.php b/apps/media/getID3/getid3/module.audio-video.real.php
new file mode 100644
index 0000000000000000000000000000000000000000..013f4784bcb66ca862657b010e2fbc728041b461
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.real.php
@@ -0,0 +1,528 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.real.php                                 //
+// module for analyzing Real Audio/Video files                 //
+// dependencies: module.audio-video.riff.php                   //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+class getid3_real
+{
+
+	function getid3_real(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']       = 'real';
+		$ThisFileInfo['bitrate']          = 0;
+		$ThisFileInfo['playtime_seconds'] = 0;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$ChunkCounter = 0;
+		while (ftell($fd) < $ThisFileInfo['avdataend']) {
+			$ChunkData  = fread($fd, 8);
+			$ChunkName  =                           substr($ChunkData, 0, 4);
+			$ChunkSize  = getid3_lib::BigEndian2Int(substr($ChunkData, 4, 4));
+
+			if ($ChunkName == '.ra'."\xFD") {
+				$ChunkData .= fread($fd, $ChunkSize - 8);
+				if ($this->ParseOldRAheader(substr($ChunkData, 0, 128), $ThisFileInfo['real']['old_ra_header'])) {
+					$ThisFileInfo['audio']['dataformat']      = 'real';
+					$ThisFileInfo['audio']['lossless']        = false;
+					$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['real']['old_ra_header']['sample_rate'];
+					$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['real']['old_ra_header']['bits_per_sample'];
+					$ThisFileInfo['audio']['channels']        = $ThisFileInfo['real']['old_ra_header']['channels'];
+
+					$ThisFileInfo['playtime_seconds']         = 60 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['real']['old_ra_header']['bytes_per_minute']);
+					$ThisFileInfo['audio']['bitrate']         =  8 * ($ThisFileInfo['real']['old_ra_header']['audio_bytes'] / $ThisFileInfo['playtime_seconds']);
+					$ThisFileInfo['audio']['codec']           = $this->RealAudioCodecFourCClookup($ThisFileInfo['real']['old_ra_header']['fourcc'], $ThisFileInfo['audio']['bitrate']);
+
+					foreach ($ThisFileInfo['real']['old_ra_header']['comments'] as $key => $valuearray) {
+						if (strlen(trim($valuearray[0])) > 0) {
+							$ThisFileInfo['real']['comments'][$key][] = trim($valuearray[0]);
+						}
+					}
+					return true;
+				}
+				$ThisFileInfo['error'][] = 'There was a problem parsing this RealAudio file. Please submit it for analysis to http://www.getid3.org/upload/ or info@getid3.org';
+				unset($ThisFileInfo['bitrate']);
+				unset($ThisFileInfo['playtime_seconds']);
+				return false;
+			}
+
+			// shortcut
+			$ThisFileInfo['real']['chunks'][$ChunkCounter] = array();
+			$thisfile_real_chunks_currentchunk = &$ThisFileInfo['real']['chunks'][$ChunkCounter];
+
+			$thisfile_real_chunks_currentchunk['name']   = $ChunkName;
+			$thisfile_real_chunks_currentchunk['offset'] = ftell($fd) - 8;
+			$thisfile_real_chunks_currentchunk['length'] = $ChunkSize;
+			if (($thisfile_real_chunks_currentchunk['offset'] + $thisfile_real_chunks_currentchunk['length']) > $ThisFileInfo['avdataend']) {
+				$ThisFileInfo['warning'][] = 'Chunk "'.$thisfile_real_chunks_currentchunk['name'].'" at offset '.$thisfile_real_chunks_currentchunk['offset'].' claims to be '.$thisfile_real_chunks_currentchunk['length'].' bytes long, which is beyond end of file';
+				return false;
+			}
+
+			if ($ChunkSize > (GETID3_FREAD_BUFFER_SIZE + 8)) {
+
+				$ChunkData .= fread($fd, GETID3_FREAD_BUFFER_SIZE - 8);
+				fseek($fd, $thisfile_real_chunks_currentchunk['offset'] + $ChunkSize, SEEK_SET);
+
+			} elseif(($ChunkSize - 8) > 0) {
+                
+				$ChunkData .= fread($fd, $ChunkSize - 8);
+
+			}
+			$offset = 8;
+
+			switch ($ChunkName) {
+
+				case '.RMF': // RealMedia File Header
+					$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+					$offset += 2;
+					switch ($thisfile_real_chunks_currentchunk['object_version']) {
+
+						case 0:
+							$thisfile_real_chunks_currentchunk['file_version']  = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+							$offset += 4;
+							$thisfile_real_chunks_currentchunk['headers_count'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+							$offset += 4;
+							break;
+
+						default:
+							//$ThisFileInfo['warning'][] = 'Expected .RMF-object_version to be "0", actual value is "'.$thisfile_real_chunks_currentchunk['object_version'].'" (should not be a problem)';
+							break;
+
+					}
+					break;
+
+
+				case 'PROP': // Properties Header
+					$thisfile_real_chunks_currentchunk['object_version']      = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
+						$thisfile_real_chunks_currentchunk['max_bit_rate']    = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['avg_bit_rate']    = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['max_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['avg_packet_size'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['num_packets']     = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['duration']        = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['preroll']         = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['index_offset']    = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['data_offset']     = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['num_streams']     = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['flags_raw']       = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$ThisFileInfo['playtime_seconds'] = $thisfile_real_chunks_currentchunk['duration'] / 1000;
+						if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
+							$ThisFileInfo['bitrate'] += $thisfile_real_chunks_currentchunk['avg_bit_rate'];
+						}
+						$thisfile_real_chunks_currentchunk['flags']['save_enabled']   = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0001);
+						$thisfile_real_chunks_currentchunk['flags']['perfect_play']   = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0002);
+						$thisfile_real_chunks_currentchunk['flags']['live_broadcast'] = (bool) ($thisfile_real_chunks_currentchunk['flags_raw'] & 0x0004);
+					}
+					break;
+
+				case 'MDPR': // Media Properties Header
+					$thisfile_real_chunks_currentchunk['object_version']         = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
+						$thisfile_real_chunks_currentchunk['stream_number']      = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['max_bit_rate']       = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['avg_bit_rate']       = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['max_packet_size']    = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['avg_packet_size']    = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['start_time']         = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['preroll']            = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['duration']           = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['stream_name_size']   = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
+						$offset += 1;
+						$thisfile_real_chunks_currentchunk['stream_name']        = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['stream_name_size']);
+						$offset += $thisfile_real_chunks_currentchunk['stream_name_size'];
+						$thisfile_real_chunks_currentchunk['mime_type_size']     = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 1));
+						$offset += 1;
+						$thisfile_real_chunks_currentchunk['mime_type']          = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['mime_type_size']);
+						$offset += $thisfile_real_chunks_currentchunk['mime_type_size'];
+						$thisfile_real_chunks_currentchunk['type_specific_len']  = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['type_specific_data'] = substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['type_specific_len']);
+						$offset += $thisfile_real_chunks_currentchunk['type_specific_len'];
+
+						// shortcut
+						$thisfile_real_chunks_currentchunk_typespecificdata = &$thisfile_real_chunks_currentchunk['type_specific_data'];
+
+						switch ($thisfile_real_chunks_currentchunk['mime_type']) {
+							case 'video/x-pn-realvideo':
+							case 'video/x-pn-multirate-realvideo':
+								// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
+
+								// shortcut
+								$thisfile_real_chunks_currentchunk['video_info'] = array();
+								$thisfile_real_chunks_currentchunk_videoinfo     = &$thisfile_real_chunks_currentchunk['video_info'];
+
+								$thisfile_real_chunks_currentchunk_videoinfo['dwSize']            = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata,  0, 4));
+								$thisfile_real_chunks_currentchunk_videoinfo['fourcc1']           =                           substr($thisfile_real_chunks_currentchunk_typespecificdata,  4, 4);
+								$thisfile_real_chunks_currentchunk_videoinfo['fourcc2']           =                           substr($thisfile_real_chunks_currentchunk_typespecificdata,  8, 4);
+								$thisfile_real_chunks_currentchunk_videoinfo['width']             = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 12, 2));
+								$thisfile_real_chunks_currentchunk_videoinfo['height']            = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 14, 2));
+								$thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample']   = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 16, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown1']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 18, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown2']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 20, 2));
+								$thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 22, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown3']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 24, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown4']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 26, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown5']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 28, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown6']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 30, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown7']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 32, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown8']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 34, 2));
+								//$thisfile_real_chunks_currentchunk_videoinfo['unknown9']          = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 36, 2));
+
+								$thisfile_real_chunks_currentchunk_videoinfo['codec'] = getid3_riff::RIFFfourccLookup($thisfile_real_chunks_currentchunk_videoinfo['fourcc2']);
+
+								$ThisFileInfo['video']['resolution_x']    =         $thisfile_real_chunks_currentchunk_videoinfo['width'];
+								$ThisFileInfo['video']['resolution_y']    =         $thisfile_real_chunks_currentchunk_videoinfo['height'];
+								$ThisFileInfo['video']['frame_rate']      = (float) $thisfile_real_chunks_currentchunk_videoinfo['frames_per_second'];
+								$ThisFileInfo['video']['codec']           =         $thisfile_real_chunks_currentchunk_videoinfo['codec'];
+								$ThisFileInfo['video']['bits_per_sample'] =         $thisfile_real_chunks_currentchunk_videoinfo['bits_per_sample'];
+								break;
+
+							case 'audio/x-pn-realaudio':
+							case 'audio/x-pn-multirate-realaudio':
+								$this->ParseOldRAheader($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk['parsed_audio_data']);
+
+								$ThisFileInfo['audio']['sample_rate']     = $thisfile_real_chunks_currentchunk['parsed_audio_data']['sample_rate'];
+								$ThisFileInfo['audio']['bits_per_sample'] = $thisfile_real_chunks_currentchunk['parsed_audio_data']['bits_per_sample'];
+								$ThisFileInfo['audio']['channels']        = $thisfile_real_chunks_currentchunk['parsed_audio_data']['channels'];
+								if (!empty($ThisFileInfo['audio']['dataformat'])) {
+									foreach ($ThisFileInfo['audio'] as $key => $value) {
+										if ($key != 'streams') {
+											$ThisFileInfo['audio']['streams'][$thisfile_real_chunks_currentchunk['stream_number']][$key] = $value;
+										}
+									}
+								}
+								break;
+
+							case 'logical-fileinfo':
+								// shortcut
+								$thisfile_real_chunks_currentchunk['logical_fileinfo'] = array();
+								$thisfile_real_chunks_currentchunk_logicalfileinfo     = &$thisfile_real_chunks_currentchunk['logical_fileinfo'];
+
+								$thisfile_real_chunks_currentchunk_logicalfileinfo_offset = 0;
+								$thisfile_real_chunks_currentchunk_logicalfileinfo['logical_fileinfo_length'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
+								$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
+
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown1']                = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
+								$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
+
+								$thisfile_real_chunks_currentchunk_logicalfileinfo['num_tags']                = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
+								$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
+
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo['unknown2']                = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
+								$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += 4;
+
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo['d']                       = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 1));
+
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo['one_type'] = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata,     $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 4));
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo_thislength  = getid3_lib::BigEndian2Int(substr($thisfile_real_chunks_currentchunk_typespecificdata, 4 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, 2));
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo['one']      =                           substr($thisfile_real_chunks_currentchunk_typespecificdata, 6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_offset, $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
+								//$thisfile_real_chunks_currentchunk_logicalfileinfo_offset += (6 + $thisfile_real_chunks_currentchunk_logicalfileinfo_thislength);
+
+								break;
+
+						}
+
+
+						if (empty($ThisFileInfo['playtime_seconds'])) {
+							$ThisFileInfo['playtime_seconds'] = max($ThisFileInfo['playtime_seconds'], ($thisfile_real_chunks_currentchunk['duration'] + $thisfile_real_chunks_currentchunk['start_time']) / 1000);
+						}
+						if ($thisfile_real_chunks_currentchunk['duration'] > 0) {
+							switch ($thisfile_real_chunks_currentchunk['mime_type']) {
+								case 'audio/x-pn-realaudio':
+								case 'audio/x-pn-multirate-realaudio':
+									$ThisFileInfo['audio']['bitrate']    = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
+									$ThisFileInfo['audio']['codec']      = $this->RealAudioCodecFourCClookup($thisfile_real_chunks_currentchunk['parsed_audio_data']['fourcc'], $ThisFileInfo['audio']['bitrate']);
+									$ThisFileInfo['audio']['dataformat'] = 'real';
+									$ThisFileInfo['audio']['lossless']   = false;
+									break;
+
+								case 'video/x-pn-realvideo':
+								case 'video/x-pn-multirate-realvideo':
+									$ThisFileInfo['video']['bitrate']            = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
+									$ThisFileInfo['video']['bitrate_mode']       = 'cbr';
+									$ThisFileInfo['video']['dataformat']         = 'real';
+									$ThisFileInfo['video']['lossless']           = false;
+									$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+									break;
+
+								case 'audio/x-ralf-mpeg4-generic':
+									$ThisFileInfo['audio']['bitrate']    = (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0) + $thisfile_real_chunks_currentchunk['avg_bit_rate'];
+									$ThisFileInfo['audio']['codec']      = 'RealAudio Lossless';
+									$ThisFileInfo['audio']['dataformat'] = 'real';
+									$ThisFileInfo['audio']['lossless']   = true;
+									break;
+							}
+							$ThisFileInfo['bitrate'] = (isset($ThisFileInfo['video']['bitrate']) ? $ThisFileInfo['video']['bitrate'] : 0) + (isset($ThisFileInfo['audio']['bitrate']) ? $ThisFileInfo['audio']['bitrate'] : 0);
+						}
+					}
+					break;
+
+				case 'CONT': // Content Description Header (text comments)
+					$thisfile_real_chunks_currentchunk['object_version'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
+						$thisfile_real_chunks_currentchunk['title_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['title'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['title_len']);
+						$offset += $thisfile_real_chunks_currentchunk['title_len'];
+
+						$thisfile_real_chunks_currentchunk['artist_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['artist'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['artist_len']);
+						$offset += $thisfile_real_chunks_currentchunk['artist_len'];
+
+						$thisfile_real_chunks_currentchunk['copyright_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['copyright'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['copyright_len']);
+						$offset += $thisfile_real_chunks_currentchunk['copyright_len'];
+
+						$thisfile_real_chunks_currentchunk['comment_len'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['comment'] = (string) substr($ChunkData, $offset, $thisfile_real_chunks_currentchunk['comment_len']);
+						$offset += $thisfile_real_chunks_currentchunk['comment_len'];
+
+
+						$commentkeystocopy = array('title'=>'title', 'artist'=>'artist', 'copyright'=>'copyright', 'comment'=>'comment');
+						foreach ($commentkeystocopy as $key => $val) {
+							if ($thisfile_real_chunks_currentchunk[$key]) {
+								$ThisFileInfo['real']['comments'][$val][] = trim($thisfile_real_chunks_currentchunk[$key]);
+							}
+						}
+
+					}
+					break;
+
+
+				case 'DATA': // Data Chunk Header
+					// do nothing
+					break;
+
+				case 'INDX': // Index Section Header
+					$thisfile_real_chunks_currentchunk['object_version']        = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+					$offset += 2;
+					if ($thisfile_real_chunks_currentchunk['object_version'] == 0) {
+						$thisfile_real_chunks_currentchunk['num_indices']       = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+						$thisfile_real_chunks_currentchunk['stream_number']     = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 2));
+						$offset += 2;
+						$thisfile_real_chunks_currentchunk['next_index_header'] = getid3_lib::BigEndian2Int(substr($ChunkData, $offset, 4));
+						$offset += 4;
+
+						if ($thisfile_real_chunks_currentchunk['next_index_header'] == 0) {
+							// last index chunk found, ignore rest of file
+							break 2;
+						} else {
+							// non-last index chunk, seek to next index chunk (skipping actual index data)
+							fseek($fd, $thisfile_real_chunks_currentchunk['next_index_header'], SEEK_SET);
+						}
+					}
+					break;
+
+				default:
+					$ThisFileInfo['warning'][] = 'Unhandled RealMedia chunk "'.$ChunkName.'" at offset '.$thisfile_real_chunks_currentchunk['offset'];
+					break;
+			}
+			$ChunkCounter++;
+		}
+
+		if (!empty($ThisFileInfo['audio']['streams'])) {
+			$ThisFileInfo['audio']['bitrate'] = 0;
+			foreach ($ThisFileInfo['audio']['streams'] as $key => $valuearray) {
+				$ThisFileInfo['audio']['bitrate'] += $valuearray['bitrate'];
+			}
+		}
+
+		return true;
+	}
+
+
+	function ParseOldRAheader($OldRAheaderData, &$ParsedArray) {
+		// http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
+
+		$ParsedArray = array();
+		$ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
+		if ($ParsedArray['magic'] != '.ra'."\xFD") {
+			return false;
+		}
+		$ParsedArray['version1']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData,  4, 2));
+
+		if ($ParsedArray['version1'] < 3) {
+
+			return false;
+
+		} elseif ($ParsedArray['version1'] == 3) {
+
+			$ParsedArray['fourcc1']          = '.ra3';
+			$ParsedArray['bits_per_sample']  = 16;   // hard-coded for old versions?
+			$ParsedArray['sample_rate']      = 8000; // hard-coded for old versions?
+
+			$ParsedArray['header_size']      = getid3_lib::BigEndian2Int(substr($OldRAheaderData,  6, 2));
+			$ParsedArray['channels']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData,  8, 2)); // always 1 (?)
+			//$ParsedArray['unknown1']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
+			//$ParsedArray['unknown2']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
+			//$ParsedArray['unknown3']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
+			$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
+			$ParsedArray['audio_bytes']      = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
+			$ParsedArray['comments_raw']     =                           substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1); // not including null terminator
+
+			$commentoffset = 0;
+			$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
+			$ParsedArray['comments']['title'][]     = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
+			$commentoffset += $commentlength;
+
+			$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
+			$ParsedArray['comments']['artist'][]    = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
+			$commentoffset += $commentlength;
+
+			$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
+			$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
+			$commentoffset += $commentlength;
+
+			$commentoffset++; // final null terminator (?)
+			$commentoffset++; // fourcc length (?) should be 4
+			$ParsedArray['fourcc']           =                           substr($OldRAheaderData, 23 + $commentoffset, 4);
+
+		} elseif ($ParsedArray['version1'] <= 5) {
+
+			//$ParsedArray['unknown1']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData,  6, 2));
+			$ParsedArray['fourcc1']          =                           substr($OldRAheaderData,  8, 4);
+			$ParsedArray['file_size']        = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 4));
+			$ParsedArray['version2']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 16, 2));
+			$ParsedArray['header_size']      = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 18, 4));
+			$ParsedArray['codec_flavor_id']  = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 22, 2));
+			$ParsedArray['coded_frame_size'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 24, 4));
+			$ParsedArray['audio_bytes']      = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 28, 4));
+			$ParsedArray['bytes_per_minute'] = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 32, 4));
+			//$ParsedArray['unknown5']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
+			$ParsedArray['sub_packet_h']     = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 40, 2));
+			$ParsedArray['frame_size']       = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 42, 2));
+			$ParsedArray['sub_packet_size']  = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 44, 2));
+			//$ParsedArray['unknown6']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
+
+			switch ($ParsedArray['version1']) {
+
+				case 4:
+					$ParsedArray['sample_rate']      = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 2));
+					//$ParsedArray['unknown8']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
+					$ParsedArray['bits_per_sample']  = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 2));
+					$ParsedArray['channels']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 54, 2));
+					$ParsedArray['length_fourcc2']   = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 1));
+					$ParsedArray['fourcc2']          =                           substr($OldRAheaderData, 57, 4);
+					$ParsedArray['length_fourcc3']   = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 61, 1));
+					$ParsedArray['fourcc3']          =                           substr($OldRAheaderData, 62, 4);
+					//$ParsedArray['unknown9']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
+					//$ParsedArray['unknown10']        = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
+					$ParsedArray['comments_raw']     =                           substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
+
+					$commentoffset = 0;
+					$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
+					$ParsedArray['comments']['title'][]     = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
+					$commentoffset += $commentlength;
+
+					$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
+					$ParsedArray['comments']['artist'][]    = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
+					$commentoffset += $commentlength;
+
+					$commentlength = getid3_lib::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
+					$ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
+					$commentoffset += $commentlength;
+					break;
+
+				case 5:
+					$ParsedArray['sample_rate']      = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 48, 4));
+					$ParsedArray['sample_rate2']     = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 52, 4));
+					$ParsedArray['bits_per_sample']  = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 56, 4));
+					$ParsedArray['channels']         = getid3_lib::BigEndian2Int(substr($OldRAheaderData, 60, 2));
+					$ParsedArray['genr']             =                           substr($OldRAheaderData, 62, 4);
+					$ParsedArray['fourcc3']          =                           substr($OldRAheaderData, 66, 4);
+					$ParsedArray['comments']         = array();
+					break;
+			}
+			$ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
+
+		}
+		foreach ($ParsedArray['comments'] as $key => $value) {
+			if ($ParsedArray['comments'][$key][0] === false) {
+				$ParsedArray['comments'][$key][0] = '';
+			}
+		}
+
+		return true;
+	}
+
+	function RealAudioCodecFourCClookup($fourcc, $bitrate) {
+		static $RealAudioCodecFourCClookup = array();
+		if (empty($RealAudioCodecFourCClookup)) {
+			// http://www.its.msstate.edu/net/real/reports/config/tags.stats
+			// http://www.freelists.org/archives/matroska-devel/06-2003/fullthread18.html
+
+			$RealAudioCodecFourCClookup['14_4'][8000]  = 'RealAudio v2 (14.4kbps)';
+			$RealAudioCodecFourCClookup['14.4'][8000]  = 'RealAudio v2 (14.4kbps)';
+			$RealAudioCodecFourCClookup['lpcJ'][8000]  = 'RealAudio v2 (14.4kbps)';
+			$RealAudioCodecFourCClookup['28_8'][15200] = 'RealAudio v2 (28.8kbps)';
+			$RealAudioCodecFourCClookup['28.8'][15200] = 'RealAudio v2 (28.8kbps)';
+			$RealAudioCodecFourCClookup['sipr'][4933]  = 'RealAudio v4 (5kbps Voice)';
+			$RealAudioCodecFourCClookup['sipr'][6444]  = 'RealAudio v4 (6.5kbps Voice)';
+			$RealAudioCodecFourCClookup['sipr'][8444]  = 'RealAudio v4 (8.5kbps Voice)';
+			$RealAudioCodecFourCClookup['sipr'][16000] = 'RealAudio v4 (16kbps Wideband)';
+			$RealAudioCodecFourCClookup['dnet'][8000]  = 'RealAudio v3 (8kbps Music)';
+			$RealAudioCodecFourCClookup['dnet'][16000] = 'RealAudio v3 (16kbps Music Low Response)';
+			$RealAudioCodecFourCClookup['dnet'][15963] = 'RealAudio v3 (16kbps Music Mid/High Response)';
+			$RealAudioCodecFourCClookup['dnet'][20000] = 'RealAudio v3 (20kbps Music Stereo)';
+			$RealAudioCodecFourCClookup['dnet'][32000] = 'RealAudio v3 (32kbps Music Mono)';
+			$RealAudioCodecFourCClookup['dnet'][31951] = 'RealAudio v3 (32kbps Music Stereo)';
+			$RealAudioCodecFourCClookup['dnet'][39965] = 'RealAudio v3 (40kbps Music Mono)';
+			$RealAudioCodecFourCClookup['dnet'][40000] = 'RealAudio v3 (40kbps Music Stereo)';
+			$RealAudioCodecFourCClookup['dnet'][79947] = 'RealAudio v3 (80kbps Music Mono)';
+			$RealAudioCodecFourCClookup['dnet'][80000] = 'RealAudio v3 (80kbps Music Stereo)';
+
+			$RealAudioCodecFourCClookup['dnet'][0] = 'RealAudio v3';
+			$RealAudioCodecFourCClookup['sipr'][0] = 'RealAudio v4';
+			$RealAudioCodecFourCClookup['cook'][0] = 'RealAudio G2';
+			$RealAudioCodecFourCClookup['atrc'][0] = 'RealAudio 8';
+		}
+		$roundbitrate = intval(round($bitrate));
+		if (isset($RealAudioCodecFourCClookup[$fourcc][$roundbitrate])) {
+			return $RealAudioCodecFourCClookup[$fourcc][$roundbitrate];
+		} elseif (isset($RealAudioCodecFourCClookup[$fourcc][0])) {
+			return $RealAudioCodecFourCClookup[$fourcc][0];
+		}
+		return $fourcc;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.riff.php b/apps/media/getID3/getid3/module.audio-video.riff.php
new file mode 100644
index 0000000000000000000000000000000000000000..74ea33966c07e974f592007989b9fe4d9f16f6a1
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.riff.php
@@ -0,0 +1,2110 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.riff.php                                 //
+// module for analyzing RIFF files                             //
+// multiple formats supported by this module:                  //
+//    Wave, AVI, AIFF/AIFC, (MP3,AC3)/RIFF, Wavpack v3, 8SVX   //
+// dependencies: module.audio.mp3.php                          //
+//               module.audio.ac3.php (optional)               //
+//               module.audio.dts.php (optional)               //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
+
+class getid3_riff
+{
+
+	function getid3_riff(&$fd, &$ThisFileInfo) {
+
+		// initialize these values to an empty array, otherwise they default to NULL
+		// and you can't append array values to a NULL value
+		$ThisFileInfo['riff'] = array('raw'=>array());
+
+		// Shortcuts
+		$thisfile_riff             = &$ThisFileInfo['riff'];
+		$thisfile_riff_raw         = &$thisfile_riff['raw'];
+		$thisfile_audio            = &$ThisFileInfo['audio'];
+		$thisfile_video            = &$ThisFileInfo['video'];
+		$thisfile_avdataoffset     = &$ThisFileInfo['avdataoffset'];
+		$thisfile_avdataend        = &$ThisFileInfo['avdataend'];
+		$thisfile_audio_dataformat = &$thisfile_audio['dataformat'];
+		$thisfile_riff_audio       = &$thisfile_riff['audio'];
+		$thisfile_riff_video       = &$thisfile_riff['video'];
+
+
+		$Original['avdataoffset'] = $thisfile_avdataoffset;
+		$Original['avdataend']    = $thisfile_avdataend;
+
+		fseek($fd, $thisfile_avdataoffset, SEEK_SET);
+		$RIFFheader = fread($fd, 12);
+		$RIFFsubtype = substr($RIFFheader, 8, 4);
+		switch (substr($RIFFheader, 0, 4)) {
+			case 'FORM':
+				$ThisFileInfo['fileformat']   = 'aiff';
+				$RIFFheaderSize               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4));
+				$thisfile_riff[$RIFFsubtype]  = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo);
+				$thisfile_riff['header_size'] = $RIFFheaderSize;
+				break;
+
+			case 'RIFF':
+			case 'SDSS':  // SDSS is identical to RIFF, just renamed. Used by SmartSound QuickTracks (www.smartsound.com)
+			case 'RMP3':  // RMP3 is identical to RIFF, just renamed. Used by [unknown program] when creating RIFF-MP3s
+				if ($RIFFsubtype == 'RMP3') {
+					// RMP3 is identical to WAVE, just renamed. Used by [unknown program] when creating RIFF-MP3s
+					$RIFFsubtype = 'WAVE';
+				}
+
+				$ThisFileInfo['fileformat']   = 'riff';
+				$RIFFheaderSize               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($RIFFheader, 4, 4));
+				$thisfile_riff['header_size'] = $RIFFheaderSize;
+				$thisfile_riff[$RIFFsubtype]  = getid3_riff::ParseRIFF($fd, $thisfile_avdataoffset + 12, $thisfile_avdataoffset + $RIFFheaderSize, $ThisFileInfo);
+
+				fseek($fd, $thisfile_avdataoffset + $RIFFheaderSize);
+				$nextRIFFheader = fread($fd, 20);
+				if (substr($nextRIFFheader, 8, 4) == 'RIFF') {
+					$nextRIFFsize = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($nextRIFFheader, 12, 4));
+					$nextRIFFtype = substr($nextRIFFheader, 16, 4).'<br>';
+					$thisfile_riff[$nextRIFFtype]['offset'] = ftell($fd) - 4;
+					$thisfile_riff[$nextRIFFtype]['size']   = $nextRIFFsize;
+					$ThisFileInfo['avdataend'] = $thisfile_riff[$nextRIFFtype]['offset'] + $thisfile_riff[$nextRIFFtype]['size'];
+					$ThisFileInfo['error'][]   = 'AVI extends beyond 2GB and PHP filesystem functions cannot read that far, playtime is probably wrong';
+					$ThisFileInfo['warning'][] = '[avdataend] value may be incorrect, multiple AVIX chunks may be present';
+					$thisfile_riff[$nextRIFFtype]  = getid3_riff::ParseRIFF($fd, $thisfile_riff[$nextRIFFtype]['offset'] + 4, $thisfile_riff[$nextRIFFtype]['offset'] + $thisfile_riff[$nextRIFFtype]['size'], $ThisFileInfo);
+				}
+				if ($RIFFsubtype == 'WAVE') {
+					$thisfile_riff_WAVE = &$thisfile_riff['WAVE'];
+				}
+				break;
+
+			default:
+				$ThisFileInfo['error'][] = 'Cannot parse RIFF (this is maybe not a RIFF / WAV / AVI file?) - expecting "FORM|RIFF|SDSS|RMP3" found "'.$RIFFsubtype.'" instead';
+				unset($ThisFileInfo['fileformat']);
+				return false;
+				break;
+		}
+
+		$streamindex = 0;
+		switch ($RIFFsubtype) {
+			case 'WAVE':
+				if (empty($thisfile_audio['bitrate_mode'])) {
+					$thisfile_audio['bitrate_mode'] = 'cbr';
+				}
+				if (empty($thisfile_audio_dataformat)) {
+					$thisfile_audio_dataformat = 'wav';
+				}
+
+				if (isset($thisfile_riff_WAVE['data'][0]['offset'])) {
+					$thisfile_avdataoffset = $thisfile_riff_WAVE['data'][0]['offset'] + 8;
+					$thisfile_avdataend    = $thisfile_avdataoffset + $thisfile_riff_WAVE['data'][0]['size'];
+				}
+				if (isset($thisfile_riff_WAVE['fmt '][0]['data'])) {
+
+					$thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($thisfile_riff_WAVE['fmt '][0]['data']);
+					$thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
+					if (@$thisfile_riff_audio[$streamindex]['bitrate'] == 0) {
+						$ThisFileInfo['error'][] = 'Corrupt RIFF file: bitrate_audio == zero';
+						return false;
+					}
+					$thisfile_riff_raw['fmt '] = $thisfile_riff_audio[$streamindex]['raw'];
+					unset($thisfile_riff_audio[$streamindex]['raw']);
+					$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
+
+					$thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
+					if (substr($thisfile_audio['codec'], 0, strlen('unknown: 0x')) == 'unknown: 0x') {
+						$ThisFileInfo['warning'][] = 'Audio codec = '.$thisfile_audio['codec'];
+					}
+					$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
+
+					$ThisFileInfo['playtime_seconds'] = (float) ((($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $thisfile_audio['bitrate']);
+
+					$thisfile_audio['lossless'] = false;
+					if (isset($thisfile_riff_WAVE['data'][0]['offset']) && isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
+						switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
+
+							case 0x0001:  // PCM
+								$thisfile_audio['lossless'] = true;
+								break;
+
+							case 0x2000:  // AC-3
+								$thisfile_audio_dataformat = 'ac3';
+								break;
+
+							default:
+								// do nothing
+								break;
+
+						}
+					}
+					$thisfile_audio['streams'][$streamindex]['wformattag']   = $thisfile_audio['wformattag'];
+					$thisfile_audio['streams'][$streamindex]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
+					$thisfile_audio['streams'][$streamindex]['lossless']     = $thisfile_audio['lossless'];
+					$thisfile_audio['streams'][$streamindex]['dataformat']   = $thisfile_audio_dataformat;
+				}
+
+				if (isset($thisfile_riff_WAVE['rgad'][0]['data'])) {
+
+					// shortcuts
+					$rgadData = &$thisfile_riff_WAVE['rgad'][0]['data'];
+					$thisfile_riff_raw['rgad']    = array('track'=>array(), 'album'=>array());
+					$thisfile_riff_raw_rgad       = &$thisfile_riff_raw['rgad'];
+					$thisfile_riff_raw_rgad_track = &$thisfile_riff_raw_rgad['track'];
+					$thisfile_riff_raw_rgad_album = &$thisfile_riff_raw_rgad['album'];
+
+					$thisfile_riff_raw_rgad['fPeakAmplitude']      =               getid3_lib::LittleEndian2Float(substr($rgadData, 0, 4));
+					$thisfile_riff_raw_rgad['nRadioRgAdjust']      = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 4, 2));
+					$thisfile_riff_raw_rgad['nAudiophileRgAdjust'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($rgadData, 6, 2));
+
+					$nRadioRgAdjustBitstring      = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nRadioRgAdjust']), 16, '0', STR_PAD_LEFT);
+					$nAudiophileRgAdjustBitstring = str_pad(getid3_lib::Dec2Bin($thisfile_riff_raw_rgad['nAudiophileRgAdjust']), 16, '0', STR_PAD_LEFT);
+					$thisfile_riff_raw_rgad_track['name']       = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 0, 3));
+					$thisfile_riff_raw_rgad_track['originator'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 3, 3));
+					$thisfile_riff_raw_rgad_track['signbit']    = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 6, 1));
+					$thisfile_riff_raw_rgad_track['adjustment'] = getid3_lib::Bin2Dec(substr($nRadioRgAdjustBitstring, 7, 9));
+					$thisfile_riff_raw_rgad_album['name']       = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 0, 3));
+					$thisfile_riff_raw_rgad_album['originator'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 3, 3));
+					$thisfile_riff_raw_rgad_album['signbit']    = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 6, 1));
+					$thisfile_riff_raw_rgad_album['adjustment'] = getid3_lib::Bin2Dec(substr($nAudiophileRgAdjustBitstring, 7, 9));
+
+					$thisfile_riff['rgad']['peakamplitude'] = $thisfile_riff_raw_rgad['fPeakAmplitude'];
+					if (($thisfile_riff_raw_rgad_track['name'] != 0) && ($thisfile_riff_raw_rgad_track['originator'] != 0)) {
+						$thisfile_riff['rgad']['track']['name']            = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_track['name']);
+						$thisfile_riff['rgad']['track']['originator']      = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_track['originator']);
+						$thisfile_riff['rgad']['track']['adjustment']      = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_track['adjustment'], $thisfile_riff_raw_rgad_track['signbit']);
+					}
+					if (($thisfile_riff_raw_rgad_album['name'] != 0) && ($thisfile_riff_raw_rgad_album['originator'] != 0)) {
+						$thisfile_riff['rgad']['album']['name']       = getid3_lib::RGADnameLookup($thisfile_riff_raw_rgad_album['name']);
+						$thisfile_riff['rgad']['album']['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_riff_raw_rgad_album['originator']);
+						$thisfile_riff['rgad']['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($thisfile_riff_raw_rgad_album['adjustment'], $thisfile_riff_raw_rgad_album['signbit']);
+					}
+				}
+
+				if (isset($thisfile_riff_WAVE['fact'][0]['data'])) {
+					$thisfile_riff_raw['fact']['NumberOfSamples'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_WAVE['fact'][0]['data'], 0, 4));
+
+					// This should be a good way of calculating exact playtime,
+					// but some sample files have had incorrect number of samples,
+					// so cannot use this method
+
+					// if (!empty($thisfile_riff_raw['fmt ']['nSamplesPerSec'])) {
+					//     $ThisFileInfo['playtime_seconds'] = (float) $thisfile_riff_raw['fact']['NumberOfSamples'] / $thisfile_riff_raw['fmt ']['nSamplesPerSec'];
+					// }
+				}
+				if (!empty($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'])) {
+					$thisfile_audio['bitrate'] = getid3_lib::CastAsInt($thisfile_riff_raw['fmt ']['nAvgBytesPerSec'] * 8);
+				}
+
+				if (isset($thisfile_riff_WAVE['bext'][0]['data'])) {
+					// shortcut
+					$thisfile_riff_WAVE_bext_0 = &$thisfile_riff_WAVE['bext'][0];
+
+					$thisfile_riff_WAVE_bext_0['title']          =                         trim(substr($thisfile_riff_WAVE_bext_0['data'],   0, 256));
+					$thisfile_riff_WAVE_bext_0['author']         =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 256,  32));
+					$thisfile_riff_WAVE_bext_0['reference']      =                         trim(substr($thisfile_riff_WAVE_bext_0['data'], 288,  32));
+					$thisfile_riff_WAVE_bext_0['origin_date']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 320,  10);
+					$thisfile_riff_WAVE_bext_0['origin_time']    =                              substr($thisfile_riff_WAVE_bext_0['data'], 330,   8);
+					$thisfile_riff_WAVE_bext_0['time_reference'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 338,   8));
+					$thisfile_riff_WAVE_bext_0['bwf_version']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 346,   1));
+					$thisfile_riff_WAVE_bext_0['reserved']       = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_bext_0['data'], 347, 254));
+					$thisfile_riff_WAVE_bext_0['coding_history'] =         explode("\r\n", trim(substr($thisfile_riff_WAVE_bext_0['data'], 601)));
+
+					$thisfile_riff_WAVE_bext_0['origin_date_unix'] = gmmktime(
+																				substr($thisfile_riff_WAVE_bext_0['origin_time'], 0, 2),
+																				substr($thisfile_riff_WAVE_bext_0['origin_time'], 3, 2),
+																				substr($thisfile_riff_WAVE_bext_0['origin_time'], 6, 2),
+																				substr($thisfile_riff_WAVE_bext_0['origin_date'], 5, 2),
+																				substr($thisfile_riff_WAVE_bext_0['origin_date'], 8, 2),
+																				substr($thisfile_riff_WAVE_bext_0['origin_date'], 0, 4));
+
+					$thisfile_riff['comments']['author'][] = $thisfile_riff_WAVE_bext_0['author'];
+					$thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_bext_0['title'];
+				}
+
+				if (isset($thisfile_riff_WAVE['MEXT'][0]['data'])) {
+					// shortcut
+					$thisfile_riff_WAVE_MEXT_0 = &$thisfile_riff_WAVE['MEXT'][0];
+
+					$thisfile_riff_WAVE_MEXT_0['raw']['sound_information']      = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 0, 2));
+					$thisfile_riff_WAVE_MEXT_0['flags']['homogenous']           = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0001);
+					if ($thisfile_riff_WAVE_MEXT_0['flags']['homogenous']) {
+						$thisfile_riff_WAVE_MEXT_0['flags']['padding']          = ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0002) ? false : true;
+						$thisfile_riff_WAVE_MEXT_0['flags']['22_or_44']         =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0004);
+						$thisfile_riff_WAVE_MEXT_0['flags']['free_format']      =        (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['sound_information'] & 0x0008);
+
+						$thisfile_riff_WAVE_MEXT_0['nominal_frame_size']        = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 2, 2));
+					}
+					$thisfile_riff_WAVE_MEXT_0['anciliary_data_length']         = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 6, 2));
+					$thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def']     = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_MEXT_0['data'], 8, 2));
+					$thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_left']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0001);
+					$thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_free']  = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0002);
+					$thisfile_riff_WAVE_MEXT_0['flags']['anciliary_data_right'] = (bool) ($thisfile_riff_WAVE_MEXT_0['raw']['anciliary_data_def'] & 0x0004);
+				}
+
+				if (isset($thisfile_riff_WAVE['cart'][0]['data'])) {
+					// shortcut
+					$thisfile_riff_WAVE_cart_0 = &$thisfile_riff_WAVE['cart'][0];
+
+					$thisfile_riff_WAVE_cart_0['version']              =                  substr($thisfile_riff_WAVE_cart_0['data'],    0,    4);
+					$thisfile_riff_WAVE_cart_0['title']                =             trim(substr($thisfile_riff_WAVE_cart_0['data'],    4,   64));
+					$thisfile_riff_WAVE_cart_0['artist']               =             trim(substr($thisfile_riff_WAVE_cart_0['data'],   68,   64));
+					$thisfile_riff_WAVE_cart_0['cut_id']               =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  132,   64));
+					$thisfile_riff_WAVE_cart_0['client_id']            =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  196,   64));
+					$thisfile_riff_WAVE_cart_0['category']             =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  260,   64));
+					$thisfile_riff_WAVE_cart_0['classification']       =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  324,   64));
+					$thisfile_riff_WAVE_cart_0['out_cue']              =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  388,   64));
+					$thisfile_riff_WAVE_cart_0['start_date']           =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  452,   10));
+					$thisfile_riff_WAVE_cart_0['start_time']           =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  462,    8));
+					$thisfile_riff_WAVE_cart_0['end_date']             =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  470,   10));
+					$thisfile_riff_WAVE_cart_0['end_time']             =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  480,    8));
+					$thisfile_riff_WAVE_cart_0['producer_app_id']      =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  488,   64));
+					$thisfile_riff_WAVE_cart_0['producer_app_version'] =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  552,   64));
+					$thisfile_riff_WAVE_cart_0['user_defined_text']    =             trim(substr($thisfile_riff_WAVE_cart_0['data'],  616,   64));
+					$thisfile_riff_WAVE_cart_0['zero_db_reference']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'],  680,    4), true);
+					for ($i = 0; $i < 8; $i++) {
+						$thisfile_riff_WAVE_cart_0['post_time'][$i]['usage_fourcc'] =                  substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8), 4);
+						$thisfile_riff_WAVE_cart_0['post_time'][$i]['timer_value']  = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE_cart_0['data'], 684 + ($i * 8) + 4, 4));
+					}
+					$thisfile_riff_WAVE_cart_0['url']              =                 trim(substr($thisfile_riff_WAVE_cart_0['data'],  748, 1024));
+					$thisfile_riff_WAVE_cart_0['tag_text']         = explode("\r\n", trim(substr($thisfile_riff_WAVE_cart_0['data'], 1772)));
+
+					$thisfile_riff['comments']['artist'][] = $thisfile_riff_WAVE_cart_0['artist'];
+					$thisfile_riff['comments']['title'][]  = $thisfile_riff_WAVE_cart_0['title'];
+				}
+
+				if (!isset($thisfile_audio['bitrate']) && isset($thisfile_riff_audio[$streamindex]['bitrate'])) {
+					$thisfile_audio['bitrate'] = $thisfile_riff_audio[$streamindex]['bitrate'];
+					$ThisFileInfo['playtime_seconds'] = (float) ((($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $thisfile_audio['bitrate']);
+				}
+
+				if (!empty($ThisFileInfo['wavpack'])) {
+					$thisfile_audio_dataformat = 'wavpack';
+					$thisfile_audio['bitrate_mode'] = 'vbr';
+					$thisfile_audio['encoder']      = 'WavPack v'.$ThisFileInfo['wavpack']['version'];
+
+					// Reset to the way it was - RIFF parsing will have messed this up
+					$thisfile_avdataend        = $Original['avdataend'];
+					$thisfile_audio['bitrate'] = (($thisfile_avdataend - $thisfile_avdataoffset) * 8) / $ThisFileInfo['playtime_seconds'];
+
+					fseek($fd, $thisfile_avdataoffset - 44, SEEK_SET);
+					$RIFFdata = fread($fd, 44);
+					$OrignalRIFFheaderSize = getid3_lib::LittleEndian2Int(substr($RIFFdata,  4, 4)) +  8;
+					$OrignalRIFFdataSize   = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
+
+					if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
+						$thisfile_avdataend -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
+						fseek($fd, $thisfile_avdataend, SEEK_SET);
+						$RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
+					}
+
+					// move the data chunk after all other chunks (if any)
+					// so that the RIFF parser doesn't see EOF when trying
+					// to skip over the data chunk
+					$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
+					getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
+				}
+
+				if (isset($thisfile_riff_raw['fmt ']['wFormatTag'])) {
+					switch ($thisfile_riff_raw['fmt ']['wFormatTag']) {
+						case 0x08AE: // ClearJump LiteWave
+							$thisfile_audio['bitrate_mode'] = 'vbr';
+							$thisfile_audio_dataformat   = 'litewave';
+
+							//typedef struct tagSLwFormat {
+							//  WORD    m_wCompFormat;     // low byte defines compression method, high byte is compression flags
+							//  DWORD   m_dwScale;         // scale factor for lossy compression
+							//  DWORD   m_dwBlockSize;     // number of samples in encoded blocks
+							//  WORD    m_wQuality;        // alias for the scale factor
+							//  WORD    m_wMarkDistance;   // distance between marks in bytes
+							//  WORD    m_wReserved;
+							//
+							//  //following paramters are ignored if CF_FILESRC is not set
+							//  DWORD   m_dwOrgSize;       // original file size in bytes
+							//  WORD    m_bFactExists;     // indicates if 'fact' chunk exists in the original file
+							//  DWORD   m_dwRiffChunkSize; // riff chunk size in the original file
+							//
+							//  PCMWAVEFORMAT m_OrgWf;     // original wave format
+							// }SLwFormat, *PSLwFormat;
+
+							// shortcut
+							$thisfile_riff['litewave']['raw'] = array();
+							$thisfile_riff_litewave     = &$thisfile_riff['litewave'];
+							$thisfile_riff_litewave_raw = &$thisfile_riff_litewave['raw'];
+
+							$thisfile_riff_litewave_raw['compression_method'] = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 18, 1));
+							$thisfile_riff_litewave_raw['compression_flags']  = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 19, 1));
+							$thisfile_riff_litewave_raw['m_dwScale']          = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 20, 4));
+							$thisfile_riff_litewave_raw['m_dwBlockSize']      = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 24, 4));
+							$thisfile_riff_litewave_raw['m_wQuality']         = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 28, 2));
+							$thisfile_riff_litewave_raw['m_wMarkDistance']    = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 30, 2));
+							$thisfile_riff_litewave_raw['m_wReserved']        = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 32, 2));
+							$thisfile_riff_litewave_raw['m_dwOrgSize']        = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 34, 4));
+							$thisfile_riff_litewave_raw['m_bFactExists']      = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 38, 2));
+							$thisfile_riff_litewave_raw['m_dwRiffChunkSize']  = getid3_lib::LittleEndian2Int(substr($thisfile_riff_WAVE['fmt '][0]['data'], 40, 4));
+
+							//$thisfile_riff_litewave['quality_factor'] = intval(round((2000 - $thisfile_riff_litewave_raw['m_dwScale']) / 20));
+							$thisfile_riff_litewave['quality_factor'] = $thisfile_riff_litewave_raw['m_wQuality'];
+
+							$thisfile_riff_litewave['flags']['raw_source']    = ($thisfile_riff_litewave_raw['compression_flags'] & 0x01) ? false : true;
+							$thisfile_riff_litewave['flags']['vbr_blocksize'] = ($thisfile_riff_litewave_raw['compression_flags'] & 0x02) ? false : true;
+							$thisfile_riff_litewave['flags']['seekpoints']    =        (bool) ($thisfile_riff_litewave_raw['compression_flags'] & 0x04);
+
+							$thisfile_audio['lossless']        = (($thisfile_riff_litewave_raw['m_wQuality'] == 100) ? true : false);
+							$thisfile_audio['encoder_options'] = '-q'.$thisfile_riff_litewave['quality_factor'];
+							break;
+
+						default:
+							break;
+					}
+				}
+				if ($thisfile_avdataend > $ThisFileInfo['filesize']) {
+					switch (@$thisfile_audio_dataformat) {
+						case 'wavpack': // WavPack
+						case 'lpac':    // LPAC
+						case 'ofr':     // OptimFROG
+						case 'ofs':     // OptimFROG DualStream
+							// lossless compressed audio formats that keep original RIFF headers - skip warning
+							break;
+
+						case 'litewave':
+							if (($thisfile_avdataend - $ThisFileInfo['filesize']) == 1) {
+								// LiteWave appears to incorrectly *not* pad actual output file
+								// to nearest WORD boundary so may appear to be short by one
+								// byte, in which case - skip warning
+							} else {
+								// Short by more than one byte, throw warning
+								$ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)';
+								$thisfile_avdataend = $ThisFileInfo['filesize'];
+							}
+							break;
+
+						default:
+							if ((($thisfile_avdataend - $ThisFileInfo['filesize']) == 1) && (($thisfile_riff[$RIFFsubtype]['data'][0]['size'] % 2) == 0) && ((($ThisFileInfo['filesize'] - $thisfile_avdataoffset) % 2) == 1)) {
+								// output file appears to be incorrectly *not* padded to nearest WORD boundary
+								// Output less severe warning
+								$ThisFileInfo['warning'][] = 'File should probably be padded to nearest WORD boundary, but it is not (expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' therefore short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)';
+								$thisfile_avdataend = $ThisFileInfo['filesize'];
+								break;
+							}
+							// Short by more than one byte, throw warning
+							$ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['data'][0]['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['data'][0]['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)';
+							$thisfile_avdataend = $ThisFileInfo['filesize'];
+							break;
+					}
+				}
+				if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes'])) {
+					if ((($thisfile_avdataend - $thisfile_avdataoffset) - $ThisFileInfo['mpeg']['audio']['LAME']['audio_bytes']) == 1) {
+						$thisfile_avdataend--;
+						$ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
+					}
+				}
+				if (@$thisfile_audio_dataformat == 'ac3') {
+					unset($thisfile_audio['bits_per_sample']);
+					if (!empty($ThisFileInfo['ac3']['bitrate']) && ($ThisFileInfo['ac3']['bitrate'] != $thisfile_audio['bitrate'])) {
+						$thisfile_audio['bitrate'] = $ThisFileInfo['ac3']['bitrate'];
+					}
+				}
+				break;
+
+			case 'AVI ':
+				$thisfile_video['bitrate_mode'] = 'vbr'; // maybe not, but probably
+				$thisfile_video['dataformat']   = 'avi';
+				$ThisFileInfo['mime_type']      = 'video/avi';
+
+				if (isset($thisfile_riff[$RIFFsubtype]['movi']['offset'])) {
+					$thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['movi']['offset'] + 8;
+					$thisfile_avdataend    = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['movi']['size'];
+					if ($thisfile_avdataend > $ThisFileInfo['filesize']) {
+						$ThisFileInfo['warning'][] = 'Probably truncated file - expecting '.$thisfile_riff[$RIFFsubtype]['movi']['size'].' bytes of data, only found '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' (short by '.($thisfile_riff[$RIFFsubtype]['movi']['size'] - ($ThisFileInfo['filesize'] - $thisfile_avdataoffset)).' bytes)';
+						$thisfile_avdataend = $ThisFileInfo['filesize'];
+					}
+				}
+
+				if (isset($thisfile_riff['AVI ']['hdrl']['strl']['indx'])) {
+					//$bIndexType = array(
+					//	0x00 => 'AVI_INDEX_OF_INDEXES',
+					//	0x01 => 'AVI_INDEX_OF_CHUNKS',
+					//	0x80 => 'AVI_INDEX_IS_DATA',
+					//);
+					//$bIndexSubtype = array(
+					//	0x01 => array(
+					//		0x01 => 'AVI_INDEX_2FIELD',
+					//	),
+					//);
+					foreach ($thisfile_riff['AVI ']['hdrl']['strl']['indx'] as $streamnumber => $steamdataarray) {
+						$thisfile_riff_avi_hdrl_strl_indx_stream_data = &$thisfile_riff['AVI ']['hdrl']['strl']['indx'][$streamnumber]['data'];
+
+						$thisfile_riff_raw['indx'][$streamnumber]['wLongsPerEntry'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data,  0, 2));
+						$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']  = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data,  2, 1));
+						$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']     = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data,  3, 1));
+						$thisfile_riff_raw['indx'][$streamnumber]['nEntriesInUse']  = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data,  4, 4));
+						$thisfile_riff_raw['indx'][$streamnumber]['dwChunkId']      =                                              substr($thisfile_riff_avi_hdrl_strl_indx_stream_data,  8, 4);
+						$thisfile_riff_raw['indx'][$streamnumber]['dwReserved']     = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_avi_hdrl_strl_indx_stream_data, 12, 4));
+
+						//$thisfile_riff_raw['indx'][$streamnumber]['bIndexType_name']    =    @$bIndexType[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']];
+						//$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType_name'] = @$bIndexSubtype[$thisfile_riff_raw['indx'][$streamnumber]['bIndexType']][$thisfile_riff_raw['indx'][$streamnumber]['bIndexSubType']];
+
+						unset($thisfile_riff_avi_hdrl_strl_indx_stream_data);
+					}
+				}
+				if (isset($thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'])) {
+					$avihData = $thisfile_riff['AVI ']['hdrl']['avih'][$streamindex]['data'];
+
+					// shortcut
+					$thisfile_riff_raw['avih'] = array();
+					$thisfile_riff_raw_avih = &$thisfile_riff_raw['avih'];
+
+					$thisfile_riff_raw_avih['dwMicroSecPerFrame']    = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData,  0, 4)); // frame display rate (or 0L)
+					if ($thisfile_riff_raw_avih['dwMicroSecPerFrame'] == 0) {
+						$ThisFileInfo['error'][] = 'Corrupt RIFF file: avih.dwMicroSecPerFrame == zero';
+						return false;
+					}
+					$thisfile_riff_raw_avih['dwMaxBytesPerSec']      = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData,  4, 4)); // max. transfer rate
+					$thisfile_riff_raw_avih['dwPaddingGranularity']  = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData,  8, 4)); // pad to multiples of this size; normally 2K.
+					$thisfile_riff_raw_avih['dwFlags']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 12, 4)); // the ever-present flags
+					$thisfile_riff_raw_avih['dwTotalFrames']         = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 16, 4)); // # frames in file
+					$thisfile_riff_raw_avih['dwInitialFrames']       = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 20, 4));
+					$thisfile_riff_raw_avih['dwStreams']             = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 24, 4));
+					$thisfile_riff_raw_avih['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 28, 4));
+					$thisfile_riff_raw_avih['dwWidth']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 32, 4));
+					$thisfile_riff_raw_avih['dwHeight']              = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 36, 4));
+					$thisfile_riff_raw_avih['dwScale']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 40, 4));
+					$thisfile_riff_raw_avih['dwRate']                = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 44, 4));
+					$thisfile_riff_raw_avih['dwStart']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 48, 4));
+					$thisfile_riff_raw_avih['dwLength']              = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($avihData, 52, 4));
+
+					$thisfile_riff_raw_avih['flags']['hasindex']     = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000010);
+					$thisfile_riff_raw_avih['flags']['mustuseindex'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000020);
+					$thisfile_riff_raw_avih['flags']['interleaved']  = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000100);
+					$thisfile_riff_raw_avih['flags']['trustcktype']  = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00000800);
+					$thisfile_riff_raw_avih['flags']['capturedfile'] = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00010000);
+					$thisfile_riff_raw_avih['flags']['copyrighted']  = (bool) ($thisfile_riff_raw_avih['dwFlags'] & 0x00020010);
+
+					// shortcut
+					$thisfile_riff_video[$streamindex] = array();
+					$thisfile_riff_video_current = &$thisfile_riff_video[$streamindex];
+
+					if ($thisfile_riff_raw_avih['dwWidth'] > 0) {
+						$thisfile_riff_video_current['frame_width'] = $thisfile_riff_raw_avih['dwWidth'];
+						$thisfile_video['resolution_x']             = $thisfile_riff_video_current['frame_width'];
+					}
+					if ($thisfile_riff_raw_avih['dwHeight'] > 0) {
+						$thisfile_riff_video_current['frame_height'] = $thisfile_riff_raw_avih['dwHeight'];
+						$thisfile_video['resolution_y']              = $thisfile_riff_video_current['frame_height'];
+					}
+					if ($thisfile_riff_raw_avih['dwTotalFrames'] > 0) {
+						$thisfile_riff_video_current['total_frames'] = $thisfile_riff_raw_avih['dwTotalFrames'];
+						$thisfile_video['total_frames']              = $thisfile_riff_video_current['total_frames'];
+					}
+
+					$thisfile_riff_video_current['frame_rate'] = round(1000000 / $thisfile_riff_raw_avih['dwMicroSecPerFrame'], 3);
+					$thisfile_video['frame_rate'] = $thisfile_riff_video_current['frame_rate'];
+				}
+				if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][0]['data'])) {
+					if (is_array($thisfile_riff['AVI ']['hdrl']['strl']['strh'])) {
+						for ($i = 0; $i < count($thisfile_riff['AVI ']['hdrl']['strl']['strh']); $i++) {
+							if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'])) {
+								$strhData = $thisfile_riff['AVI ']['hdrl']['strl']['strh'][$i]['data'];
+								$strhfccType = substr($strhData,  0, 4);
+
+								if (isset($thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'])) {
+									$strfData = $thisfile_riff['AVI ']['hdrl']['strl']['strf'][$i]['data'];
+
+									// shortcut
+									$thisfile_riff_raw_strf_strhfccType_streamindex = &$thisfile_riff_raw['strf'][$strhfccType][$streamindex];
+
+									switch ($strhfccType) {
+										case 'auds':
+											$thisfile_audio['bitrate_mode'] = 'cbr';
+											$thisfile_audio_dataformat      = 'wav';
+											if (isset($thisfile_riff_audio) && is_array($thisfile_riff_audio)) {
+												$streamindex = count($thisfile_riff_audio);
+											}
+
+											$thisfile_riff_audio[$streamindex] = getid3_riff::RIFFparseWAVEFORMATex($strfData);
+											$thisfile_audio['wformattag'] = $thisfile_riff_audio[$streamindex]['raw']['wFormatTag'];
+
+											// shortcut
+											$thisfile_audio['streams'][$streamindex] = $thisfile_riff_audio[$streamindex];
+											$thisfile_audio_streams_currentstream = &$thisfile_audio['streams'][$streamindex];
+
+											if ($thisfile_audio_streams_currentstream['bits_per_sample'] == 0) {
+												unset($thisfile_audio_streams_currentstream['bits_per_sample']);
+											}
+											$thisfile_audio_streams_currentstream['wformattag'] = $thisfile_audio_streams_currentstream['raw']['wFormatTag'];
+											unset($thisfile_audio_streams_currentstream['raw']);
+
+											// shortcut
+											$thisfile_riff_raw['strf'][$strhfccType][$streamindex] = $thisfile_riff_audio[$streamindex]['raw'];
+
+											unset($thisfile_riff_audio[$streamindex]['raw']);
+											$thisfile_audio = getid3_lib::array_merge_noclobber($thisfile_audio, $thisfile_riff_audio[$streamindex]);
+
+											$thisfile_audio['lossless'] = false;
+											switch ($thisfile_riff_raw_strf_strhfccType_streamindex['wFormatTag']) {
+												case 0x0001:  // PCM
+													$thisfile_audio_dataformat  = 'wav';
+													$thisfile_audio['lossless'] = true;
+													break;
+
+												case 0x0050: // MPEG Layer 2 or Layer 1
+													$thisfile_audio_dataformat = 'mp2'; // Assume Layer-2
+													break;
+
+												case 0x0055: // MPEG Layer 3
+													$thisfile_audio_dataformat = 'mp3';
+													break;
+
+												case 0x00FF: // AAC
+													$thisfile_audio_dataformat = 'aac';
+													break;
+
+												case 0x0161: // Windows Media v7 / v8 / v9
+												case 0x0162: // Windows Media Professional v9
+												case 0x0163: // Windows Media Lossess v9
+													$thisfile_audio_dataformat = 'wma';
+													break;
+
+												case 0x2000: // AC-3
+													$thisfile_audio_dataformat = 'ac3';
+													break;
+
+												case 0x2001: // DTS
+													$thisfile_audio_dataformat = 'dts';
+													break;
+
+												default:
+													$thisfile_audio_dataformat = 'wav';
+													break;
+											}
+											$thisfile_audio_streams_currentstream['dataformat']   = $thisfile_audio_dataformat;
+											$thisfile_audio_streams_currentstream['lossless']     = $thisfile_audio['lossless'];
+											$thisfile_audio_streams_currentstream['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
+											break;
+
+
+										case 'iavs':
+										case 'vids':
+											// shortcut
+											$thisfile_riff_raw['strh'][$i]                  = array();
+											$thisfile_riff_raw_strh_current                 = &$thisfile_riff_raw['strh'][$i];
+
+											$thisfile_riff_raw_strh_current['fccType']               =                  substr($strhData,  0, 4);  // same as $strhfccType;
+											$thisfile_riff_raw_strh_current['fccHandler']            =                  substr($strhData,  4, 4);
+											$thisfile_riff_raw_strh_current['dwFlags']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData,  8, 4)); // Contains AVITF_* flags
+											$thisfile_riff_raw_strh_current['wPriority']             = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 12, 2));
+											$thisfile_riff_raw_strh_current['wLanguage']             = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 14, 2));
+											$thisfile_riff_raw_strh_current['dwInitialFrames']       = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 16, 4));
+											$thisfile_riff_raw_strh_current['dwScale']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 20, 4));
+											$thisfile_riff_raw_strh_current['dwRate']                = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 24, 4));
+											$thisfile_riff_raw_strh_current['dwStart']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 28, 4));
+											$thisfile_riff_raw_strh_current['dwLength']              = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 32, 4));
+											$thisfile_riff_raw_strh_current['dwSuggestedBufferSize'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 36, 4));
+											$thisfile_riff_raw_strh_current['dwQuality']             = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 40, 4));
+											$thisfile_riff_raw_strh_current['dwSampleSize']          = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 44, 4));
+											$thisfile_riff_raw_strh_current['rcFrame']               = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strhData, 48, 4));
+
+											$thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strh_current['fccHandler']);
+											$thisfile_video['fourcc']             = $thisfile_riff_raw_strh_current['fccHandler'];
+											if (!$thisfile_riff_video_current['codec'] && isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
+												$thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
+												$thisfile_video['fourcc']             = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
+											}
+											$thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
+											$thisfile_video['pixel_aspect_ratio'] = (float) 1;
+											switch ($thisfile_riff_raw_strh_current['fccHandler']) {
+												case 'HFYU': // Huffman Lossless Codec
+												case 'IRAW': // Intel YUV Uncompressed
+												case 'YUY2': // Uncompressed YUV 4:2:2
+													$thisfile_video['lossless'] = true;
+													break;
+
+												default:
+													$thisfile_video['lossless'] = false;
+													break;
+											}
+
+											switch ($strhfccType) {
+												case 'vids':
+													$thisfile_riff_raw_strf_strhfccType_streamindex = getid3_riff::ParseBITMAPINFOHEADER(substr($strfData, 0, 40), ($ThisFileInfo['fileformat'] == 'riff'));
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biSize']          = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData,  0, 4)); // number of bytes required by the BITMAPINFOHEADER structure
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biWidth']         = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData,  4, 4)); // width of the bitmap in pixels
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biHeight']        = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData,  8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biPlanes']        = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount']      = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 14, 2)); // Specifies the number of bits per pixels
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']          =                                              substr($strfData, 16, 4);  //
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biSizeImage']     = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biXPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 24, 4)); // horizontal resolution, in pixels per metre, of the target device
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biYPelsPerMeter'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 28, 4)); // vertical resolution, in pixels per metre, of the target device
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biClrUsed']       = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression
+													//$thisfile_riff_raw_strf_strhfccType_streamindex['biClrImportant']  = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($strfData, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important
+
+													$thisfile_video['bits_per_sample'] = $thisfile_riff_raw_strf_strhfccType_streamindex['biBitCount'];
+
+													if ($thisfile_riff_video_current['codec'] == 'DV') {
+														$thisfile_riff_video_current['dv_type'] = 2;
+													}
+													break;
+
+												case 'iavs':
+													$thisfile_riff_video_current['dv_type'] = 1;
+													break;
+											}
+											break;
+
+										default:
+											$ThisFileInfo['warning'][] = 'Unhandled fccType for stream ('.$i.'): "'.$strhfccType.'"';
+											break;
+
+									}
+								}
+							}
+
+							if (isset($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) && getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'])) {
+
+								$thisfile_riff_video_current['codec'] = getid3_riff::RIFFfourccLookup($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']);
+								$thisfile_video['codec']              = $thisfile_riff_video_current['codec'];
+								$thisfile_video['fourcc']             = $thisfile_riff_raw_strf_strhfccType_streamindex['fourcc'];
+
+								switch ($thisfile_riff_raw_strf_strhfccType_streamindex['fourcc']) {
+									case 'HFYU': // Huffman Lossless Codec
+									case 'IRAW': // Intel YUV Uncompressed
+									case 'YUY2': // Uncompressed YUV 4:2:2
+										$thisfile_video['lossless']        = true;
+										$thisfile_video['bits_per_sample'] = 24;
+										break;
+
+									default:
+										$thisfile_video['lossless']        = false;
+										$thisfile_video['bits_per_sample'] = 24;
+										break;
+								}
+
+							}
+						}
+					}
+				}
+				break;
+
+			case 'CDDA':
+				$thisfile_audio['bitrate_mode'] = 'cbr';
+				$thisfile_audio_dataformat      = 'cda';
+				$thisfile_audio['lossless']     = true;
+				unset($ThisFileInfo['mime_type']);
+
+				$thisfile_avdataoffset = 44;
+
+				if (isset($thisfile_riff['CDDA']['fmt '][0]['data'])) {
+					// shortcut
+					$thisfile_riff_CDDA_fmt_0 = &$thisfile_riff['CDDA']['fmt '][0];
+
+					$thisfile_riff_CDDA_fmt_0['unknown1']           = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'],  0, 2));
+					$thisfile_riff_CDDA_fmt_0['track_num']          = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'],  2, 2));
+					$thisfile_riff_CDDA_fmt_0['disc_id']            = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'],  4, 4));
+					$thisfile_riff_CDDA_fmt_0['start_offset_frame'] = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'],  8, 4));
+					$thisfile_riff_CDDA_fmt_0['playtime_frames']    = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 12, 4));
+					$thisfile_riff_CDDA_fmt_0['unknown6']           = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 16, 4));
+					$thisfile_riff_CDDA_fmt_0['unknown7']           = getid3_riff::EitherEndian2Int($ThisFileInfo, substr($thisfile_riff_CDDA_fmt_0['data'], 20, 4));
+
+					$thisfile_riff_CDDA_fmt_0['start_offset_seconds'] = (float) $thisfile_riff_CDDA_fmt_0['start_offset_frame'] / 75;
+					$thisfile_riff_CDDA_fmt_0['playtime_seconds']     = (float) $thisfile_riff_CDDA_fmt_0['playtime_frames'] / 75;
+					$ThisFileInfo['comments']['track']                = $thisfile_riff_CDDA_fmt_0['track_num'];
+					$ThisFileInfo['playtime_seconds']                 = $thisfile_riff_CDDA_fmt_0['playtime_seconds'];
+
+					// hardcoded data for CD-audio
+					$thisfile_audio['sample_rate']     = 44100;
+					$thisfile_audio['channels']        = 2;
+					$thisfile_audio['bits_per_sample'] = 16;
+					$thisfile_audio['bitrate']         = $thisfile_audio['sample_rate'] * $thisfile_audio['channels'] * $thisfile_audio['bits_per_sample'];
+					$thisfile_audio['bitrate_mode']    = 'cbr';
+				}
+				break;
+
+
+			case 'AIFF':
+			case 'AIFC':
+				$thisfile_audio['bitrate_mode'] = 'cbr';
+				$thisfile_audio_dataformat      = 'aiff';
+				$thisfile_audio['lossless']     = true;
+				$ThisFileInfo['mime_type']      = 'audio/x-aiff';
+
+				if (isset($thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'])) {
+					$thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['SSND'][0]['offset'] + 8;
+					$thisfile_avdataend    = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['SSND'][0]['size'];
+					if ($thisfile_avdataend > $ThisFileInfo['filesize']) {
+						if (($thisfile_avdataend == ($ThisFileInfo['filesize'] + 1)) && (($ThisFileInfo['filesize'] % 2) == 1)) {
+							// structures rounded to 2-byte boundary, but dumb encoders
+							// forget to pad end of file to make this actually work
+						} else {
+							$ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['SSND'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' bytes found';
+						}
+						$thisfile_avdataend = $ThisFileInfo['filesize'];
+					}
+				}
+
+				if (isset($thisfile_riff[$RIFFsubtype]['COMM'][0]['data'])) {
+
+					// shortcut
+					$thisfile_riff_RIFFsubtype_COMM_0_data = &$thisfile_riff[$RIFFsubtype]['COMM'][0]['data'];
+
+					$thisfile_riff_audio['channels']         =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  0,  2), true);
+					$thisfile_riff_audio['total_samples']    =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  2,  4), false);
+					$thisfile_riff_audio['bits_per_sample']  =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  6,  2), true);
+					$thisfile_riff_audio['sample_rate']      = (int) getid3_lib::BigEndian2Float(substr($thisfile_riff_RIFFsubtype_COMM_0_data,  8, 10));
+
+					if ($thisfile_riff[$RIFFsubtype]['COMM'][0]['size'] > 18) {
+						$thisfile_riff_audio['codec_fourcc'] =                                   substr($thisfile_riff_RIFFsubtype_COMM_0_data, 18,  4);
+						$CodecNameSize                       =         getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_COMM_0_data, 22,  1), false);
+						$thisfile_riff_audio['codec_name']   =                                   substr($thisfile_riff_RIFFsubtype_COMM_0_data, 23,  $CodecNameSize);
+						switch ($thisfile_riff_audio['codec_name']) {
+							case 'NONE':
+								$thisfile_audio['codec']    = 'Pulse Code Modulation (PCM)';
+								$thisfile_audio['lossless'] = true;
+								break;
+
+							case '':
+								switch ($thisfile_riff_audio['codec_fourcc']) {
+									// http://developer.apple.com/qa/snd/snd07.html
+									case 'sowt':
+										$thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Little-Endian PCM';
+										$thisfile_audio['lossless'] = true;
+										break;
+
+									case 'twos':
+										$thisfile_riff_audio['codec_name'] = 'Two\'s Compliment Big-Endian PCM';
+										$thisfile_audio['lossless'] = true;
+										break;
+
+									default:
+										break;
+								}
+								break;
+
+							default:
+								$thisfile_audio['codec']    = $thisfile_riff_audio['codec_name'];
+								$thisfile_audio['lossless'] = false;
+								break;
+						}
+					}
+
+					$thisfile_audio['channels']        = $thisfile_riff_audio['channels'];
+					if ($thisfile_riff_audio['bits_per_sample'] > 0) {
+						$thisfile_audio['bits_per_sample'] = $thisfile_riff_audio['bits_per_sample'];
+					}
+					$thisfile_audio['sample_rate']     = $thisfile_riff_audio['sample_rate'];
+					if ($thisfile_audio['sample_rate'] == 0) {
+						$ThisFileInfo['error'][] = 'Corrupted AIFF file: sample_rate == zero';
+						return false;
+					}
+					$ThisFileInfo['playtime_seconds'] = $thisfile_riff_audio['total_samples'] / $thisfile_audio['sample_rate'];
+				}
+
+				if (isset($thisfile_riff[$RIFFsubtype]['COMT'])) {
+					$offset = 0;
+					$CommentCount                                           = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
+					$offset += 2;
+					for ($i = 0; $i < $CommentCount; $i++) {
+						$ThisFileInfo['comments_raw'][$i]['timestamp']      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 4), false);
+						$offset += 4;
+						$ThisFileInfo['comments_raw'][$i]['marker_id']      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), true);
+						$offset += 2;
+						$CommentLength                                      = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, 2), false);
+						$offset += 2;
+						$ThisFileInfo['comments_raw'][$i]['comment']        =                           substr($thisfile_riff[$RIFFsubtype]['COMT'][0]['data'], $offset, $CommentLength);
+						$offset += $CommentLength;
+
+						$ThisFileInfo['comments_raw'][$i]['timestamp_unix'] = getid3_lib::DateMac2Unix($ThisFileInfo['comments_raw'][$i]['timestamp']);
+						$thisfile_riff['comments']['comment'][] = $ThisFileInfo['comments_raw'][$i]['comment'];
+					}
+				}
+
+				$CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
+				foreach ($CommentsChunkNames as $key => $value) {
+					if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
+						$thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
+					}
+				}
+				break;
+
+			case '8SVX':
+				$thisfile_audio['bitrate_mode']    = 'cbr';
+				$thisfile_audio_dataformat         = '8svx';
+				$thisfile_audio['bits_per_sample'] = 8;
+				$thisfile_audio['channels']        = 1; // overridden below, if need be
+				$ThisFileInfo['mime_type']                = 'audio/x-aiff';
+
+				if (isset($thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'])) {
+					$thisfile_avdataoffset = $thisfile_riff[$RIFFsubtype]['BODY'][0]['offset'] + 8;
+					$thisfile_avdataend    = $thisfile_avdataoffset + $thisfile_riff[$RIFFsubtype]['BODY'][0]['size'];
+					if ($thisfile_avdataend > $ThisFileInfo['filesize']) {
+						$ThisFileInfo['warning'][] = 'Probable truncated AIFF file: expecting '.$thisfile_riff[$RIFFsubtype]['BODY'][0]['size'].' bytes of audio data, only '.($ThisFileInfo['filesize'] - $thisfile_avdataoffset).' bytes found';
+					}
+				}
+
+				if (isset($thisfile_riff[$RIFFsubtype]['VHDR'][0]['offset'])) {
+					// shortcut
+					$thisfile_riff_RIFFsubtype_VHDR_0 = &$thisfile_riff[$RIFFsubtype]['VHDR'][0];
+
+					$thisfile_riff_RIFFsubtype_VHDR_0['oneShotHiSamples']  =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  0, 4));
+					$thisfile_riff_RIFFsubtype_VHDR_0['repeatHiSamples']   =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  4, 4));
+					$thisfile_riff_RIFFsubtype_VHDR_0['samplesPerHiCycle'] =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'],  8, 4));
+					$thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec']     =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 12, 2));
+					$thisfile_riff_RIFFsubtype_VHDR_0['ctOctave']          =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 14, 1));
+					$thisfile_riff_RIFFsubtype_VHDR_0['sCompression']      =   getid3_lib::BigEndian2Int(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 15, 1));
+					$thisfile_riff_RIFFsubtype_VHDR_0['Volume']            = getid3_lib::FixedPoint16_16(substr($thisfile_riff_RIFFsubtype_VHDR_0['data'], 16, 4));
+
+					$thisfile_audio['sample_rate'] = $thisfile_riff_RIFFsubtype_VHDR_0['samplesPerSec'];
+
+					switch ($thisfile_riff_RIFFsubtype_VHDR_0['sCompression']) {
+						case 0:
+							$thisfile_audio['codec']    = 'Pulse Code Modulation (PCM)';
+							$thisfile_audio['lossless'] = true;
+							$ActualBitsPerSample               = 8;
+							break;
+
+						case 1:
+							$thisfile_audio['codec']    = 'Fibonacci-delta encoding';
+							$thisfile_audio['lossless'] = false;
+							$ActualBitsPerSample               = 4;
+							break;
+
+						default:
+							$ThisFileInfo['warning'][] = 'Unexpected sCompression value in 8SVX.VHDR chunk - expecting 0 or 1, found "'.sCompression.'"';
+							break;
+					}
+				}
+
+				if (isset($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'])) {
+					$ChannelsIndex = getid3_lib::BigEndian2Int(substr($thisfile_riff[$RIFFsubtype]['CHAN'][0]['data'], 0, 4));
+					switch ($ChannelsIndex) {
+						case 6: // Stereo
+							$thisfile_audio['channels'] = 2;
+							break;
+
+						case 2: // Left channel only
+						case 4: // Right channel only
+							$thisfile_audio['channels'] = 1;
+							break;
+
+						default:
+							$ThisFileInfo['warning'][] = 'Unexpected value in 8SVX.CHAN chunk - expecting 2 or 4 or 6, found "'.$ChannelsIndex.'"';
+							break;
+					}
+
+				}
+
+				$CommentsChunkNames = array('NAME'=>'title', 'author'=>'artist', '(c) '=>'copyright', 'ANNO'=>'comment');
+				foreach ($CommentsChunkNames as $key => $value) {
+					if (isset($thisfile_riff[$RIFFsubtype][$key][0]['data'])) {
+						$thisfile_riff['comments'][$value][] = $thisfile_riff[$RIFFsubtype][$key][0]['data'];
+					}
+				}
+
+				$thisfile_audio['bitrate'] = $thisfile_audio['sample_rate'] * $ActualBitsPerSample * $thisfile_audio['channels'];
+				if (!empty($thisfile_audio['bitrate'])) {
+					$ThisFileInfo['playtime_seconds'] = ($thisfile_avdataend - $thisfile_avdataoffset) / ($thisfile_audio['bitrate'] / 8);
+				}
+				break;
+
+
+			case 'CDXA':
+				$ThisFileInfo['mime_type']      = 'video/mpeg';
+				if (!empty($thisfile_riff['CDXA']['data'][0]['size'])) {
+					$GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
+					if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.mpeg.php', __FILE__, false)) {
+						$dummy = $ThisFileInfo;
+						$dummy['error'] = array();
+						$mpeg_scanner = new getid3_mpeg($fd, $dummy);
+						if (empty($dummy['error'])) {
+							$ThisFileInfo['audio']   = $dummy['audio'];
+							$ThisFileInfo['video']   = $dummy['video'];
+							$ThisFileInfo['mpeg']    = $dummy['mpeg'];
+							$ThisFileInfo['warning'] = $dummy['warning'];
+						}
+						unset($mpeg_scanner);
+					}
+				}
+				break;
+
+
+			default:
+				$ThisFileInfo['error'][] = 'Unknown RIFF type: expecting one of (WAVE|RMP3|AVI |CDDA|AIFF|AIFC|8SVX|CDXA), found "'.$RIFFsubtype.'" instead';
+				unset($ThisFileInfo['fileformat']);
+				break;
+		}
+
+		if (@$thisfile_riff_raw['fmt ']['wFormatTag'] == 1) {
+			// http://www.mega-nerd.com/erikd/Blog/Windiots/dts.html
+			fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+			$FirstFourBytes = fread($fd, 4);
+			if (preg_match('/^\xFF\x1F\x00\xE8/s', $FirstFourBytes)) {
+				// DTSWAV
+				$thisfile_audio_dataformat = 'dts';
+			} elseif (preg_match('/^\x7F\xFF\x80\x01/s', $FirstFourBytes)) {
+				// DTS, but this probably shouldn't happen
+				$thisfile_audio_dataformat = 'dts';
+			}
+		}
+
+
+		if (isset($thisfile_riff_WAVE['DISP']) && is_array($thisfile_riff_WAVE['DISP'])) {
+			$thisfile_riff['comments']['title'][] = trim(substr($thisfile_riff_WAVE['DISP'][count($thisfile_riff_WAVE['DISP']) - 1]['data'], 4));
+		}
+		if (isset($thisfile_riff_WAVE['INFO']) && is_array($thisfile_riff_WAVE['INFO'])) {
+			$this->RIFFcommentsParse($thisfile_riff_WAVE['INFO'], $thisfile_riff['comments']);
+		}
+
+		if (empty($thisfile_audio['encoder']) && !empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) {
+			$thisfile_audio['encoder'] = $ThisFileInfo['mpeg']['audio']['LAME']['short_version'];
+		}
+
+		if (!isset($ThisFileInfo['playtime_seconds'])) {
+			$ThisFileInfo['playtime_seconds'] = 0;
+		}
+		if (isset($thisfile_riff_raw['avih']['dwTotalFrames']) && isset($thisfile_riff_raw['avih']['dwMicroSecPerFrame'])) {
+			$ThisFileInfo['playtime_seconds'] = $thisfile_riff_raw['avih']['dwTotalFrames'] * ($thisfile_riff_raw['avih']['dwMicroSecPerFrame'] / 1000000);
+		}
+
+		if ($ThisFileInfo['playtime_seconds'] > 0) {
+			if (isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
+
+				if (!isset($ThisFileInfo['bitrate'])) {
+					$ThisFileInfo['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8);
+				}
+
+			} elseif (isset($thisfile_riff_audio) && !isset($thisfile_riff_video)) {
+
+				if (!isset($thisfile_audio['bitrate'])) {
+					$thisfile_audio['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8);
+				}
+
+			} elseif (!isset($thisfile_riff_audio) && isset($thisfile_riff_video)) {
+
+				if (!isset($thisfile_video['bitrate'])) {
+					$thisfile_video['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8);
+				}
+
+			}
+		}
+
+
+		if (isset($thisfile_riff_video) && isset($thisfile_audio['bitrate']) && ($thisfile_audio['bitrate'] > 0) && ($ThisFileInfo['playtime_seconds'] > 0)) {
+
+			$ThisFileInfo['bitrate'] = ((($thisfile_avdataend - $thisfile_avdataoffset) / $ThisFileInfo['playtime_seconds']) * 8);
+			$thisfile_audio['bitrate'] = 0;
+			$thisfile_video['bitrate'] = $ThisFileInfo['bitrate'];
+			foreach ($thisfile_riff_audio as $channelnumber => $audioinfoarray) {
+				$thisfile_video['bitrate'] -= $audioinfoarray['bitrate'];
+				$thisfile_audio['bitrate'] += $audioinfoarray['bitrate'];
+			}
+			if ($thisfile_video['bitrate'] <= 0) {
+				unset($thisfile_video['bitrate']);
+			}
+			if ($thisfile_audio['bitrate'] <= 0) {
+				unset($thisfile_audio['bitrate']);
+			}
+		}
+
+		if (isset($ThisFileInfo['mpeg']['audio'])) {
+			$thisfile_audio_dataformat      = 'mp'.$ThisFileInfo['mpeg']['audio']['layer'];
+			$thisfile_audio['sample_rate']  = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+			$thisfile_audio['channels']     = $ThisFileInfo['mpeg']['audio']['channels'];
+			$thisfile_audio['bitrate']      = $ThisFileInfo['mpeg']['audio']['bitrate'];
+			$thisfile_audio['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
+			if (!empty($ThisFileInfo['mpeg']['audio']['codec'])) {
+				$thisfile_audio['codec'] = $ThisFileInfo['mpeg']['audio']['codec'].' '.$thisfile_audio['codec'];
+			}
+			if (!empty($thisfile_audio['streams'])) {
+				foreach ($thisfile_audio['streams'] as $streamnumber => $streamdata) {
+					if ($streamdata['dataformat'] == $thisfile_audio_dataformat) {
+						$thisfile_audio['streams'][$streamnumber]['sample_rate']  = $thisfile_audio['sample_rate'];
+						$thisfile_audio['streams'][$streamnumber]['channels']     = $thisfile_audio['channels'];
+						$thisfile_audio['streams'][$streamnumber]['bitrate']      = $thisfile_audio['bitrate'];
+						$thisfile_audio['streams'][$streamnumber]['bitrate_mode'] = $thisfile_audio['bitrate_mode'];
+						$thisfile_audio['streams'][$streamnumber]['codec']        = $thisfile_audio['codec'];
+					}
+				}
+			}
+			$thisfile_audio['encoder_options'] = getid3_mp3::GuessEncoderOptions($ThisFileInfo);
+		}
+
+
+		if (!empty($thisfile_riff_raw['fmt ']['wBitsPerSample']) && ($thisfile_riff_raw['fmt ']['wBitsPerSample'] > 0)) {
+			switch ($thisfile_audio_dataformat) {
+				case 'ac3':
+					// ignore bits_per_sample
+					break;
+
+				default:
+					$thisfile_audio['bits_per_sample'] = $thisfile_riff_raw['fmt ']['wBitsPerSample'];
+					break;
+			}
+		}
+
+
+		if (empty($thisfile_riff_raw)) {
+			unset($thisfile_riff['raw']);
+		}
+		if (empty($thisfile_riff_audio)) {
+			unset($thisfile_riff['audio']);
+		}
+		if (empty($thisfile_riff_video)) {
+			unset($thisfile_riff['video']);
+		}
+
+		return true;
+	}
+
+
+	function RIFFcommentsParse(&$RIFFinfoArray, &$CommentsTargetArray) {
+		$RIFFinfoKeyLookup = array(
+			'IARL'=>'archivallocation',
+			'IART'=>'artist',
+			'ICDS'=>'costumedesigner',
+			'ICMS'=>'commissionedby',
+			'ICMT'=>'comment',
+			'ICNT'=>'country',
+			'ICOP'=>'copyright',
+			'ICRD'=>'creationdate',
+			'IDIM'=>'dimensions',
+			'IDIT'=>'digitizationdate',
+			'IDPI'=>'resolution',
+			'IDST'=>'distributor',
+			'IEDT'=>'editor',
+			'IENG'=>'engineers',
+			'IFRM'=>'accountofparts',
+			'IGNR'=>'genre',
+			'IKEY'=>'keywords',
+			'ILGT'=>'lightness',
+			'ILNG'=>'language',
+			'IMED'=>'orignalmedium',
+			'IMUS'=>'composer',
+			'INAM'=>'title',
+			'IPDS'=>'productiondesigner',
+			'IPLT'=>'palette',
+			'IPRD'=>'product',
+			'IPRO'=>'producer',
+			'IPRT'=>'part',
+			'IRTD'=>'rating',
+			'ISBJ'=>'subject',
+			'ISFT'=>'software',
+			'ISGN'=>'secondarygenre',
+			'ISHP'=>'sharpness',
+			'ISRC'=>'sourcesupplier',
+			'ISRF'=>'digitizationsource',
+			'ISTD'=>'productionstudio',
+			'ISTR'=>'starring',
+			'ITCH'=>'encoded_by',
+			'IWEB'=>'url',
+			'IWRI'=>'writer'
+		);
+		foreach ($RIFFinfoKeyLookup as $key => $value) {
+			if (isset($RIFFinfoArray[$key])) {
+				foreach ($RIFFinfoArray[$key] as $commentid => $commentdata) {
+					if (trim($commentdata['data']) != '') {
+						@$CommentsTargetArray[$value][] = trim($commentdata['data']);
+					}
+				}
+			}
+		}
+		return true;
+	}
+
+	function ParseRIFF(&$fd, $startoffset, $maxoffset, &$ThisFileInfo) {
+		$maxoffset = min($maxoffset, $ThisFileInfo['avdataend']);
+
+		$RIFFchunk = false;
+		$FoundAllChunksWeNeed = false;
+
+		if (($startoffset < 0) || ($startoffset >= pow(2, 31))) {
+			$ThisFileInfo['warning'][] = 'Unable to ParseRIFF() at '.$startoffset.' because beyond 2GB limit of PHP filesystem functions';
+			return false;
+		}
+		fseek($fd, $startoffset, SEEK_SET);
+
+		while (ftell($fd) < $maxoffset) {
+			$chunkname = fread($fd, 4);
+			if (strlen($chunkname) < 4) {
+				$ThisFileInfo['error'][] = 'Expecting chunk name at offset '.(ftell($fd) - 4).' but found nothing. Aborting RIFF parsing.';
+				break;
+			}
+
+			$chunksize = getid3_riff::EitherEndian2Int($ThisFileInfo, fread($fd, 4));
+			if ($chunksize == 0) {
+				$ThisFileInfo['warning'][] = 'Chunk size at offset '.(ftell($fd) - 4).' is zero. Aborting RIFF parsing.';
+				continue;
+			}
+			if (($chunksize % 2) != 0) {
+				// all structures are packed on word boundaries
+				$chunksize++;
+			}
+
+			switch ($chunkname) {
+				case 'LIST':
+					$listname = fread($fd, 4);
+					if (eregi('^(movi|rec )$', $listname)) {
+						$RIFFchunk[$listname]['offset'] = ftell($fd) - 4;
+						$RIFFchunk[$listname]['size']   = $chunksize;
+
+						if ($FoundAllChunksWeNeed) {
+
+							// skip over
+
+						} else {
+
+							$WhereWeWere = ftell($fd);
+							$AudioChunkHeader = fread($fd, 12);
+							$AudioChunkStreamNum  =                              substr($AudioChunkHeader, 0, 2);
+							$AudioChunkStreamType =                              substr($AudioChunkHeader, 2, 2);
+							$AudioChunkSize       = getid3_lib::LittleEndian2Int(substr($AudioChunkHeader, 4, 4));
+
+							if ($AudioChunkStreamType == 'wb') {
+								$FirstFourBytes = substr($AudioChunkHeader, 8, 4);
+								if (preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', $FirstFourBytes)) {
+									// MP3
+									if (getid3_mp3::MPEGaudioHeaderBytesValid($FirstFourBytes)) {
+										$dummy = $ThisFileInfo;
+										$dummy['avdataoffset'] = ftell($fd) - 4;
+										$dummy['avdataend']    = ftell($fd) + $AudioChunkSize;
+										getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $dummy['avdataoffset'], false);
+										if (isset($dummy['mpeg']['audio'])) {
+											$ThisFileInfo = $dummy;
+											$ThisFileInfo['audio']['dataformat']   = 'mp'.$ThisFileInfo['mpeg']['audio']['layer'];
+											$ThisFileInfo['audio']['sample_rate']  = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+											$ThisFileInfo['audio']['channels']     = $ThisFileInfo['mpeg']['audio']['channels'];
+											$ThisFileInfo['audio']['bitrate']      = $ThisFileInfo['mpeg']['audio']['bitrate'];
+											$ThisFileInfo['bitrate']               = $ThisFileInfo['audio']['bitrate'];
+											$ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
+										}
+										unset($dummy);
+									}
+
+								} elseif (preg_match('/^\x0B\x77/s', $FirstFourBytes)) {
+
+									// AC3
+									$GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
+									if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) {
+
+										$dummy = $ThisFileInfo;
+										$dummy['avdataoffset'] = ftell($fd) - 4;
+										$dummy['avdataend']    = ftell($fd) + $AudioChunkSize;
+										$dummy['error']        = array();
+										$ac3_tag = new getid3_ac3($fd, $dummy);
+										if (empty($dummy['error'])) {
+											$ThisFileInfo['audio']   = $dummy['audio'];
+											$ThisFileInfo['ac3']     = $dummy['ac3'];
+											$ThisFileInfo['warning'] = $dummy['warning'];
+										}
+										unset($ac3_tag);
+
+									}
+
+								}
+
+							}
+
+							$FoundAllChunksWeNeed = true;
+							fseek($fd, $WhereWeWere, SEEK_SET);
+
+						}
+						fseek($fd, $chunksize - 4, SEEK_CUR);
+
+					//} elseif (ereg('^[0-9]{2}(wb|pc|dc|db)$', $listname)) {
+                    //
+					//	// data chunk, ignore
+                    //
+					} else {
+
+						if (!isset($RIFFchunk[$listname])) {
+							$RIFFchunk[$listname] = array();
+						}
+						$LISTchunkParent    = $listname;
+						$LISTchunkMaxOffset = ftell($fd) - 4 + $chunksize;
+						if ($parsedChunk = getid3_riff::ParseRIFF($fd, ftell($fd), ftell($fd) + $chunksize - 4, $ThisFileInfo)) {
+							$RIFFchunk[$listname] = array_merge_recursive($RIFFchunk[$listname], $parsedChunk);
+						}
+
+					}
+					break;
+
+				default:
+					if (eregi('^[0-9]{2}(wb|pc|dc|db)$', $chunkname)) {
+						$nextoffset = ftell($fd) + $chunksize;
+						if (($nextoffset < 0) || ($nextoffset >= pow(2, 31))) {
+							$ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond 2GB limit of PHP filesystem functions';
+							break 2;
+						}
+						fseek($fd, $nextoffset, SEEK_SET);
+						break;
+					}
+					$thisindex = 0;
+					if (isset($RIFFchunk[$chunkname]) && is_array($RIFFchunk[$chunkname])) {
+						$thisindex = count($RIFFchunk[$chunkname]);
+					}
+					$RIFFchunk[$chunkname][$thisindex]['offset'] = ftell($fd) - 8;
+					$RIFFchunk[$chunkname][$thisindex]['size']   = $chunksize;
+					switch ($chunkname) {
+						case 'data':
+							$ThisFileInfo['avdataoffset'] = ftell($fd);
+							$ThisFileInfo['avdataend']    = $ThisFileInfo['avdataoffset'] + $chunksize;
+
+							$RIFFdataChunkContentsTest = fread($fd, 36);
+
+							if ((strlen($RIFFdataChunkContentsTest) > 0) && preg_match('/^\xFF[\xE2-\xE7\xF2-\xF7\xFA-\xFF][\x00-\xEB]/s', substr($RIFFdataChunkContentsTest, 0, 4))) {
+
+								// Probably is MP3 data
+								if (getid3_mp3::MPEGaudioHeaderBytesValid(substr($RIFFdataChunkContentsTest, 0, 4))) {
+
+									// copy info array
+									$dummy = $ThisFileInfo;
+
+									getid3_mp3::getOnlyMPEGaudioInfo($fd, $dummy, $RIFFchunk[$chunkname][$thisindex]['offset'], false);
+
+                                    // use dummy array unless error
+									if (empty($dummy['error'])) {
+									    $ThisFileInfo = $dummy;
+									}
+								}
+
+							} elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 2) == "\x0B\x77")) {
+
+								// This is probably AC-3 data
+								$GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
+								if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) {
+
+									$dummy = $ThisFileInfo;
+									$dummy['avdataoffset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
+									$dummy['avdataend']    = $dummy['avdataoffset'] + $RIFFchunk[$chunkname][$thisindex]['size'];
+									$dummy['error']        = array();
+
+									$ac3_tag = new getid3_ac3($fd, $dummy);
+									if (empty($dummy['error'])) {
+										$ThisFileInfo['audio']   = $dummy['audio'];
+										$ThisFileInfo['ac3']     = $dummy['ac3'];
+										$ThisFileInfo['warning'] = $dummy['warning'];
+									}
+									unset($ac3_tag);
+
+								}
+
+							} elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 8, 2) == "\x77\x0B")) {
+
+								// Dolby Digital WAV
+								// AC-3 content, but not encoded in same format as normal AC-3 file
+								// For one thing, byte order is swapped
+
+								$GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
+								if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, false)) {
+
+									// ok to use tmpfile here - only 56 bytes
+									if ($fd_temp = tmpfile()) {
+
+										for ($i = 0; $i < 28; $i += 2) {
+											// swap byte order
+											fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 1, 1));
+											fwrite($fd_temp, substr($RIFFdataChunkContentsTest, 8 + $i + 0, 1));
+										}
+
+										$dummy = $ThisFileInfo;
+										$dummy['avdataoffset'] = 0;
+										$dummy['avdataend']    = 20;
+										$dummy['error']        = array();
+										$ac3_tag = new getid3_ac3($fd_temp, $dummy);
+										fclose($fd_temp);
+										if (empty($dummy['error'])) {
+											$ThisFileInfo['audio']   = $dummy['audio'];
+											$ThisFileInfo['ac3']     = $dummy['ac3'];
+											$ThisFileInfo['warning'] = $dummy['warning'];
+										} else {
+											$ThisFileInfo['error'][] = 'Errors parsing DolbyDigital WAV: '.explode(';', $dummy['error']);
+										}
+										unset($ac3_tag);
+
+									} else {
+
+										$ThisFileInfo['error'][] = 'Could not create temporary file to analyze DolbyDigital WAV';
+
+									}
+
+								}
+
+							} elseif ((strlen($RIFFdataChunkContentsTest) > 0) && (substr($RIFFdataChunkContentsTest, 0, 4) == 'wvpk')) {
+
+								// This is WavPack data
+								$ThisFileInfo['wavpack']['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
+								$ThisFileInfo['wavpack']['size']   = getid3_lib::LittleEndian2Int(substr($RIFFdataChunkContentsTest, 4, 4));
+								getid3_riff::RIFFparseWavPackHeader(substr($RIFFdataChunkContentsTest, 8, 28), $ThisFileInfo);
+
+							} else {
+
+								// This is some other kind of data (quite possibly just PCM)
+								// do nothing special, just skip it
+
+							}
+							$nextoffset = $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize;
+							if (($nextoffset < 0) || ($nextoffset >= pow(2, 31))) {
+								$ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond 2GB limit of PHP filesystem functions';
+								break 3;
+							}
+							fseek($fd, $RIFFchunk[$chunkname][$thisindex]['offset'] + 8 + $chunksize, SEEK_SET);
+							break;
+
+						case 'bext':
+						case 'cart':
+						case 'fmt ':
+						case 'strh':
+						case 'strf':
+						case 'indx':
+						case 'MEXT':
+						case 'DISP':
+							// always read data in
+							$RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
+							break;
+
+						default:
+							if (!ereg('^[0-9]{2}(wb|pc|dc|db)$', $chunkname) && !empty($LISTchunkParent) && (($RIFFchunk[$chunkname][$thisindex]['offset'] + $RIFFchunk[$chunkname][$thisindex]['size']) <= $LISTchunkMaxOffset)) {
+								$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['offset'] = $RIFFchunk[$chunkname][$thisindex]['offset'];
+								$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['size']   = $RIFFchunk[$chunkname][$thisindex]['size'];
+								unset($RIFFchunk[$chunkname][$thisindex]['offset']);
+								unset($RIFFchunk[$chunkname][$thisindex]['size']);
+								if (isset($RIFFchunk[$chunkname][$thisindex]) && empty($RIFFchunk[$chunkname][$thisindex])) {
+									unset($RIFFchunk[$chunkname][$thisindex]);
+								}
+								if (isset($RIFFchunk[$chunkname]) && empty($RIFFchunk[$chunkname])) {
+									unset($RIFFchunk[$chunkname]);
+								}
+								$RIFFchunk[$LISTchunkParent][$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
+							} elseif ($chunksize < 2048) {
+								// only read data in if smaller than 2kB
+								$RIFFchunk[$chunkname][$thisindex]['data'] = fread($fd, $chunksize);
+							} else {
+								$nextoffset = ftell($fd) + $chunksize;
+								if (($nextoffset < 0) || ($nextoffset >= pow(2, 31))) {
+									$ThisFileInfo['warning'][] = 'Unable to parse chunk at offset '.$nextoffset.' because beyond 2GB limit of PHP filesystem functions';
+									break 3;
+								}
+								fseek($fd, $nextoffset, SEEK_SET);
+							}
+							break;
+					}
+					break;
+
+			}
+
+		}
+
+		return $RIFFchunk;
+	}
+
+
+	function ParseRIFFdata(&$RIFFdata, &$ThisFileInfo) {
+		if ($RIFFdata) {
+
+		    $tempfile = tempnam('*', 'getID3');
+            $fp_temp  = fopen($tempfile, "wb");
+			$RIFFdataLength = strlen($RIFFdata);
+			$NewLengthString = getid3_lib::LittleEndian2String($RIFFdataLength, 4);
+			for ($i = 0; $i < 4; $i++) {
+				$RIFFdata{$i + 4} = $NewLengthString{$i};
+			}
+			fwrite($fp_temp, $RIFFdata);
+			fclose($fp_temp);
+
+			$fp_temp  = fopen($tempfile, "rb");
+			$dummy = array('filesize'=>$RIFFdataLength, 'filenamepath'=>$ThisFileInfo['filenamepath'], 'tags'=>$ThisFileInfo['tags'], 'avdataoffset'=>0, 'avdataend'=>$RIFFdataLength, 'warning'=>$ThisFileInfo['warning'], 'error'=>$ThisFileInfo['error'], 'comments'=>$ThisFileInfo['comments'], 'audio'=>(isset($ThisFileInfo['audio']) ? $ThisFileInfo['audio'] : array()), 'video'=>(isset($ThisFileInfo['video']) ? $ThisFileInfo['video'] : array()));
+			$riff = new getid3_riff($fp_temp, $dummy);
+			$ThisFileInfo['riff']     = $dummy['riff'];
+			$ThisFileInfo['warning']  = $dummy['warning'];
+			$ThisFileInfo['error']    = $dummy['error'];
+			$ThisFileInfo['tags']     = $dummy['tags'];
+			$ThisFileInfo['comments'] = $dummy['comments'];
+			unset($riff);
+			fclose($fp_temp);
+			unlink($tempfile);
+			return true;
+		}
+		return false;
+	}
+
+
+	function RIFFparseWAVEFORMATex($WaveFormatExData) {
+		// shortcut
+		$WaveFormatEx['raw'] = array();
+		$WaveFormatEx_raw    = &$WaveFormatEx['raw'];
+
+		$WaveFormatEx_raw['wFormatTag']      = getid3_lib::LittleEndian2Int(substr($WaveFormatExData,  0, 2));
+		$WaveFormatEx_raw['nChannels']       = getid3_lib::LittleEndian2Int(substr($WaveFormatExData,  2, 2));
+		$WaveFormatEx_raw['nSamplesPerSec']  = getid3_lib::LittleEndian2Int(substr($WaveFormatExData,  4, 4));
+		$WaveFormatEx_raw['nAvgBytesPerSec'] = getid3_lib::LittleEndian2Int(substr($WaveFormatExData,  8, 4));
+		$WaveFormatEx_raw['nBlockAlign']     = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 12, 2));
+		$WaveFormatEx_raw['wBitsPerSample']  = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 14, 2));
+		if (strlen($WaveFormatExData) > 16) {
+			$WaveFormatEx_raw['cbSize']      = getid3_lib::LittleEndian2Int(substr($WaveFormatExData, 16, 2));
+		}
+
+		$WaveFormatEx['codec']           = getid3_riff::RIFFwFormatTagLookup($WaveFormatEx_raw['wFormatTag']);
+		$WaveFormatEx['channels']        = $WaveFormatEx_raw['nChannels'];
+		$WaveFormatEx['sample_rate']     = $WaveFormatEx_raw['nSamplesPerSec'];
+		$WaveFormatEx['bitrate']         = $WaveFormatEx_raw['nAvgBytesPerSec'] * 8;
+		$WaveFormatEx['bits_per_sample'] = $WaveFormatEx_raw['wBitsPerSample'];
+
+		return $WaveFormatEx;
+	}
+
+
+	function RIFFparseWavPackHeader($WavPackChunkData, &$ThisFileInfo) {
+		// typedef struct {
+		//     char ckID [4];
+		//     long ckSize;
+		//     short version;
+		//     short bits;                // added for version 2.00
+		//     short flags, shift;        // added for version 3.00
+		//     long total_samples, crc, crc2;
+		//     char extension [4], extra_bc, extras [3];
+		// } WavpackHeader;
+
+		// shortcut
+		$ThisFileInfo['wavpack'] = array();
+		$thisfile_wavpack        = &$ThisFileInfo['wavpack'];
+
+		$thisfile_wavpack['version']           = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  0, 2));
+		if ($thisfile_wavpack['version'] >= 2) {
+			$thisfile_wavpack['bits']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  2, 2));
+		}
+		if ($thisfile_wavpack['version'] >= 3) {
+			$thisfile_wavpack['flags_raw']     = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  4, 2));
+			$thisfile_wavpack['shift']         = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  6, 2));
+			$thisfile_wavpack['total_samples'] = getid3_lib::LittleEndian2Int(substr($WavPackChunkData,  8, 4));
+			$thisfile_wavpack['crc1']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 12, 4));
+			$thisfile_wavpack['crc2']          = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 16, 4));
+			$thisfile_wavpack['extension']     =                              substr($WavPackChunkData, 20, 4);
+			$thisfile_wavpack['extra_bc']      = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 24, 1));
+			for ($i = 0; $i <= 2; $i++) {
+				$thisfile_wavpack['extras'][]  = getid3_lib::LittleEndian2Int(substr($WavPackChunkData, 25 + $i, 1));
+			}
+
+			// shortcut
+			$thisfile_wavpack['flags'] = array();
+			$thisfile_wavpack_flags = &$thisfile_wavpack['flags'];
+
+			$thisfile_wavpack_flags['mono']                 = (bool) ($thisfile_wavpack['flags_raw'] & 0x000001);
+			$thisfile_wavpack_flags['fast_mode']            = (bool) ($thisfile_wavpack['flags_raw'] & 0x000002);
+			$thisfile_wavpack_flags['raw_mode']             = (bool) ($thisfile_wavpack['flags_raw'] & 0x000004);
+			$thisfile_wavpack_flags['calc_noise']           = (bool) ($thisfile_wavpack['flags_raw'] & 0x000008);
+			$thisfile_wavpack_flags['high_quality']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000010);
+			$thisfile_wavpack_flags['3_byte_samples']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000020);
+			$thisfile_wavpack_flags['over_20_bits']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000040);
+			$thisfile_wavpack_flags['use_wvc']              = (bool) ($thisfile_wavpack['flags_raw'] & 0x000080);
+			$thisfile_wavpack_flags['noiseshaping']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x000100);
+			$thisfile_wavpack_flags['very_fast_mode']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000200);
+			$thisfile_wavpack_flags['new_high_quality']     = (bool) ($thisfile_wavpack['flags_raw'] & 0x000400);
+			$thisfile_wavpack_flags['cancel_extreme']       = (bool) ($thisfile_wavpack['flags_raw'] & 0x000800);
+			$thisfile_wavpack_flags['cross_decorrelation']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x001000);
+			$thisfile_wavpack_flags['new_decorrelation']    = (bool) ($thisfile_wavpack['flags_raw'] & 0x002000);
+			$thisfile_wavpack_flags['joint_stereo']         = (bool) ($thisfile_wavpack['flags_raw'] & 0x004000);
+			$thisfile_wavpack_flags['extra_decorrelation']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x008000);
+			$thisfile_wavpack_flags['override_noiseshape']  = (bool) ($thisfile_wavpack['flags_raw'] & 0x010000);
+			$thisfile_wavpack_flags['override_jointstereo'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x020000);
+			$thisfile_wavpack_flags['copy_source_filetime'] = (bool) ($thisfile_wavpack['flags_raw'] & 0x040000);
+			$thisfile_wavpack_flags['create_exe']           = (bool) ($thisfile_wavpack['flags_raw'] & 0x080000);
+		}
+
+		return true;
+	}
+
+	function ParseBITMAPINFOHEADER($BITMAPINFOHEADER, $littleEndian=true) {
+		// yes it's ugly to instantiate a getid3_lib object here, suggested alternative please?
+		$getid3_lib = new getid3_lib();
+		$functionname = ($littleEndian ? 'LittleEndian2Int' : 'BigEndian2Int');
+		$parsed['biSize']          = $getid3_lib->$functionname(substr($BITMAPINFOHEADER,  0, 4)); // number of bytes required by the BITMAPINFOHEADER structure
+		$parsed['biWidth']         = $getid3_lib->$functionname(substr($BITMAPINFOHEADER,  4, 4)); // width of the bitmap in pixels
+		$parsed['biHeight']        = $getid3_lib->$functionname(substr($BITMAPINFOHEADER,  8, 4)); // height of the bitmap in pixels. If biHeight is positive, the bitmap is a 'bottom-up' DIB and its origin is the lower left corner. If biHeight is negative, the bitmap is a 'top-down' DIB and its origin is the upper left corner
+		$parsed['biPlanes']        = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 12, 2)); // number of color planes on the target device. In most cases this value must be set to 1
+		$parsed['biBitCount']      = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 14, 2)); // Specifies the number of bits per pixels
+		$parsed['fourcc']          =                            substr($BITMAPINFOHEADER, 16, 4);  // compression identifier
+		$parsed['biSizeImage']     = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 20, 4)); // size of the bitmap data section of the image (the actual pixel data, excluding BITMAPINFOHEADER and RGBQUAD structures)
+		$parsed['biXPelsPerMeter'] = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 24, 4)); // horizontal resolution, in pixels per metre, of the target device
+		$parsed['biYPelsPerMeter'] = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 28, 4)); // vertical resolution, in pixels per metre, of the target device
+		$parsed['biClrUsed']       = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 32, 4)); // actual number of color indices in the color table used by the bitmap. If this value is zero, the bitmap uses the maximum number of colors corresponding to the value of the biBitCount member for the compression mode specified by biCompression
+		$parsed['biClrImportant']  = $getid3_lib->$functionname(substr($BITMAPINFOHEADER, 36, 4)); // number of color indices that are considered important for displaying the bitmap. If this value is zero, all colors are important
+		return $parsed;
+	}
+
+	function RIFFwFormatTagLookup($wFormatTag) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			0x0000	Microsoft Unknown Wave Format
+			0x0001	Pulse Code Modulation (PCM)
+			0x0002	Microsoft ADPCM
+			0x0003	IEEE Float
+			0x0004	Compaq Computer VSELP
+			0x0005	IBM CVSD
+			0x0006	Microsoft A-Law
+			0x0007	Microsoft mu-Law
+			0x0008	Microsoft DTS
+			0x0010	OKI ADPCM
+			0x0011	Intel DVI/IMA ADPCM
+			0x0012	Videologic MediaSpace ADPCM
+			0x0013	Sierra Semiconductor ADPCM
+			0x0014	Antex Electronics G.723 ADPCM
+			0x0015	DSP Solutions DigiSTD
+			0x0016	DSP Solutions DigiFIX
+			0x0017	Dialogic OKI ADPCM
+			0x0018	MediaVision ADPCM
+			0x0019	Hewlett-Packard CU
+			0x0020	Yamaha ADPCM
+			0x0021	Speech Compression Sonarc
+			0x0022	DSP Group TrueSpeech
+			0x0023	Echo Speech EchoSC1
+			0x0024	Audiofile AF36
+			0x0025	Audio Processing Technology APTX
+			0x0026	AudioFile AF10
+			0x0027	Prosody 1612
+			0x0028	LRC
+			0x0030	Dolby AC2
+			0x0031	Microsoft GSM 6.10
+			0x0032	MSNAudio
+			0x0033	Antex Electronics ADPCME
+			0x0034	Control Resources VQLPC
+			0x0035	DSP Solutions DigiREAL
+			0x0036	DSP Solutions DigiADPCM
+			0x0037	Control Resources CR10
+			0x0038	Natural MicroSystems VBXADPCM
+			0x0039	Crystal Semiconductor IMA ADPCM
+			0x003A	EchoSC3
+			0x003B	Rockwell ADPCM
+			0x003C	Rockwell Digit LK
+			0x003D	Xebec
+			0x0040	Antex Electronics G.721 ADPCM
+			0x0041	G.728 CELP
+			0x0042	MSG723
+			0x0050	MPEG Layer-2 or Layer-1
+			0x0052	RT24
+			0x0053	PAC
+			0x0055	MPEG Layer-3
+			0x0059	Lucent G.723
+			0x0060	Cirrus
+			0x0061	ESPCM
+			0x0062	Voxware
+			0x0063	Canopus Atrac
+			0x0064	G.726 ADPCM
+			0x0065	G.722 ADPCM
+			0x0066	DSAT
+			0x0067	DSAT Display
+			0x0069	Voxware Byte Aligned
+			0x0070	Voxware AC8
+			0x0071	Voxware AC10
+			0x0072	Voxware AC16
+			0x0073	Voxware AC20
+			0x0074	Voxware MetaVoice
+			0x0075	Voxware MetaSound
+			0x0076	Voxware RT29HW
+			0x0077	Voxware VR12
+			0x0078	Voxware VR18
+			0x0079	Voxware TQ40
+			0x0080	Softsound
+			0x0081	Voxware TQ60
+			0x0082	MSRT24
+			0x0083	G.729A
+			0x0084	MVI MV12
+			0x0085	DF G.726
+			0x0086	DF GSM610
+			0x0088	ISIAudio
+			0x0089	Onlive
+			0x0091	SBC24
+			0x0092	Dolby AC3 SPDIF
+			0x0093	MediaSonic G.723
+			0x0094	Aculab PLC    Prosody 8kbps
+			0x0097	ZyXEL ADPCM
+			0x0098	Philips LPCBB
+			0x0099	Packed
+			0x00FF	AAC
+			0x0100	Rhetorex ADPCM
+			0x0101	IBM mu-law
+			0x0102	IBM A-law
+			0x0103	IBM AVC Adaptive Differential Pulse Code Modulation (ADPCM)
+			0x0111	Vivo G.723
+			0x0112	Vivo Siren
+			0x0123	Digital G.723
+			0x0125	Sanyo LD ADPCM
+			0x0130	Sipro Lab Telecom ACELP NET
+			0x0131	Sipro Lab Telecom ACELP 4800
+			0x0132	Sipro Lab Telecom ACELP 8V3
+			0x0133	Sipro Lab Telecom G.729
+			0x0134	Sipro Lab Telecom G.729A
+			0x0135	Sipro Lab Telecom Kelvin
+			0x0140	Windows Media Video V8
+			0x0150	Qualcomm PureVoice
+			0x0151	Qualcomm HalfRate
+			0x0155	Ring Zero Systems TUB GSM
+			0x0160	Microsoft Audio 1
+			0x0161	Windows Media Audio V7 / V8 / V9
+			0x0162	Windows Media Audio Professional V9
+			0x0163	Windows Media Audio Lossless V9
+			0x0200	Creative Labs ADPCM
+			0x0202	Creative Labs Fastspeech8
+			0x0203	Creative Labs Fastspeech10
+			0x0210	UHER Informatic GmbH ADPCM
+			0x0220	Quarterdeck
+			0x0230	I-link Worldwide VC
+			0x0240	Aureal RAW Sport
+			0x0250	Interactive Products HSX
+			0x0251	Interactive Products RPELP
+			0x0260	Consistent Software CS2
+			0x0270	Sony SCX
+			0x0300	Fujitsu FM Towns Snd
+			0x0400	BTV Digital
+			0x0401	Intel Music Coder
+			0x0450	QDesign Music
+			0x0680	VME VMPCM
+			0x0681	AT&T Labs TPC
+			0x08AE	ClearJump LiteWave
+			0x1000	Olivetti GSM
+			0x1001	Olivetti ADPCM
+			0x1002	Olivetti CELP
+			0x1003	Olivetti SBC
+			0x1004	Olivetti OPR
+			0x1100	Lernout & Hauspie Codec (0x1100)
+			0x1101	Lernout & Hauspie CELP Codec (0x1101)
+			0x1102	Lernout & Hauspie SBC Codec (0x1102)
+			0x1103	Lernout & Hauspie SBC Codec (0x1103)
+			0x1104	Lernout & Hauspie SBC Codec (0x1104)
+			0x1400	Norris
+			0x1401	AT&T ISIAudio
+			0x1500	Soundspace Music Compression
+			0x181C	VoxWare RT24 Speech
+			0x1FC4	NCT Soft ALF2CD (www.nctsoft.com)
+			0x2000	Dolby AC3
+			0x2001	Dolby DTS
+			0x2002	WAVE_FORMAT_14_4
+			0x2003	WAVE_FORMAT_28_8
+			0x2004	WAVE_FORMAT_COOK
+			0x2005	WAVE_FORMAT_DNET
+			0x674F	Ogg Vorbis 1
+			0x6750	Ogg Vorbis 2
+			0x6751	Ogg Vorbis 3
+			0x676F	Ogg Vorbis 1+
+			0x6770	Ogg Vorbis 2+
+			0x6771	Ogg Vorbis 3+
+			0x7A21	GSM-AMR (CBR, no SID)
+			0x7A22	GSM-AMR (VBR, including SID)
+			0xFFFE	WAVE_FORMAT_EXTENSIBLE
+			0xFFFF	WAVE_FORMAT_DEVELOPMENT
+
+		*/
+
+		return getid3_lib::EmbeddedLookup('0x'.str_pad(strtoupper(dechex($wFormatTag)), 4, '0', STR_PAD_LEFT), $begin, __LINE__, __FILE__, 'riff-wFormatTag');
+
+	}
+
+
+	function RIFFfourccLookup($fourcc) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			swot	http://developer.apple.com/qa/snd/snd07.html
+			____	No Codec (____)
+			_BIT	BI_BITFIELDS (Raw RGB)
+			_JPG	JPEG compressed
+			_PNG	PNG compressed W3C/ISO/IEC (RFC-2083)
+			_RAW	Full Frames (Uncompressed)
+			_RGB	Raw RGB Bitmap
+			_RL4	RLE 4bpp RGB
+			_RL8	RLE 8bpp RGB
+			3IV1	3ivx MPEG-4 v1
+			3IV2	3ivx MPEG-4 v2
+			3IVX	3ivx MPEG-4
+			AASC	Autodesk Animator
+			ABYR	Kensington ?ABYR?
+			AEMI	Array Microsystems VideoONE MPEG1-I Capture
+			AFLC	Autodesk Animator FLC
+			AFLI	Autodesk Animator FLI
+			AMPG	Array Microsystems VideoONE MPEG
+			ANIM	Intel RDX (ANIM)
+			AP41	AngelPotion Definitive
+			ASV1	Asus Video v1
+			ASV2	Asus Video v2
+			ASVX	Asus Video 2.0 (audio)
+			AUR2	AuraVision Aura 2 Codec - YUV 4:2:2
+			AURA	AuraVision Aura 1 Codec - YUV 4:1:1
+			AVDJ	Independent JPEG Group\'s codec (AVDJ)
+			AVRN	Independent JPEG Group\'s codec (AVRN)
+			AYUV	4:4:4 YUV (AYUV)
+			AZPR	Quicktime Apple Video (AZPR)
+			BGR 	Raw RGB32
+			BLZ0	Blizzard DivX MPEG-4
+			BTVC	Conexant Composite Video
+			BINK	RAD Game Tools Bink Video
+			BT20	Conexant Prosumer Video
+			BTCV	Conexant Composite Video Codec
+			BW10	Data Translation Broadway MPEG Capture
+			CC12	Intel YUV12
+			CDVC	Canopus DV
+			CFCC	Digital Processing Systems DPS Perception
+			CGDI	Microsoft Office 97 Camcorder Video
+			CHAM	Winnov Caviara Champagne
+			CJPG	Creative WebCam JPEG
+			CLJR	Cirrus Logic YUV 4:1:1
+			CMYK	Common Data Format in Printing (Colorgraph)
+			CPLA	Weitek 4:2:0 YUV Planar
+			CRAM	Microsoft Video 1 (CRAM)
+			cvid	Radius Cinepak
+			CVID	Radius Cinepak
+			CWLT	Microsoft Color WLT DIB
+			CYUV	Creative Labs YUV
+			CYUY	ATI YUV
+			D261	H.261
+			D263	H.263
+			DIB 	Device Independent Bitmap
+			DIV1	FFmpeg OpenDivX
+			DIV2	Microsoft MPEG-4 v1/v2
+			DIV3	DivX ;-) MPEG-4 v3.x Low-Motion
+			DIV4	DivX ;-) MPEG-4 v3.x Fast-Motion
+			DIV5	DivX MPEG-4 v5.x
+			DIV6	DivX ;-) (MS MPEG-4 v3.x)
+			DIVX	DivX MPEG-4 v4 (OpenDivX / Project Mayo)
+			divx	DivX MPEG-4
+			DMB1	Matrox Rainbow Runner hardware MJPEG
+			DMB2	Paradigm MJPEG
+			DSVD	?DSVD?
+			DUCK	Duck TrueMotion 1.0
+			DPS0	DPS/Leitch Reality Motion JPEG
+			DPSC	DPS/Leitch PAR Motion JPEG
+			DV25	Matrox DVCPRO codec
+			DV50	Matrox DVCPRO50 codec
+			DVC 	IEC 61834 and SMPTE 314M (DVC/DV Video)
+			DVCP	IEC 61834 and SMPTE 314M (DVC/DV Video)
+			DVHD	IEC Standard DV 1125 lines @ 30fps / 1250 lines @ 25fps
+			DVMA	Darim Vision DVMPEG (dummy for MPEG compressor) (www.darvision.com)
+			DVSL	IEC Standard DV compressed in SD (SDL)
+			DVAN	?DVAN?
+			DVE2	InSoft DVE-2 Videoconferencing
+			dvsd	IEC 61834 and SMPTE 314M DVC/DV Video
+			DVSD	IEC 61834 and SMPTE 314M DVC/DV Video
+			DVX1	Lucent DVX1000SP Video Decoder
+			DVX2	Lucent DVX2000S Video Decoder
+			DVX3	Lucent DVX3000S Video Decoder
+			DX50	DivX v5
+			DXT1	Microsoft DirectX Compressed Texture (DXT1)
+			DXT2	Microsoft DirectX Compressed Texture (DXT2)
+			DXT3	Microsoft DirectX Compressed Texture (DXT3)
+			DXT4	Microsoft DirectX Compressed Texture (DXT4)
+			DXT5	Microsoft DirectX Compressed Texture (DXT5)
+			DXTC	Microsoft DirectX Compressed Texture (DXTC)
+			DXTn	Microsoft DirectX Compressed Texture (DXTn)
+			EM2V	Etymonix MPEG-2 I-frame (www.etymonix.com)
+			EKQ0	Elsa ?EKQ0?
+			ELK0	Elsa ?ELK0?
+			ESCP	Eidos Escape
+			ETV1	eTreppid Video ETV1
+			ETV2	eTreppid Video ETV2
+			ETVC	eTreppid Video ETVC
+			FLIC	Autodesk FLI/FLC Animation
+			FRWT	Darim Vision Forward Motion JPEG (www.darvision.com)
+			FRWU	Darim Vision Forward Uncompressed (www.darvision.com)
+			FLJP	D-Vision Field Encoded Motion JPEG
+			FRWA	SoftLab-Nsk Forward Motion JPEG w/ alpha channel
+			FRWD	SoftLab-Nsk Forward Motion JPEG
+			FVF1	Iterated Systems Fractal Video Frame
+			GLZW	Motion LZW (gabest@freemail.hu)
+			GPEG	Motion JPEG (gabest@freemail.hu)
+			GWLT	Microsoft Greyscale WLT DIB
+			H260	Intel ITU H.260 Videoconferencing
+			H261	Intel ITU H.261 Videoconferencing
+			H262	Intel ITU H.262 Videoconferencing
+			H263	Intel ITU H.263 Videoconferencing
+			H264	Intel ITU H.264 Videoconferencing
+			H265	Intel ITU H.265 Videoconferencing
+			H266	Intel ITU H.266 Videoconferencing
+			H267	Intel ITU H.267 Videoconferencing
+			H268	Intel ITU H.268 Videoconferencing
+			H269	Intel ITU H.269 Videoconferencing
+			HFYU	Huffman Lossless Codec
+			HMCR	Rendition Motion Compensation Format (HMCR)
+			HMRR	Rendition Motion Compensation Format (HMRR)
+			I263	FFmpeg I263 decoder
+			IF09	Indeo YVU9 ("YVU9 with additional delta-frame info after the U plane")
+			IUYV	Interlaced version of UYVY (www.leadtools.com)
+			IY41	Interlaced version of Y41P (www.leadtools.com)
+			IYU1	12 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec    IEEE standard
+			IYU2	24 bit format used in mode 2 of the IEEE 1394 Digital Camera 1.04 spec    IEEE standard
+			IYUV	Planar YUV format (8-bpp Y plane, followed by 8-bpp 2×2 U and V planes)
+			i263	Intel ITU H.263 Videoconferencing (i263)
+			I420	Intel Indeo 4
+			IAN 	Intel Indeo 4 (RDX)
+			ICLB	InSoft CellB Videoconferencing
+			IGOR	Power DVD
+			IJPG	Intergraph JPEG
+			ILVC	Intel Layered Video
+			ILVR	ITU-T H.263+
+			IPDV	I-O Data Device Giga AVI DV Codec
+			IR21	Intel Indeo 2.1
+			IRAW	Intel YUV Uncompressed
+			IV30	Intel Indeo 3.0
+			IV31	Intel Indeo 3.1
+			IV32	Ligos Indeo 3.2
+			IV33	Ligos Indeo 3.3
+			IV34	Ligos Indeo 3.4
+			IV35	Ligos Indeo 3.5
+			IV36	Ligos Indeo 3.6
+			IV37	Ligos Indeo 3.7
+			IV38	Ligos Indeo 3.8
+			IV39	Ligos Indeo 3.9
+			IV40	Ligos Indeo Interactive 4.0
+			IV41	Ligos Indeo Interactive 4.1
+			IV42	Ligos Indeo Interactive 4.2
+			IV43	Ligos Indeo Interactive 4.3
+			IV44	Ligos Indeo Interactive 4.4
+			IV45	Ligos Indeo Interactive 4.5
+			IV46	Ligos Indeo Interactive 4.6
+			IV47	Ligos Indeo Interactive 4.7
+			IV48	Ligos Indeo Interactive 4.8
+			IV49	Ligos Indeo Interactive 4.9
+			IV50	Ligos Indeo Interactive 5.0
+			JBYR	Kensington ?JBYR?
+			JPEG	Still Image JPEG DIB
+			JPGL	Pegasus Lossless Motion JPEG
+			KMVC	Team17 Software Karl Morton\'s Video Codec
+			LSVM	Vianet Lighting Strike Vmail (Streaming) (www.vianet.com)
+			LEAD	LEAD Video Codec
+			Ljpg	LEAD MJPEG Codec
+			MDVD	Alex MicroDVD Video (hacked MS MPEG-4) (www.tiasoft.de)
+			MJPA	Morgan Motion JPEG (MJPA) (www.morgan-multimedia.com)
+			MJPB	Morgan Motion JPEG (MJPB) (www.morgan-multimedia.com)
+			MMES	Matrox MPEG-2 I-frame
+			MP2v	Microsoft S-Mpeg 4 version 1 (MP2v)
+			MP42	Microsoft S-Mpeg 4 version 2 (MP42)
+			MP43	Microsoft S-Mpeg 4 version 3 (MP43)
+			MP4S	Microsoft S-Mpeg 4 version 3 (MP4S)
+			MP4V	FFmpeg MPEG-4
+			MPG1	FFmpeg MPEG 1/2
+			MPG2	FFmpeg MPEG 1/2
+			MPG3	FFmpeg DivX ;-) (MS MPEG-4 v3)
+			MPG4	Microsoft MPEG-4
+			MPGI	Sigma Designs MPEG
+			MPNG	PNG images decoder
+			MSS1	Microsoft Windows Screen Video
+			MSZH	LCL (Lossless Codec Library) (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm)
+			M261	Microsoft H.261
+			M263	Microsoft H.263
+			M4S2	Microsoft Fully Compliant MPEG-4 v2 simple profile (M4S2)
+			m4s2	Microsoft Fully Compliant MPEG-4 v2 simple profile (m4s2)
+			MC12	ATI Motion Compensation Format (MC12)
+			MCAM	ATI Motion Compensation Format (MCAM)
+			MJ2C	Morgan Multimedia Motion JPEG2000
+			mJPG	IBM Motion JPEG w/ Huffman Tables
+			MJPG	Microsoft Motion JPEG DIB
+			MP42	Microsoft MPEG-4 (low-motion)
+			MP43	Microsoft MPEG-4 (fast-motion)
+			MP4S	Microsoft MPEG-4 (MP4S)
+			mp4s	Microsoft MPEG-4 (mp4s)
+			MPEG	Chromatic Research MPEG-1 Video I-Frame
+			MPG4	Microsoft MPEG-4 Video High Speed Compressor
+			MPGI	Sigma Designs MPEG
+			MRCA	FAST Multimedia Martin Regen Codec
+			MRLE	Microsoft Run Length Encoding
+			MSVC	Microsoft Video 1
+			MTX1	Matrox ?MTX1?
+			MTX2	Matrox ?MTX2?
+			MTX3	Matrox ?MTX3?
+			MTX4	Matrox ?MTX4?
+			MTX5	Matrox ?MTX5?
+			MTX6	Matrox ?MTX6?
+			MTX7	Matrox ?MTX7?
+			MTX8	Matrox ?MTX8?
+			MTX9	Matrox ?MTX9?
+			MV12	Motion Pixels Codec (old)
+			MWV1	Aware Motion Wavelets
+			nAVI	SMR Codec (hack of Microsoft MPEG-4) (IRC #shadowrealm)
+			NT00	NewTek LightWave HDTV YUV w/ Alpha (www.newtek.com)
+			NUV1	NuppelVideo
+			NTN1	Nogatech Video Compression 1
+			NVS0	nVidia GeForce Texture (NVS0)
+			NVS1	nVidia GeForce Texture (NVS1)
+			NVS2	nVidia GeForce Texture (NVS2)
+			NVS3	nVidia GeForce Texture (NVS3)
+			NVS4	nVidia GeForce Texture (NVS4)
+			NVS5	nVidia GeForce Texture (NVS5)
+			NVT0	nVidia GeForce Texture (NVT0)
+			NVT1	nVidia GeForce Texture (NVT1)
+			NVT2	nVidia GeForce Texture (NVT2)
+			NVT3	nVidia GeForce Texture (NVT3)
+			NVT4	nVidia GeForce Texture (NVT4)
+			NVT5	nVidia GeForce Texture (NVT5)
+			PIXL	MiroXL, Pinnacle PCTV
+			PDVC	I-O Data Device Digital Video Capture DV codec
+			PGVV	Radius Video Vision
+			PHMO	IBM Photomotion
+			PIM1	MPEG Realtime (Pinnacle Cards)
+			PIM2	Pegasus Imaging ?PIM2?
+			PIMJ	Pegasus Imaging Lossless JPEG
+			PVEZ	Horizons Technology PowerEZ
+			PVMM	PacketVideo Corporation MPEG-4
+			PVW2	Pegasus Imaging Wavelet Compression
+			Q1.0	Q-Team\'s QPEG 1.0 (www.q-team.de)
+			Q1.1	Q-Team\'s QPEG 1.1 (www.q-team.de)
+			QPEG	Q-Team QPEG 1.0
+			qpeq	Q-Team QPEG 1.1
+			RGB 	Raw BGR32
+			RGBA	Raw RGB w/ Alpha
+			RMP4	REALmagic MPEG-4 (unauthorized XVID copy) (www.sigmadesigns.com)
+			ROQV	Id RoQ File Video Decoder
+			RPZA	Quicktime Apple Video (RPZA)
+			RUD0	Rududu video codec (http://rududu.ifrance.com/rududu/)
+			RV10	RealVideo 1.0 (aka RealVideo 5.0)
+			RV13	RealVideo 1.0 (RV13)
+			RV20	RealVideo G2
+			RV30	RealVideo 8
+			RV40	RealVideo 9
+			RGBT	Raw RGB w/ Transparency
+			RLE 	Microsoft Run Length Encoder
+			RLE4	Run Length Encoded (4bpp, 16-color)
+			RLE8	Run Length Encoded (8bpp, 256-color)
+			RT21	Intel Indeo RealTime Video 2.1
+			rv20	RealVideo G2
+			rv30	RealVideo 8
+			RVX 	Intel RDX (RVX )
+			SMC 	Apple Graphics (SMC )
+			SP54	Logitech Sunplus Sp54 Codec for Mustek GSmart Mini 2
+			SPIG	Radius Spigot
+			SVQ3	Sorenson Video 3 (Apple Quicktime 5)
+			s422	Tekram VideoCap C210 YUV 4:2:2
+			SDCC	Sun Communication Digital Camera Codec
+			SFMC	CrystalNet Surface Fitting Method
+			SMSC	Radius SMSC
+			SMSD	Radius SMSD
+			smsv	WorldConnect Wavelet Video
+			SPIG	Radius Spigot
+			SPLC	Splash Studios ACM Audio Codec (www.splashstudios.net)
+			SQZ2	Microsoft VXTreme Video Codec V2
+			STVA	ST Microelectronics CMOS Imager Data (Bayer)
+			STVB	ST Microelectronics CMOS Imager Data (Nudged Bayer)
+			STVC	ST Microelectronics CMOS Imager Data (Bunched)
+			STVX	ST Microelectronics CMOS Imager Data (Extended CODEC Data Format)
+			STVY	ST Microelectronics CMOS Imager Data (Extended CODEC Data Format with Correction Data)
+			SV10	Sorenson Video R1
+			SVQ1	Sorenson Video
+			T420	Toshiba YUV 4:2:0
+			TM2A	Duck TrueMotion Archiver 2.0 (www.duck.com)
+			TVJP	Pinnacle/Truevision Targa 2000 board (TVJP)
+			TVMJ	Pinnacle/Truevision Targa 2000 board (TVMJ)
+			TY0N	Tecomac Low-Bit Rate Codec (www.tecomac.com)
+			TY2C	Trident Decompression Driver
+			TLMS	TeraLogic Motion Intraframe Codec (TLMS)
+			TLST	TeraLogic Motion Intraframe Codec (TLST)
+			TM20	Duck TrueMotion 2.0
+			TM2X	Duck TrueMotion 2X
+			TMIC	TeraLogic Motion Intraframe Codec (TMIC)
+			TMOT	Horizons Technology TrueMotion S
+			tmot	Horizons TrueMotion Video Compression
+			TR20	Duck TrueMotion RealTime 2.0
+			TSCC	TechSmith Screen Capture Codec
+			TV10	Tecomac Low-Bit Rate Codec
+			TY2N	Trident ?TY2N?
+			U263	UB Video H.263/H.263+/H.263++ Decoder
+			UMP4	UB Video MPEG 4 (www.ubvideo.com)
+			UYNV	Nvidia UYVY packed 4:2:2
+			UYVP	Evans & Sutherland YCbCr 4:2:2 extended precision
+			UCOD	eMajix.com ClearVideo
+			ULTI	IBM Ultimotion
+			UYVY	UYVY packed 4:2:2
+			V261	Lucent VX2000S
+			VIFP	VFAPI Reader Codec (www.yks.ne.jp/~hori/)
+			VIV1	FFmpeg H263+ decoder
+			VIV2	Vivo H.263
+			VQC2	Vector-quantised codec 2 (research) http://eprints.ecs.soton.ac.uk/archive/00001310/01/VTC97-js.pdf)
+			VTLP	Alaris VideoGramPiX
+			VYU9	ATI YUV (VYU9)
+			VYUY	ATI YUV (VYUY)
+			V261	Lucent VX2000S
+			V422	Vitec Multimedia 24-bit YUV 4:2:2 Format
+			V655	Vitec Multimedia 16-bit YUV 4:2:2 Format
+			VCR1	ATI Video Codec 1
+			VCR2	ATI Video Codec 2
+			VCR3	ATI VCR 3.0
+			VCR4	ATI VCR 4.0
+			VCR5	ATI VCR 5.0
+			VCR6	ATI VCR 6.0
+			VCR7	ATI VCR 7.0
+			VCR8	ATI VCR 8.0
+			VCR9	ATI VCR 9.0
+			VDCT	Vitec Multimedia Video Maker Pro DIB
+			VDOM	VDOnet VDOWave
+			VDOW	VDOnet VDOLive (H.263)
+			VDTZ	Darim Vison VideoTizer YUV
+			VGPX	Alaris VideoGramPiX
+			VIDS	Vitec Multimedia YUV 4:2:2 CCIR 601 for V422
+			VIVO	Vivo H.263 v2.00
+			vivo	Vivo H.263
+			VIXL	Miro/Pinnacle Video XL
+			VLV1	VideoLogic/PURE Digital Videologic Capture
+			VP30	On2 VP3.0
+			VP31	On2 VP3.1
+			VX1K	Lucent VX1000S Video Codec
+			VX2K	Lucent VX2000S Video Codec
+			VXSP	Lucent VX1000SP Video Codec
+			WBVC	Winbond W9960
+			WHAM	Microsoft Video 1 (WHAM)
+			WINX	Winnov Software Compression
+			WJPG	AverMedia Winbond JPEG
+			WMV1	Windows Media Video V7
+			WMV2	Windows Media Video V8
+			WMV3	Windows Media Video V9
+			WNV1	Winnov Hardware Compression
+			XYZP	Extended PAL format XYZ palette (www.riff.org)
+			x263	Xirlink H.263
+			XLV0	NetXL Video Decoder
+			XMPG	Xing MPEG (I-Frame only)
+			XVID	XviD MPEG-4 (www.xvid.org)
+			XXAN	?XXAN?
+			YU92	Intel YUV (YU92)
+			YUNV	Nvidia Uncompressed YUV 4:2:2
+			YUVP	Extended PAL format YUV palette (www.riff.org)
+			Y211	YUV 2:1:1 Packed
+			Y411	YUV 4:1:1 Packed
+			Y41B	Weitek YUV 4:1:1 Planar
+			Y41P	Brooktree PC1 YUV 4:1:1 Packed
+			Y41T	Brooktree PC1 YUV 4:1:1 with transparency
+			Y42B	Weitek YUV 4:2:2 Planar
+			Y42T	Brooktree UYUV 4:2:2 with transparency
+			Y422	ADS Technologies Copy of UYVY used in Pyro WebCam firewire camera
+			Y800	Simple, single Y plane for monochrome images
+			Y8  	Grayscale video
+			YC12	Intel YUV 12 codec
+			YUV8	Winnov Caviar YUV8
+			YUV9	Intel YUV9
+			YUY2	Uncompressed YUV 4:2:2
+			YUYV	Canopus YUV
+			YV12	YVU12 Planar
+			YVU9	Intel YVU9 Planar (8-bpp Y plane, followed by 8-bpp 4x4 U and V planes)
+			YVYU	YVYU 4:2:2 Packed
+			ZLIB	Lossless Codec Library zlib compression (www.geocities.co.jp/Playtown-Denei/2837/LRC.htm)
+			ZPEG	Metheus Video Zipper
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($fourcc, $begin, __LINE__, __FILE__, 'riff-fourcc');
+	}
+
+
+	function EitherEndian2Int(&$ThisFileInfo, $byteword, $signed=false) {
+		if ($ThisFileInfo['fileformat'] == 'riff') {
+			return getid3_lib::LittleEndian2Int($byteword, $signed);
+		}
+		return getid3_lib::BigEndian2Int($byteword, false, $signed);
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio-video.swf.php b/apps/media/getID3/getid3/module.audio-video.swf.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3dbb366bc5b8043d6d1e8d976a71bead6346bf1
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio-video.swf.php
@@ -0,0 +1,149 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio-video.swf.php                                  //
+// module for analyzing Shockwave Flash files                  //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_swf
+{
+
+	function getid3_swf(&$fd, &$ThisFileInfo, $ReturnAllTagData=false) {
+//$start_time = microtime(true);
+		$ThisFileInfo['fileformat']          = 'swf';
+		$ThisFileInfo['video']['dataformat'] = 'swf';
+
+		// http://www.openswf.org/spec/SWFfileformat.html
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$SWFfileData = fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']); // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
+
+		$ThisFileInfo['swf']['header']['signature']  = substr($SWFfileData, 0, 3);
+		switch ($ThisFileInfo['swf']['header']['signature']) {
+			case 'FWS':
+				$ThisFileInfo['swf']['header']['compressed'] = false;
+				break;
+
+			case 'CWS':
+				$ThisFileInfo['swf']['header']['compressed'] = true;
+				break;
+
+			default:
+				$ThisFileInfo['error'][] = 'Expecting "FWS" or "CWS" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['swf']['header']['signature'].'"';
+				unset($ThisFileInfo['swf']);
+				unset($ThisFileInfo['fileformat']);
+				return false;
+				break;
+		}
+		$ThisFileInfo['swf']['header']['version'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 3, 1));
+		$ThisFileInfo['swf']['header']['length']  = getid3_lib::LittleEndian2Int(substr($SWFfileData, 4, 4));
+
+//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
+
+		if ($ThisFileInfo['swf']['header']['compressed']) {
+
+			$SWFHead     = substr($SWFfileData, 0, 8);
+			$SWFfileData = substr($SWFfileData, 8);
+			if ($decompressed = @gzuncompress($SWFfileData)) {
+
+				$SWFfileData = $SWFHead.$decompressed;
+
+			} else {
+
+				$ThisFileInfo['error'][] = 'Error decompressing compressed SWF data ('.strlen($SWFfileData).' bytes compressed, should be '.($ThisFileInfo['swf']['header']['length'] - 8).' bytes uncompressed)';
+				return false;
+
+			}
+
+		}
+//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
+
+		$FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xF8) >> 3;
+		$FrameSizeDataLength   = ceil((5 + (4 * $FrameSizeBitsPerValue)) / 8);
+		$FrameSizeDataString   = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x07), 3, '0', STR_PAD_LEFT);
+		for ($i = 1; $i < $FrameSizeDataLength; $i++) {
+			$FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
+		}
+		list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
+		$ThisFileInfo['swf']['header']['frame_width']  = getid3_lib::Bin2Dec($X2);
+		$ThisFileInfo['swf']['header']['frame_height'] = getid3_lib::Bin2Dec($Y2);
+
+		// http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
+		// Next in the header is the frame rate, which is kind of weird.
+		// It is supposed to be stored as a 16bit integer, but the first byte
+		// (or last depending on how you look at it) is completely ignored.
+		// Example: 0x000C  ->  0x0C  ->  12     So the frame rate is 12 fps.
+
+		// Byte at (8 + $FrameSizeDataLength) is always zero and ignored
+		$ThisFileInfo['swf']['header']['frame_rate']  = getid3_lib::LittleEndian2Int(substr($SWFfileData,  9 + $FrameSizeDataLength, 1));
+		$ThisFileInfo['swf']['header']['frame_count'] = getid3_lib::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
+
+		$ThisFileInfo['video']['frame_rate']         = $ThisFileInfo['swf']['header']['frame_rate'];
+		$ThisFileInfo['video']['resolution_x']       = intval(round($ThisFileInfo['swf']['header']['frame_width']  / 20));
+		$ThisFileInfo['video']['resolution_y']       = intval(round($ThisFileInfo['swf']['header']['frame_height'] / 20));
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		if (($ThisFileInfo['swf']['header']['frame_count'] > 0) && ($ThisFileInfo['swf']['header']['frame_rate'] > 0)) {
+			$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['swf']['header']['frame_count'] / $ThisFileInfo['swf']['header']['frame_rate'];
+		}
+//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
+
+
+		// SWF tags
+
+		$CurrentOffset = 12 + $FrameSizeDataLength;
+		$SWFdataLength = strlen($SWFfileData);
+
+		while ($CurrentOffset < $SWFdataLength) {
+//echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
+
+			$TagIDTagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
+			$TagID     = ($TagIDTagLength & 0xFFFC) >> 6;
+			$TagLength = ($TagIDTagLength & 0x003F);
+			$CurrentOffset += 2;
+			if ($TagLength == 0x3F) {
+				$TagLength = getid3_lib::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
+				$CurrentOffset += 4;
+			}
+
+			unset($TagData);
+			$TagData['offset'] = $CurrentOffset;
+			$TagData['size']   = $TagLength;
+			$TagData['id']     = $TagID;
+			$TagData['data']   = substr($SWFfileData, $CurrentOffset, $TagLength);
+			switch ($TagID) {
+				case 0: // end of movie
+					break 2;
+
+				case 9: // Set background color
+					//$ThisFileInfo['swf']['tags'][] = $TagData;
+					$ThisFileInfo['swf']['bgcolor'] = strtoupper(str_pad(dechex(getid3_lib::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
+					break;
+
+				default:
+					if ($ReturnAllTagData) {
+						$ThisFileInfo['swf']['tags'][] = $TagData;
+					}
+					break;
+			}
+
+			$CurrentOffset += $TagLength;
+		}
+
+		return true;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.aac.php b/apps/media/getID3/getid3/module.audio.aac.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ab3e99f852b260b946c8d4bf8c3dfa229458421
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.aac.php
@@ -0,0 +1,542 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.aac.php                                        //
+// module for analyzing AAC Audio files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_aac
+{
+
+	// new combined constructor
+	function getid3_aac(&$fd, &$ThisFileInfo, $option) {
+
+		if ($option === 'adif') {
+			$this->getAACADIFheaderFilepointer($fd, $ThisFileInfo);
+		}
+		elseif ($option === 'adts') {
+			$this->getAACADTSheaderFilepointer($fd, $ThisFileInfo);
+		}
+	}
+
+
+
+	function getAACADIFheaderFilepointer(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']          = 'aac';
+		$ThisFileInfo['audio']['dataformat'] = 'aac';
+		$ThisFileInfo['audio']['lossless']   = false;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$AACheader = fread($fd, 1024);
+		$offset    = 0;
+
+		if (substr($AACheader, 0, 4) == 'ADIF') {
+
+			// http://faac.sourceforge.net/wiki/index.php?page=ADIF
+
+			// http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
+			// adif_header() {
+			//     adif_id                                32
+			//     copyright_id_present                    1
+			//     if( copyright_id_present )
+			//         copyright_id                       72
+			//     original_copy                           1
+			//     home                                    1
+			//     bitstream_type                          1
+			//     bitrate                                23
+			//     num_program_config_elements             4
+			//     for (i = 0; i < num_program_config_elements + 1; i++ ) {
+			//         if( bitstream_type == '0' )
+			//             adif_buffer_fullness           20
+			//         program_config_element()
+			//     }
+			// }
+
+			$AACheaderBitstream = getid3_lib::BigEndian2Bin($AACheader);
+			$bitoffset          = 0;
+
+			$ThisFileInfo['aac']['header_type']                   = 'ADIF';
+			$bitoffset += 32;
+			$ThisFileInfo['aac']['header']['mpeg_version']        = 4;
+
+			$ThisFileInfo['aac']['header']['copyright']           = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
+			$bitoffset += 1;
+			if ($ThisFileInfo['aac']['header']['copyright']) {
+				$ThisFileInfo['aac']['header']['copyright_id']    = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 72));
+				$bitoffset += 72;
+			}
+			$ThisFileInfo['aac']['header']['original_copy']       = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
+			$bitoffset += 1;
+			$ThisFileInfo['aac']['header']['home']                = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
+			$bitoffset += 1;
+			$ThisFileInfo['aac']['header']['is_vbr']              = (bool) (substr($AACheaderBitstream, $bitoffset, 1) == '1');
+			$bitoffset += 1;
+			if ($ThisFileInfo['aac']['header']['is_vbr']) {
+				$ThisFileInfo['audio']['bitrate_mode']            = 'vbr';
+				$ThisFileInfo['aac']['header']['bitrate_max']     = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
+				$bitoffset += 23;
+			} else {
+				$ThisFileInfo['audio']['bitrate_mode']            = 'cbr';
+				$ThisFileInfo['aac']['header']['bitrate']         = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 23));
+				$bitoffset += 23;
+				$ThisFileInfo['audio']['bitrate']                 = $ThisFileInfo['aac']['header']['bitrate'];
+			}
+			if ($ThisFileInfo['audio']['bitrate'] == 0) {
+				$ThisFileInfo['error'][] = 'Corrupt AAC file: bitrate_audio == zero';
+				return false;
+			}
+			$ThisFileInfo['aac']['header']['num_program_configs'] = 1 + getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+			$bitoffset += 4;
+
+			for ($i = 0; $i < $ThisFileInfo['aac']['header']['num_program_configs']; $i++) {
+				// http://www.audiocoding.com/wiki/index.php?page=program_config_element
+
+				// buffer_fullness                       20
+
+				// element_instance_tag                   4
+				// object_type                            2
+				// sampling_frequency_index               4
+				// num_front_channel_elements             4
+				// num_side_channel_elements              4
+				// num_back_channel_elements              4
+				// num_lfe_channel_elements               2
+				// num_assoc_data_elements                3
+				// num_valid_cc_elements                  4
+				// mono_mixdown_present                   1
+				// mono_mixdown_element_number            4   if mono_mixdown_present == 1
+				// stereo_mixdown_present                 1
+				// stereo_mixdown_element_number          4   if stereo_mixdown_present == 1
+				// matrix_mixdown_idx_present             1
+				// matrix_mixdown_idx                     2   if matrix_mixdown_idx_present == 1
+				// pseudo_surround_enable                 1   if matrix_mixdown_idx_present == 1
+				// for (i = 0; i < num_front_channel_elements; i++) {
+				//     front_element_is_cpe[i]            1
+				//     front_element_tag_select[i]        4
+				// }
+				// for (i = 0; i < num_side_channel_elements; i++) {
+				//     side_element_is_cpe[i]             1
+				//     side_element_tag_select[i]         4
+				// }
+				// for (i = 0; i < num_back_channel_elements; i++) {
+				//     back_element_is_cpe[i]             1
+				//     back_element_tag_select[i]         4
+				// }
+				// for (i = 0; i < num_lfe_channel_elements; i++) {
+				//     lfe_element_tag_select[i]          4
+				// }
+				// for (i = 0; i < num_assoc_data_elements; i++) {
+				//     assoc_data_element_tag_select[i]   4
+				// }
+				// for (i = 0; i < num_valid_cc_elements; i++) {
+				//     cc_element_is_ind_sw[i]            1
+				//     valid_cc_element_tag_select[i]     4
+				// }
+				// byte_alignment()                       VAR
+				// comment_field_bytes                    8
+				// for (i = 0; i < comment_field_bytes; i++) {
+				//     comment_field_data[i]              8
+				// }
+
+				if (!$ThisFileInfo['aac']['header']['is_vbr']) {
+					$ThisFileInfo['aac']['program_configs'][$i]['buffer_fullness']        = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 20));
+					$bitoffset += 20;
+				}
+				$ThisFileInfo['aac']['program_configs'][$i]['element_instance_tag']       = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['program_configs'][$i]['object_type']                = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
+				$bitoffset += 2;
+				$ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']   = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']  = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']  = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']   = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
+				$bitoffset += 2;
+				$ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']    = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
+				$bitoffset += 3;
+				$ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']      = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']       = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+				$bitoffset += 1;
+				if ($ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_present']) {
+					$ThisFileInfo['aac']['program_configs'][$i]['mono_mixdown_element_number']    = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				$ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']             = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+				$bitoffset += 1;
+				if ($ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_present']) {
+					$ThisFileInfo['aac']['program_configs'][$i]['stereo_mixdown_element_number']  = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				$ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']         = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+				$bitoffset += 1;
+				if ($ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx_present']) {
+					$ThisFileInfo['aac']['program_configs'][$i]['matrix_mixdown_idx']             = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
+					$bitoffset += 2;
+					$ThisFileInfo['aac']['program_configs'][$i]['pseudo_surround_enable']         = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+				}
+				for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_front_channel_elements']; $j++) {
+					$ThisFileInfo['aac']['program_configs'][$i]['front_element_is_cpe'][$j]     = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+					$ThisFileInfo['aac']['program_configs'][$i]['front_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_side_channel_elements']; $j++) {
+					$ThisFileInfo['aac']['program_configs'][$i]['side_element_is_cpe'][$j]     = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+					$ThisFileInfo['aac']['program_configs'][$i]['side_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_back_channel_elements']; $j++) {
+					$ThisFileInfo['aac']['program_configs'][$i]['back_element_is_cpe'][$j]     = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+					$ThisFileInfo['aac']['program_configs'][$i]['back_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_lfe_channel_elements']; $j++) {
+					$ThisFileInfo['aac']['program_configs'][$i]['lfe_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_assoc_data_elements']; $j++) {
+					$ThisFileInfo['aac']['program_configs'][$i]['assoc_data_element_tag_select'][$j] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+				for ($j = 0; $j < $ThisFileInfo['aac']['program_configs'][$i]['num_valid_cc_elements']; $j++) {
+					$ThisFileInfo['aac']['program_configs'][$i]['cc_element_is_ind_sw'][$j]          = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+					$ThisFileInfo['aac']['program_configs'][$i]['valid_cc_element_tag_select'][$j]   = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+					$bitoffset += 4;
+				}
+
+				$bitoffset = ceil($bitoffset / 8) * 8;
+
+				$ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 8));
+				$bitoffset += 8;
+				$ThisFileInfo['aac']['program_configs'][$i]['comment_field']       = getid3_lib::Bin2String(substr($AACheaderBitstream, $bitoffset, 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes']));
+				$bitoffset += 8 * $ThisFileInfo['aac']['program_configs'][$i]['comment_field_bytes'];
+
+
+				$ThisFileInfo['aac']['header']['profile_text']                      = $this->AACprofileLookup($ThisFileInfo['aac']['program_configs'][$i]['object_type'], $ThisFileInfo['aac']['header']['mpeg_version']);
+				$ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency']   = $this->AACsampleRateLookup($ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency_index']);
+				$ThisFileInfo['audio']['sample_rate']                               = $ThisFileInfo['aac']['program_configs'][$i]['sampling_frequency'];
+				$ThisFileInfo['audio']['channels']                                  = $this->AACchannelCountCalculate($ThisFileInfo['aac']['program_configs'][$i]);
+				if ($ThisFileInfo['aac']['program_configs'][$i]['comment_field']) {
+					$ThisFileInfo['aac']['comments'][]                          = $ThisFileInfo['aac']['program_configs'][$i]['comment_field'];
+				}
+			}
+			$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
+
+			$ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text'];
+
+
+
+			return true;
+
+		} else {
+
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['aac']);
+			$ThisFileInfo['error'][] = 'AAC-ADIF synch not found at offset '.$ThisFileInfo['avdataoffset'].' (expected "ADIF", found "'.substr($AACheader, 0, 4).'" instead)';
+			return false;
+
+		}
+
+	}
+
+
+	function getAACADTSheaderFilepointer(&$fd, &$ThisFileInfo, $MaxFramesToScan=1000000, $ReturnExtendedInfo=false) {
+		// based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
+		// http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
+
+
+		// http://faac.sourceforge.net/wiki/index.php?page=ADTS
+
+		// * ADTS Fixed Header: these don't change from frame to frame
+		// syncword                                       12    always: '111111111111'
+		// ID                                              1    0: MPEG-4, 1: MPEG-2
+		// layer                                           2    always: '00'
+		// protection_absent                               1
+		// profile                                         2
+		// sampling_frequency_index                        4
+		// private_bit                                     1
+		// channel_configuration                           3
+		// original/copy                                   1
+		// home                                            1
+		// emphasis                                        2    only if ID == 0 (ie MPEG-4)
+
+		// * ADTS Variable Header: these can change from frame to frame
+		// copyright_identification_bit                    1
+		// copyright_identification_start                  1
+		// aac_frame_length                               13    length of the frame including header (in bytes)
+		// adts_buffer_fullness                           11    0x7FF indicates VBR
+		// no_raw_data_blocks_in_frame                     2
+
+		// * ADTS Error check
+		// crc_check                                      16    only if protection_absent == 0
+
+		$byteoffset  = 0;
+		$framenumber = 0;
+
+		// Init bit pattern array
+		static $decbin = array();
+
+		// Populate $bindec
+		for ($i = 0; $i < 256; $i++) {
+			$decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
+		}
+
+		// used to calculate bitrate below
+		$BitrateCache = array();
+
+
+		while (true) {
+			// breaks out when end-of-file encountered, or invalid data found,
+			// or MaxFramesToScan frames have been scanned
+
+			if ($byteoffset >= pow(2, 31)) {
+				$ThisFileInfo['warning'][] = 'Unable to parse AAC file beyond '.ftell($fd).' (PHP does not support file operations beyond 2GB)';
+				return false;
+			}
+			fseek($fd, $byteoffset, SEEK_SET);
+
+			// First get substring
+			$substring = fread($fd, 10);
+			$substringlength = strlen($substring);
+			if ($substringlength != 10) {
+				$ThisFileInfo['error'][] = 'Failed to read 10 bytes at offset '.(ftell($fd) - $substringlength).' (only read '.$substringlength.' bytes)';
+				return false;
+			}
+
+			// Initialise $AACheaderBitstream
+			$AACheaderBitstream = '';
+
+			// Loop thru substring chars
+			for ($i = 0; $i < 10; $i++) {
+				$AACheaderBitstream .= $decbin[$substring{$i}];
+			}
+
+			$bitoffset = 0;
+
+			$synctest = bindec(substr($AACheaderBitstream, $bitoffset, 12));
+
+			$bitoffset += 12;
+			if ($synctest != 0x0FFF) {
+				$ThisFileInfo['error'][] = 'Synch pattern (0x0FFF) not found at offset '.(ftell($fd) - 10).' (found 0x0'.strtoupper(dechex($synctest)).' instead)';
+				if ($ThisFileInfo['fileformat'] == 'aac') {
+					return true;
+				}
+				return false;
+			}
+
+			// Gather info for first frame only - this takes time to do 1000 times!
+			if ($framenumber > 0) {
+
+				if (!$AACheaderBitstream[$bitoffset]) {
+
+					// MPEG-4
+					$bitoffset += 20;
+
+				} else {
+
+					// MPEG-2
+					$bitoffset += 18;
+
+				}
+
+			} else {
+
+				$ThisFileInfo['aac']['header_type']                      = 'ADTS';
+				$ThisFileInfo['aac']['header']['synch']                  = $synctest;
+				$ThisFileInfo['fileformat']                              = 'aac';
+				$ThisFileInfo['audio']['dataformat']                     = 'aac';
+
+				$ThisFileInfo['aac']['header']['mpeg_version']           = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? 4 : 2);
+				$bitoffset += 1;
+				$ThisFileInfo['aac']['header']['layer']                  = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
+				$bitoffset += 2;
+				if ($ThisFileInfo['aac']['header']['layer'] != 0) {
+					$ThisFileInfo['error'][] = 'Layer error - expected 0x00, found 0x'.dechex($ThisFileInfo['aac']['header']['layer']).' instead';
+					return false;
+				}
+				$ThisFileInfo['aac']['header']['crc_present']            = ((substr($AACheaderBitstream, $bitoffset, 1) == '0') ? true : false);
+				$bitoffset += 1;
+				$ThisFileInfo['aac']['header']['profile_id']             = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
+				$bitoffset += 2;
+				$ThisFileInfo['aac']['header']['profile_text']           = $this->AACprofileLookup($ThisFileInfo['aac']['header']['profile_id'], $ThisFileInfo['aac']['header']['mpeg_version']);
+
+				$ThisFileInfo['aac']['header']['sample_frequency_index'] = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 4));
+				$bitoffset += 4;
+				$ThisFileInfo['aac']['header']['sample_frequency']       = $this->AACsampleRateLookup($ThisFileInfo['aac']['header']['sample_frequency_index']);
+				if ($ThisFileInfo['aac']['header']['sample_frequency'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt AAC file: sample_frequency == zero';
+					return false;
+				}
+				$ThisFileInfo['audio']['sample_rate']                    = $ThisFileInfo['aac']['header']['sample_frequency'];
+
+				$ThisFileInfo['aac']['header']['private']                = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+				$bitoffset += 1;
+				$ThisFileInfo['aac']['header']['channel_configuration']  = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 3));
+				$bitoffset += 3;
+				$ThisFileInfo['audio']['channels']                       = $ThisFileInfo['aac']['header']['channel_configuration'];
+				$ThisFileInfo['aac']['header']['original']               = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+				$bitoffset += 1;
+				$ThisFileInfo['aac']['header']['home']                   = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+				$bitoffset += 1;
+
+				if ($ThisFileInfo['aac']['header']['mpeg_version'] == 4) {
+					$ThisFileInfo['aac']['header']['emphasis']           = getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 2));
+					$bitoffset += 2;
+				}
+
+				if ($ReturnExtendedInfo) {
+
+					$ThisFileInfo['aac'][$framenumber]['copyright_id_bit']   = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+					$ThisFileInfo['aac'][$framenumber]['copyright_id_start'] = (bool) getid3_lib::Bin2Dec(substr($AACheaderBitstream, $bitoffset, 1));
+					$bitoffset += 1;
+
+				} else {
+
+					$bitoffset += 2;
+
+				}
+
+			}
+
+			$FrameLength = bindec(substr($AACheaderBitstream, $bitoffset, 13));
+
+			if (!isset($BitrateCache[$FrameLength])) {
+				$BitrateCache[$FrameLength] = ($ThisFileInfo['aac']['header']['sample_frequency'] / 1024) * $FrameLength * 8;
+			}
+			@$ThisFileInfo['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]]++;
+
+			$ThisFileInfo['aac'][$framenumber]['aac_frame_length']     = $FrameLength;
+			$bitoffset += 13;
+			$ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] = bindec(substr($AACheaderBitstream, $bitoffset, 11));
+			$bitoffset += 11;
+			if ($ThisFileInfo['aac'][$framenumber]['adts_buffer_fullness'] == 0x07FF) {
+				$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+			} else {
+				$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
+			}
+			$ThisFileInfo['aac'][$framenumber]['num_raw_data_blocks']  = bindec(substr($AACheaderBitstream, $bitoffset, 2));
+			$bitoffset += 2;
+
+			if ($ThisFileInfo['aac']['header']['crc_present']) {
+				//$ThisFileInfo['aac'][$framenumber]['crc']              = bindec(substr($AACheaderBitstream, $bitoffset, 16));
+				$bitoffset += 16;
+			}
+
+			if (!$ReturnExtendedInfo) {
+				unset($ThisFileInfo['aac'][$framenumber]);
+			}
+
+			$byteoffset += $FrameLength;
+			if ((++$framenumber < $MaxFramesToScan) && (($byteoffset + 10) < $ThisFileInfo['avdataend'])) {
+
+				// keep scanning
+
+			} else {
+
+				$ThisFileInfo['aac']['frames']    = $framenumber;
+				$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] / $byteoffset) * (($framenumber * 1024) / $ThisFileInfo['aac']['header']['sample_frequency']);  // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
+				if ($ThisFileInfo['playtime_seconds'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
+					return false;
+				}
+				$ThisFileInfo['audio']['bitrate']    = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+				ksort($ThisFileInfo['aac']['bitrate_distribution']);
+
+				$ThisFileInfo['audio']['encoder_options'] = $ThisFileInfo['aac']['header_type'].' '.$ThisFileInfo['aac']['header']['profile_text'];
+
+				return true;
+
+			}
+		}
+		// should never get here.
+	}
+
+	function AACsampleRateLookup($samplerateid) {
+		static $AACsampleRateLookup = array();
+		if (empty($AACsampleRateLookup)) {
+			$AACsampleRateLookup[0]  = 96000;
+			$AACsampleRateLookup[1]  = 88200;
+			$AACsampleRateLookup[2]  = 64000;
+			$AACsampleRateLookup[3]  = 48000;
+			$AACsampleRateLookup[4]  = 44100;
+			$AACsampleRateLookup[5]  = 32000;
+			$AACsampleRateLookup[6]  = 24000;
+			$AACsampleRateLookup[7]  = 22050;
+			$AACsampleRateLookup[8]  = 16000;
+			$AACsampleRateLookup[9]  = 12000;
+			$AACsampleRateLookup[10] = 11025;
+			$AACsampleRateLookup[11] = 8000;
+			$AACsampleRateLookup[12] = 0;
+			$AACsampleRateLookup[13] = 0;
+			$AACsampleRateLookup[14] = 0;
+			$AACsampleRateLookup[15] = 0;
+		}
+		return (isset($AACsampleRateLookup[$samplerateid]) ? $AACsampleRateLookup[$samplerateid] : 'invalid');
+	}
+
+	function AACprofileLookup($profileid, $mpegversion) {
+		static $AACprofileLookup = array();
+		if (empty($AACprofileLookup)) {
+			$AACprofileLookup[2][0]  = 'Main profile';
+			$AACprofileLookup[2][1]  = 'Low Complexity profile (LC)';
+			$AACprofileLookup[2][2]  = 'Scalable Sample Rate profile (SSR)';
+			$AACprofileLookup[2][3]  = '(reserved)';
+			$AACprofileLookup[4][0]  = 'AAC_MAIN';
+			$AACprofileLookup[4][1]  = 'AAC_LC';
+			$AACprofileLookup[4][2]  = 'AAC_SSR';
+			$AACprofileLookup[4][3]  = 'AAC_LTP';
+		}
+		return (isset($AACprofileLookup[$mpegversion][$profileid]) ? $AACprofileLookup[$mpegversion][$profileid] : 'invalid');
+	}
+
+	function AACchannelCountCalculate($program_configs) {
+		$channels = 0;
+		for ($i = 0; $i < $program_configs['num_front_channel_elements']; $i++) {
+			$channels++;
+			if ($program_configs['front_element_is_cpe'][$i]) {
+				// each front element is channel pair (CPE = Channel Pair Element)
+				$channels++;
+			}
+		}
+		for ($i = 0; $i < $program_configs['num_side_channel_elements']; $i++) {
+			$channels++;
+			if ($program_configs['side_element_is_cpe'][$i]) {
+				// each side element is channel pair (CPE = Channel Pair Element)
+				$channels++;
+			}
+		}
+		for ($i = 0; $i < $program_configs['num_back_channel_elements']; $i++) {
+			$channels++;
+			if ($program_configs['back_element_is_cpe'][$i]) {
+				// each back element is channel pair (CPE = Channel Pair Element)
+				$channels++;
+			}
+		}
+		for ($i = 0; $i < $program_configs['num_lfe_channel_elements']; $i++) {
+			$channels++;
+		}
+		return $channels;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.ac3.php b/apps/media/getID3/getid3/module.audio.ac3.php
new file mode 100644
index 0000000000000000000000000000000000000000..9809a47c7b039e16b9de0be7dee6b9926a030afb
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.ac3.php
@@ -0,0 +1,497 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.ac3.php                                        //
+// module for analyzing AC-3 (aka Dolby Digital) audio files   //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_ac3
+{
+
+	function getid3_ac3(&$fd, &$ThisFileInfo) {
+
+		///AH
+		$ThisFileInfo['ac3']['raw']['bsi'] = array();
+		$thisfile_ac3                      = &$ThisFileInfo['ac3'];
+		$thisfile_ac3_raw                  = &$thisfile_ac3['raw'];
+		$thisfile_ac3_raw_bsi              = &$thisfile_ac3_raw['bsi'];
+
+
+		// http://www.atsc.org/standards/a_52a.pdf
+
+		$ThisFileInfo['fileformat']            = 'ac3';
+		$ThisFileInfo['audio']['dataformat']   = 'ac3';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
+		$ThisFileInfo['audio']['lossless']     = false;
+
+		// An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
+		// Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
+		// new audio samples per channel. A synchronization information (SI) header at the beginning
+		// of each frame contains information needed to acquire and maintain synchronization. A
+		// bit stream information (BSI) header follows SI, and contains parameters describing the coded
+		// audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
+		// end of each frame is an error check field that includes a CRC word for error detection. An
+		// additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
+		//
+		// syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$AC3header['syncinfo'] = fread($fd, 5);
+		$thisfile_ac3_raw['synchinfo']['synchword'] = substr($AC3header['syncinfo'], 0, 2);
+
+		if ($thisfile_ac3_raw['synchinfo']['synchword'] != "\x0B\x77") {
+
+			$ThisFileInfo['error'][] = 'Expecting "\x0B\x77" at offset '.$ThisFileInfo['avdataoffset'].', found \x'.strtoupper(dechex($AC3header['syncinfo']{0})).'\x'.strtoupper(dechex($AC3header['syncinfo']{1})).' instead';
+			unset($thisfile_ac3);
+			return false;
+
+		} else {
+
+			// syncinfo() {
+			// 	 syncword    16
+			// 	 crc1        16
+			// 	 fscod        2
+			// 	 frmsizecod   6
+			// } /* end of syncinfo */
+
+			$thisfile_ac3_raw['synchinfo']['crc1']       = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 2, 2));
+			$ac3_synchinfo_fscod_frmsizecod                        = getid3_lib::LittleEndian2Int(substr($AC3header['syncinfo'], 4, 1));
+			$thisfile_ac3_raw['synchinfo']['fscod']      = ($ac3_synchinfo_fscod_frmsizecod & 0xC0) >> 6;
+			$thisfile_ac3_raw['synchinfo']['frmsizecod'] = ($ac3_synchinfo_fscod_frmsizecod & 0x3F);
+
+			$thisfile_ac3['sample_rate'] = $this->AC3sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
+			if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
+				$ThisFileInfo['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
+			}
+
+			$thisfile_ac3['frame_length'] = $this->AC3frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
+			$thisfile_ac3['bitrate']      = $this->AC3bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
+			$ThisFileInfo['audio']['bitrate'] = $thisfile_ac3['bitrate'];
+
+			$AC3header['bsi'] = getid3_lib::BigEndian2Bin(fread($fd, 15));
+			$ac3_bsi_offset = 0;
+
+			$thisfile_ac3_raw_bsi['bsid'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
+			$ac3_bsi_offset += 5;
+			if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
+				// Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
+				// If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
+				// Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
+				$ThisFileInfo['error'][] = 'Bit stream identification is version '.$thisfile_ac3_raw_bsi['bsid'].', but getID3() only understands up to version 8';
+				unset($thisfile_ac3);
+				return false;
+			}
+
+			$thisfile_ac3_raw_bsi['bsmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
+			$ac3_bsi_offset += 3;
+			$thisfile_ac3_raw_bsi['acmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 3));
+			$ac3_bsi_offset += 3;
+
+			$thisfile_ac3['service_type'] = $this->AC3serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
+			$ac3_coding_mode = $this->AC3audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
+			foreach($ac3_coding_mode as $key => $value) {
+				$thisfile_ac3[$key] = $value;
+			}
+			switch ($thisfile_ac3_raw_bsi['acmod']) {
+				case 0:
+				case 1:
+					$ThisFileInfo['audio']['channelmode'] = 'mono';
+					break;
+				case 3:
+				case 4:
+					$ThisFileInfo['audio']['channelmode'] = 'stereo';
+					break;
+				default:
+					$ThisFileInfo['audio']['channelmode'] = 'surround';
+					break;
+			}
+			$ThisFileInfo['audio']['channels'] = $thisfile_ac3['num_channels'];
+
+			if ($thisfile_ac3_raw_bsi['acmod'] & 0x01) {
+				// If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
+				$thisfile_ac3_raw_bsi['cmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
+				$ac3_bsi_offset += 2;
+				$thisfile_ac3['center_mix_level'] = $this->AC3centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
+			}
+
+			if ($thisfile_ac3_raw_bsi['acmod'] & 0x04) {
+				// If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
+				$thisfile_ac3_raw_bsi['surmixlev'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
+				$ac3_bsi_offset += 2;
+				$thisfile_ac3['surround_mix_level'] = $this->AC3surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
+			}
+
+			if ($thisfile_ac3_raw_bsi['acmod'] == 0x02) {
+				// When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
+				$thisfile_ac3_raw_bsi['dsurmod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
+				$ac3_bsi_offset += 2;
+				$thisfile_ac3['dolby_surround_mode'] = $this->AC3dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
+			}
+
+			$thisfile_ac3_raw_bsi['lfeon'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			$thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
+			if ($thisfile_ac3_raw_bsi['lfeon']) {
+				//$ThisFileInfo['audio']['channels']++;
+				$ThisFileInfo['audio']['channels'] .= '.1';
+			}
+
+			$thisfile_ac3['channels_enabled'] = $this->AC3channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
+
+			// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
+			// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
+			$thisfile_ac3_raw_bsi['dialnorm'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
+			$ac3_bsi_offset += 5;
+			$thisfile_ac3['dialogue_normalization'] = '-'.$thisfile_ac3_raw_bsi['dialnorm'].'dB';
+
+			$thisfile_ac3_raw_bsi['compre_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			if ($thisfile_ac3_raw_bsi['compre_flag']) {
+				$thisfile_ac3_raw_bsi['compr'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
+				$ac3_bsi_offset += 8;
+				$thisfile_ac3['heavy_compression'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr']);
+			}
+
+			$thisfile_ac3_raw_bsi['langcode_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			if ($thisfile_ac3_raw_bsi['langcode_flag']) {
+				$thisfile_ac3_raw_bsi['langcod'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
+				$ac3_bsi_offset += 8;
+			}
+
+			$thisfile_ac3_raw_bsi['audprodie'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			if ($thisfile_ac3_raw_bsi['audprodie']) {
+				$thisfile_ac3_raw_bsi['mixlevel'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
+				$ac3_bsi_offset += 5;
+				$thisfile_ac3_raw_bsi['roomtyp']  = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
+				$ac3_bsi_offset += 2;
+
+				$thisfile_ac3['mixing_level'] = (80 + $thisfile_ac3_raw_bsi['mixlevel']).'dB';
+				$thisfile_ac3['room_type']    = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
+			}
+
+			if ($thisfile_ac3_raw_bsi['acmod'] == 0x00) {
+				// If acmod is 0, then two completely independent program channels (dual mono)
+				// are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
+				// a number of additional items are present in BSI or audblk to fully describe Ch2.
+
+
+				// This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1–31.
+				// The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
+				$thisfile_ac3_raw_bsi['dialnorm2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
+				$ac3_bsi_offset += 5;
+				$thisfile_ac3['dialogue_normalization2'] = '-'.$thisfile_ac3_raw_bsi['dialnorm2'].'dB';
+
+				$thisfile_ac3_raw_bsi['compre_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+				$ac3_bsi_offset += 1;
+				if ($thisfile_ac3_raw_bsi['compre_flag2']) {
+					$thisfile_ac3_raw_bsi['compr2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
+					$ac3_bsi_offset += 8;
+					$thisfile_ac3['heavy_compression2'] = $this->AC3heavyCompression($thisfile_ac3_raw_bsi['compr2']);
+				}
+
+				$thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+				$ac3_bsi_offset += 1;
+				if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
+					$thisfile_ac3_raw_bsi['langcod2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 8));
+					$ac3_bsi_offset += 8;
+				}
+
+				$thisfile_ac3_raw_bsi['audprodie2'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+				$ac3_bsi_offset += 1;
+				if ($thisfile_ac3_raw_bsi['audprodie2']) {
+					$thisfile_ac3_raw_bsi['mixlevel2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 5));
+					$ac3_bsi_offset += 5;
+					$thisfile_ac3_raw_bsi['roomtyp2']  = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 2));
+					$ac3_bsi_offset += 2;
+
+					$thisfile_ac3['mixing_level2'] = (80 + $thisfile_ac3_raw_bsi['mixlevel2']).'dB';
+					$thisfile_ac3['room_type2']    = $this->AC3roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
+				}
+
+			}
+
+			$thisfile_ac3_raw_bsi['copyright'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+
+			$thisfile_ac3_raw_bsi['original']  = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+
+			$thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
+				$thisfile_ac3_raw_bsi['timecode1'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14));
+				$ac3_bsi_offset += 14;
+			}
+
+			$thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
+				$thisfile_ac3_raw_bsi['timecode2'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 14));
+				$ac3_bsi_offset += 14;
+			}
+
+			$thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 1));
+			$ac3_bsi_offset += 1;
+			if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
+				$thisfile_ac3_raw_bsi['addbsi_length'] = bindec(substr($AC3header['bsi'], $ac3_bsi_offset, 6));
+				$ac3_bsi_offset += 6;
+
+				$AC3header['bsi'] .= getid3_lib::BigEndian2Bin(fread($fd, $thisfile_ac3_raw_bsi['addbsi_length']));
+
+				$thisfile_ac3_raw_bsi['addbsi_data'] = substr($AC3header['bsi'], $ac3_bsi_offset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
+				$ac3_bsi_offset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
+			}
+
+		}
+
+		return true;
+	}
+
+
+	function AC3sampleRateCodeLookup($fscod) {
+		static $AC3sampleRateCodeLookup = array(
+			0 => 48000,
+			1 => 44100,
+			2 => 32000,
+			3 => 'reserved' // If the reserved code is indicated, the decoder should not attempt to decode audio and should mute.
+		);
+		return (isset($AC3sampleRateCodeLookup[$fscod]) ? $AC3sampleRateCodeLookup[$fscod] : false);
+	}
+
+	function AC3serviceTypeLookup($bsmod, $acmod) {
+		static $AC3serviceTypeLookup = array();
+		if (empty($AC3serviceTypeLookup)) {
+			for ($i = 0; $i <= 7; $i++) {
+				$AC3serviceTypeLookup[0][$i] = 'main audio service: complete main (CM)';
+				$AC3serviceTypeLookup[1][$i] = 'main audio service: music and effects (ME)';
+				$AC3serviceTypeLookup[2][$i] = 'associated service: visually impaired (VI)';
+				$AC3serviceTypeLookup[3][$i] = 'associated service: hearing impaired (HI)';
+				$AC3serviceTypeLookup[4][$i] = 'associated service: dialogue (D)';
+				$AC3serviceTypeLookup[5][$i] = 'associated service: commentary (C)';
+				$AC3serviceTypeLookup[6][$i] = 'associated service: emergency (E)';
+			}
+
+			$AC3serviceTypeLookup[7][1]      = 'associated service: voice over (VO)';
+			for ($i = 2; $i <= 7; $i++) {
+				$AC3serviceTypeLookup[7][$i] = 'main audio service: karaoke';
+			}
+		}
+		return (isset($AC3serviceTypeLookup[$bsmod][$acmod]) ? $AC3serviceTypeLookup[$bsmod][$acmod] : false);
+	}
+
+	function AC3audioCodingModeLookup($acmod) {
+		static $AC3audioCodingModeLookup = array();
+		if (empty($AC3audioCodingModeLookup)) {
+			// array(channel configuration, # channels (not incl LFE), channel order)
+			$AC3audioCodingModeLookup = array (
+				0 => array('channel_config'=>'1+1', 'num_channels'=>2, 'channel_order'=>'Ch1,Ch2'),
+				1 => array('channel_config'=>'1/0', 'num_channels'=>1, 'channel_order'=>'C'),
+				2 => array('channel_config'=>'2/0', 'num_channels'=>2, 'channel_order'=>'L,R'),
+				3 => array('channel_config'=>'3/0', 'num_channels'=>3, 'channel_order'=>'L,C,R'),
+				4 => array('channel_config'=>'2/1', 'num_channels'=>3, 'channel_order'=>'L,R,S'),
+				5 => array('channel_config'=>'3/1', 'num_channels'=>4, 'channel_order'=>'L,C,R,S'),
+				6 => array('channel_config'=>'2/2', 'num_channels'=>4, 'channel_order'=>'L,R,SL,SR'),
+				7 => array('channel_config'=>'3/2', 'num_channels'=>5, 'channel_order'=>'L,C,R,SL,SR')
+			);
+		}
+		return (isset($AC3audioCodingModeLookup[$acmod]) ? $AC3audioCodingModeLookup[$acmod] : false);
+	}
+
+	function AC3centerMixLevelLookup($cmixlev) {
+		static $AC3centerMixLevelLookup;
+		if (empty($AC3centerMixLevelLookup)) {
+			$AC3centerMixLevelLookup = array(
+				0 => pow(2, -3.0 / 6), // 0.707 (–3.0 dB)
+				1 => pow(2, -4.5 / 6), // 0.595 (–4.5 dB)
+				2 => pow(2, -6.0 / 6), // 0.500 (–6.0 dB)
+				3 => 'reserved'
+			);
+		}
+		return (isset($AC3centerMixLevelLookup[$cmixlev]) ? $AC3centerMixLevelLookup[$cmixlev] : false);
+	}
+
+	function AC3surroundMixLevelLookup($surmixlev) {
+		static $AC3surroundMixLevelLookup;
+		if (empty($AC3surroundMixLevelLookup)) {
+			$AC3surroundMixLevelLookup = array(
+				0 => pow(2, -3.0 / 6),
+				1 => pow(2, -6.0 / 6),
+				2 => 0,
+				3 => 'reserved'
+			);
+		}
+		return (isset($AC3surroundMixLevelLookup[$surmixlev]) ? $AC3surroundMixLevelLookup[$surmixlev] : false);
+	}
+
+	function AC3dolbySurroundModeLookup($dsurmod) {
+		static $AC3dolbySurroundModeLookup = array(
+			0 => 'not indicated',
+			1 => 'Not Dolby Surround encoded',
+			2 => 'Dolby Surround encoded',
+			3 => 'reserved'
+		);
+		return (isset($AC3dolbySurroundModeLookup[$dsurmod]) ? $AC3dolbySurroundModeLookup[$dsurmod] : false);
+	}
+
+	function AC3channelsEnabledLookup($acmod, $lfeon) {
+		$AC3channelsEnabledLookup = array(
+			'ch1'=>(bool) ($acmod == 0),
+			'ch2'=>(bool) ($acmod == 0),
+			'left'=>(bool) ($acmod > 1),
+			'right'=>(bool) ($acmod > 1),
+			'center'=>(bool) ($acmod & 0x01),
+			'surround_mono'=>false,
+			'surround_left'=>false,
+			'surround_right'=>false,
+			'lfe'=>$lfeon);
+		switch ($acmod) {
+			case 4:
+			case 5:
+				$AC3channelsEnabledLookup['surround_mono']  = true;
+				break;
+			case 6:
+			case 7:
+				$AC3channelsEnabledLookup['surround_left']  = true;
+				$AC3channelsEnabledLookup['surround_right'] = true;
+				break;
+		}
+		return $AC3channelsEnabledLookup;
+	}
+
+	function AC3heavyCompression($compre) {
+		// The first four bits indicate gain changes in 6.02dB increments which can be
+		// implemented with an arithmetic shift operation. The following four bits
+		// indicate linear gain changes, and require a 5-bit multiply.
+		// We will represent the two 4-bit fields of compr as follows:
+		//   X0 X1 X2 X3 . Y4 Y5 Y6 Y7
+		// The meaning of the X values is most simply described by considering X to represent a 4-bit
+		// signed integer with values from –8 to +7. The gain indicated by X is then (X + 1) * 6.02 dB. The
+		// following table shows this in detail.
+
+		// Meaning of 4 msb of compr
+		//  7    +48.16 dB
+		//  6    +42.14 dB
+		//  5    +36.12 dB
+		//  4    +30.10 dB
+		//  3    +24.08 dB
+		//  2    +18.06 dB
+		//  1    +12.04 dB
+		//  0     +6.02 dB
+		// -1         0 dB
+		// -2     –6.02 dB
+		// -3    –12.04 dB
+		// -4    –18.06 dB
+		// -5    –24.08 dB
+		// -6    –30.10 dB
+		// -7    –36.12 dB
+		// -8    –42.14 dB
+
+		$fourbit = str_pad(decbin(($compre & 0xF0) >> 4), 4, '0', STR_PAD_LEFT);
+		if ($fourbit{0} == '1') {
+			$log_gain = -8 + bindec(substr($fourbit, 1));
+		} else {
+			$log_gain = bindec(substr($fourbit, 1));
+		}
+		$log_gain = ($log_gain + 1) * getid3_lib::RGADamplitude2dB(2);
+
+		// The value of Y is a linear representation of a gain change of up to –6 dB. Y is considered to
+		// be an unsigned fractional integer, with a leading value of 1, or: 0.1 Y4 Y5 Y6 Y7 (base 2). Y can
+		// represent values between 0.111112 (or 31/32) and 0.100002 (or 1/2). Thus, Y can represent gain
+		// changes from –0.28 dB to –6.02 dB.
+
+		$lin_gain = (16 + ($compre & 0x0F)) / 32;
+
+		// The combination of X and Y values allows compr to indicate gain changes from
+		//  48.16 – 0.28 = +47.89 dB, to
+		// –42.14 – 6.02 = –48.16 dB.
+
+		return $log_gain - $lin_gain;
+	}
+
+	function AC3roomTypeLookup($roomtyp) {
+		static $AC3roomTypeLookup = array(
+			0 => 'not indicated',
+			1 => 'large room, X curve monitor',
+			2 => 'small room, flat monitor',
+			3 => 'reserved'
+		);
+		return (isset($AC3roomTypeLookup[$roomtyp]) ? $AC3roomTypeLookup[$roomtyp] : false);
+	}
+
+	function AC3frameSizeLookup($frmsizecod, $fscod) {
+		$padding     = (bool) ($frmsizecod % 2);
+		$framesizeid =   floor($frmsizecod / 2);
+
+		static $AC3frameSizeLookup = array();
+		if (empty($AC3frameSizeLookup)) {
+			$AC3frameSizeLookup = array (
+				0  => array(128, 138, 192),
+				1  => array(40, 160, 174, 240),
+				2  => array(48, 192, 208, 288),
+				3  => array(56, 224, 242, 336),
+				4  => array(64, 256, 278, 384),
+				5  => array(80, 320, 348, 480),
+				6  => array(96, 384, 416, 576),
+				7  => array(112, 448, 486, 672),
+				8  => array(128, 512, 556, 768),
+				9  => array(160, 640, 696, 960),
+				10 => array(192, 768, 834, 1152),
+				11 => array(224, 896, 974, 1344),
+				12 => array(256, 1024, 1114, 1536),
+				13 => array(320, 1280, 1392, 1920),
+				14 => array(384, 1536, 1670, 2304),
+				15 => array(448, 1792, 1950, 2688),
+				16 => array(512, 2048, 2228, 3072),
+				17 => array(576, 2304, 2506, 3456),
+				18 => array(640, 2560, 2786, 3840)
+			);
+		}
+		if (($fscod == 1) && $padding) {
+			// frame lengths are padded by 1 word (16 bits) at 44100
+			$AC3frameSizeLookup[$frmsizecod] += 2;
+		}
+		return (isset($AC3frameSizeLookup[$framesizeid][$fscod]) ? $AC3frameSizeLookup[$framesizeid][$fscod] : false);
+	}
+
+	function AC3bitrateLookup($frmsizecod) {
+		$framesizeid =   floor($frmsizecod / 2);
+
+		static $AC3bitrateLookup = array(
+			0  => 32000,
+			1  => 40000,
+			2  => 48000,
+			3  => 56000,
+			4  => 64000,
+			5  => 80000,
+			6  => 96000,
+			7  => 112000,
+			8  => 128000,
+			9  => 160000,
+			10 => 192000,
+			11 => 224000,
+			12 => 256000,
+			13 => 320000,
+			14 => 384000,
+			15 => 448000,
+			16 => 512000,
+			17 => 576000,
+			18 => 640000
+		);
+		return (isset($AC3bitrateLookup[$framesizeid]) ? $AC3bitrateLookup[$framesizeid] : false);
+	}
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.au.php b/apps/media/getID3/getid3/module.audio.au.php
new file mode 100644
index 0000000000000000000000000000000000000000..afbc75d6712ee7eb7e18edf9749a06098ea4545f
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.au.php
@@ -0,0 +1,163 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.au.php                                         //
+// module for analyzing AU files                               //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_au
+{
+
+	function getid3_au(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$AUheader  = fread($fd, 8);
+
+		if (substr($AUheader, 0, 4) != '.snd') {
+			$ThisFileInfo['error'][] = 'Expecting ".snd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($AUheader, 0, 4).'"';
+			return false;
+		}
+
+		// shortcut
+		$ThisFileInfo['au'] = array();
+		$thisfile_au        = &$ThisFileInfo['au'];
+
+		$ThisFileInfo['fileformat']            = 'au';
+		$ThisFileInfo['audio']['dataformat']   = 'au';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
+		$thisfile_au['encoding']               = 'ISO-8859-1';
+
+		$thisfile_au['header_length']   = getid3_lib::BigEndian2Int(substr($AUheader,  4, 4));
+		$AUheader .= fread($fd, $thisfile_au['header_length'] - 8);
+		$ThisFileInfo['avdataoffset'] += $thisfile_au['header_length'];
+
+		$thisfile_au['data_size']             = getid3_lib::BigEndian2Int(substr($AUheader,  8, 4));
+		$thisfile_au['data_format_id']        = getid3_lib::BigEndian2Int(substr($AUheader, 12, 4));
+		$thisfile_au['sample_rate']           = getid3_lib::BigEndian2Int(substr($AUheader, 16, 4));
+		$thisfile_au['channels']              = getid3_lib::BigEndian2Int(substr($AUheader, 20, 4));
+		$thisfile_au['comments']['comment'][] =                      trim(substr($AUheader, 24));
+
+		$thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
+		$thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
+		if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
+			$ThisFileInfo['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
+		} else {
+			unset($thisfile_au['bits_per_sample']);
+		}
+
+		$ThisFileInfo['audio']['sample_rate']  = $thisfile_au['sample_rate'];
+		$ThisFileInfo['audio']['channels']     = $thisfile_au['channels'];
+
+		if (($ThisFileInfo['avdataoffset'] + $thisfile_au['data_size']) > $ThisFileInfo['avdataend']) {
+			$ThisFileInfo['warning'][] = 'Possible truncated file - expecting "'.$thisfile_au['data_size'].'" bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' bytes"';
+		}
+
+		$ThisFileInfo['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
+		$ThisFileInfo['audio']['bitrate'] = ($thisfile_au['data_size'] * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+	}
+
+	function AUdataFormatNameLookup($id) {
+		static $AUdataFormatNameLookup = array(
+			0  => 'unspecified format',
+			1  => '8-bit mu-law',
+			2  => '8-bit linear',
+			3  => '16-bit linear',
+			4  => '24-bit linear',
+			5  => '32-bit linear',
+			6  => 'floating-point',
+			7  => 'double-precision float',
+			8  => 'fragmented sampled data',
+			9  => 'SUN_FORMAT_NESTED',
+			10 => 'DSP program',
+			11 => '8-bit fixed-point',
+			12 => '16-bit fixed-point',
+			13 => '24-bit fixed-point',
+			14 => '32-bit fixed-point',
+
+			16 => 'non-audio display data',
+			17 => 'SND_FORMAT_MULAW_SQUELCH',
+			18 => '16-bit linear with emphasis',
+			19 => '16-bit linear with compression',
+			20 => '16-bit linear with emphasis + compression',
+			21 => 'Music Kit DSP commands',
+			22 => 'SND_FORMAT_DSP_COMMANDS_SAMPLES',
+			23 => 'CCITT g.721 4-bit ADPCM',
+			24 => 'CCITT g.722 ADPCM',
+			25 => 'CCITT g.723 3-bit ADPCM',
+			26 => 'CCITT g.723 5-bit ADPCM',
+			27 => 'A-Law 8-bit'
+		);
+		return (isset($AUdataFormatNameLookup[$id]) ? $AUdataFormatNameLookup[$id] : false);
+	}
+
+	function AUdataFormatBitsPerSampleLookup($id) {
+		static $AUdataFormatBitsPerSampleLookup = array(
+			1  => 8,
+			2  => 8,
+			3  => 16,
+			4  => 24,
+			5  => 32,
+			6  => 32,
+			7  => 64,
+
+			11 => 8,
+			12 => 16,
+			13 => 24,
+			14 => 32,
+
+			18 => 16,
+			19 => 16,
+			20 => 16,
+
+			23 => 16,
+
+			25 => 16,
+			26 => 16,
+			27 => 8
+		);
+		return (isset($AUdataFormatBitsPerSampleLookup[$id]) ? $AUdataFormatBitsPerSampleLookup[$id] : false);
+	}
+
+	function AUdataFormatUsedBitsPerSampleLookup($id) {
+		static $AUdataFormatUsedBitsPerSampleLookup = array(
+			1  => 8,
+			2  => 8,
+			3  => 16,
+			4  => 24,
+			5  => 32,
+			6  => 32,
+			7  => 64,
+
+			11 => 8,
+			12 => 16,
+			13 => 24,
+			14 => 32,
+
+			18 => 16,
+			19 => 16,
+			20 => 16,
+
+			23 => 4,
+
+			25 => 3,
+			26 => 5,
+			27 => 8,
+		);
+		return (isset($AUdataFormatUsedBitsPerSampleLookup[$id]) ? $AUdataFormatUsedBitsPerSampleLookup[$id] : false);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.avr.php b/apps/media/getID3/getid3/module.audio.avr.php
new file mode 100644
index 0000000000000000000000000000000000000000..60994397e3a59777f214acdf86fdb20304caa98c
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.avr.php
@@ -0,0 +1,125 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.avr.php                                        //
+// module for analyzing AVR Audio files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_avr
+{
+
+	function getid3_avr(&$fd, &$ThisFileInfo) {
+
+		// http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
+		// http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
+		// offset    type    length    name        comments
+		// ---------------------------------------------------------------------
+		// 0    char    4    ID        format ID == "2BIT"
+		// 4    char    8    name        sample name (unused space filled with 0)
+		// 12    short    1    mono/stereo    0=mono, -1 (0xFFFF)=stereo
+		//                     With stereo, samples are alternated,
+		//                     the first voice is the left :
+		//                     (LRLRLRLRLRLRLRLRLR...)
+		// 14    short    1    resolution    8, 12 or 16 (bits)
+		// 16    short    1    signed or not    0=unsigned, -1 (0xFFFF)=signed
+		// 18    short    1    loop or not    0=no loop, -1 (0xFFFF)=loop on
+		// 20    short    1    MIDI note    0xFFnn, where 0 <= nn <= 127
+		//                     0xFFFF means "no MIDI note defined"
+		// 22    byte    1    Replay speed    Frequence in the Replay software
+		//                     0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
+		//                     3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
+		//                     6=43.885 Khz, 7=47.261 Khz
+		//                     -1 (0xFF)=no defined Frequence
+		// 23    byte    3    sample rate    in Hertz
+		// 26    long    1    size in bytes (2 * bytes in stereo)
+		// 30    long    1    loop begin    0 for no loop
+		// 34    long    1    loop size    equal to 'size' for no loop
+		// 38  short   2   Reserved, MIDI keyboard split */
+		// 40  short   2   Reserved, sample compression */
+		// 42  short   2   Reserved */
+		// 44  char   20;  Additional filename space, used if (name[7] != 0)
+		// 64    byte    64    user data
+		// 128    bytes    ?    sample data    (12 bits samples are coded on 16 bits:
+		//                     0000 xxxx xxxx xxxx)
+		// ---------------------------------------------------------------------
+
+		// Note that all values are in motorola (big-endian) format, and that long is
+		// assumed to be 4 bytes, and short 2 bytes.
+		// When reading the samples, you should handle both signed and unsigned data,
+		// and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
+		// 8-bit data between signed/unsigned just add 127 to the sample values.
+		// Simularly for 16-bit data you should add 32769
+
+		$ThisFileInfo['fileformat'] = 'avr';
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$AVRheader = fread($fd, 128);
+
+		$ThisFileInfo['avr']['raw']['magic']    =               substr($AVRheader,  0,  4);
+		if ($ThisFileInfo['avr']['raw']['magic'] != '2BIT') {
+			$ThisFileInfo['error'][] = 'Expecting "2BIT" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['avr']['raw']['magic'].'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['avr']);
+			return false;
+		}
+		$ThisFileInfo['avdataoffset'] += 128;
+
+		$ThisFileInfo['avr']['sample_name']        =         rtrim(substr($AVRheader,  4,  8));
+		$ThisFileInfo['avr']['raw']['mono']        = getid3_lib::BigEndian2Int(substr($AVRheader, 12,  2));
+		$ThisFileInfo['avr']['bits_per_sample']    = getid3_lib::BigEndian2Int(substr($AVRheader, 14,  2));
+		$ThisFileInfo['avr']['raw']['signed']      = getid3_lib::BigEndian2Int(substr($AVRheader, 16,  2));
+		$ThisFileInfo['avr']['raw']['loop']        = getid3_lib::BigEndian2Int(substr($AVRheader, 18,  2));
+		$ThisFileInfo['avr']['raw']['midi']        = getid3_lib::BigEndian2Int(substr($AVRheader, 20,  2));
+		$ThisFileInfo['avr']['raw']['replay_freq'] = getid3_lib::BigEndian2Int(substr($AVRheader, 22,  1));
+		$ThisFileInfo['avr']['sample_rate']        = getid3_lib::BigEndian2Int(substr($AVRheader, 23,  3));
+		$ThisFileInfo['avr']['sample_length']      = getid3_lib::BigEndian2Int(substr($AVRheader, 26,  4));
+		$ThisFileInfo['avr']['loop_start']         = getid3_lib::BigEndian2Int(substr($AVRheader, 30,  4));
+		$ThisFileInfo['avr']['loop_end']           = getid3_lib::BigEndian2Int(substr($AVRheader, 34,  4));
+		$ThisFileInfo['avr']['midi_split']         = getid3_lib::BigEndian2Int(substr($AVRheader, 38,  2));
+		$ThisFileInfo['avr']['sample_compression'] = getid3_lib::BigEndian2Int(substr($AVRheader, 40,  2));
+		$ThisFileInfo['avr']['reserved']           = getid3_lib::BigEndian2Int(substr($AVRheader, 42,  2));
+		$ThisFileInfo['avr']['sample_name_extra']  =         rtrim(substr($AVRheader, 44, 20));
+		$ThisFileInfo['avr']['comment']            =         rtrim(substr($AVRheader, 64, 64));
+
+		$ThisFileInfo['avr']['flags']['stereo'] = (($ThisFileInfo['avr']['raw']['mono']   == 0) ? false : true);
+		$ThisFileInfo['avr']['flags']['signed'] = (($ThisFileInfo['avr']['raw']['signed'] == 0) ? false : true);
+		$ThisFileInfo['avr']['flags']['loop']   = (($ThisFileInfo['avr']['raw']['loop']   == 0) ? false : true);
+
+		$ThisFileInfo['avr']['midi_notes'] = array();
+		if (($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) != 0xFF00) {
+			$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0xFF00) >> 8;
+		}
+		if (($ThisFileInfo['avr']['raw']['midi'] & 0x00FF) != 0x00FF) {
+			$ThisFileInfo['avr']['midi_notes'][] = ($ThisFileInfo['avr']['raw']['midi'] & 0x00FF);
+		}
+
+		if (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) != ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2))) {
+			$ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 1 : 2)).' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
+		}
+
+		$ThisFileInfo['audio']['dataformat']      = 'avr';
+		$ThisFileInfo['audio']['lossless']        = true;
+		$ThisFileInfo['audio']['bitrate_mode']    = 'cbr';
+		$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['avr']['bits_per_sample'];
+		$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['avr']['sample_rate'];
+		$ThisFileInfo['audio']['channels']        = ($ThisFileInfo['avr']['flags']['stereo'] ? 2 : 1);
+		$ThisFileInfo['playtime_seconds']         = ($ThisFileInfo['avr']['sample_length'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['avr']['sample_rate'];
+		$ThisFileInfo['audio']['bitrate']         = ($ThisFileInfo['avr']['sample_length'] * (($ThisFileInfo['avr']['bits_per_sample'] == 8) ? 8 : 16)) / $ThisFileInfo['playtime_seconds'];
+
+
+		return true;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.bonk.php b/apps/media/getID3/getid3/module.audio.bonk.php
new file mode 100644
index 0000000000000000000000000000000000000000..fef9782cfca433e2cfb432abf36a9f6f8c9f80c5
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.bonk.php
@@ -0,0 +1,221 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.la.php                                         //
+// module for analyzing BONK audio files                       //
+// dependencies: module.tag.id3v2.php (optional)               //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_bonk
+{
+	function getid3_bonk(&$fd, &$ThisFileInfo) {
+
+		// shortcut
+		$ThisFileInfo['bonk'] = array();
+		$thisfile_bonk        = &$ThisFileInfo['bonk'];
+
+		$thisfile_bonk['dataoffset']      = $ThisFileInfo['avdataoffset'];
+		$thisfile_bonk['dataend']         = $ThisFileInfo['avdataend'];
+
+		if ($thisfile_bonk['dataend'] >= pow(2, 31)) {
+
+			$ThisFileInfo['warning'][] = 'Unable to parse BONK file from end (v0.6+ preferred method) because PHP filesystem functions only support up to 2GB';
+
+		} else {
+
+			// scan-from-end method, for v0.6 and higher
+			fseek($fd, $thisfile_bonk['dataend'] - 8, SEEK_SET);
+			$PossibleBonkTag = fread($fd, 8);
+			while ($this->BonkIsValidTagName(substr($PossibleBonkTag, 4, 4), true)) {
+				$BonkTagSize = getid3_lib::LittleEndian2Int(substr($PossibleBonkTag, 0, 4));
+				fseek($fd, 0 - $BonkTagSize, SEEK_CUR);
+				$BonkTagOffset = ftell($fd);
+				$TagHeaderTest = fread($fd, 5);
+				if (($TagHeaderTest{0} != "\x00") || (substr($PossibleBonkTag, 4, 4) != strtolower(substr($PossibleBonkTag, 4, 4)))) {
+					$ThisFileInfo['error'][] = 'Expecting "Ø'.strtoupper(substr($PossibleBonkTag, 4, 4)).'" at offset '.$BonkTagOffset.', found "'.$TagHeaderTest.'"';
+					return false;
+				}
+				$BonkTagName = substr($TagHeaderTest, 1, 4);
+
+				$thisfile_bonk[$BonkTagName]['size']   = $BonkTagSize;
+				$thisfile_bonk[$BonkTagName]['offset'] = $BonkTagOffset;
+				$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
+				$NextTagEndOffset = $BonkTagOffset - 8;
+				if ($NextTagEndOffset < $thisfile_bonk['dataoffset']) {
+					if (empty($ThisFileInfo['audio']['encoder'])) {
+						$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
+					}
+					return true;
+				}
+				fseek($fd, $NextTagEndOffset, SEEK_SET);
+				$PossibleBonkTag = fread($fd, 8);
+			}
+
+		}
+
+		// seek-from-beginning method for v0.4 and v0.5
+		if (empty($thisfile_bonk['BONK'])) {
+			fseek($fd, $thisfile_bonk['dataoffset'], SEEK_SET);
+			do {
+				$TagHeaderTest = fread($fd, 5);
+				switch ($TagHeaderTest) {
+					case "\x00".'BONK':
+						if (empty($ThisFileInfo['audio']['encoder'])) {
+							$ThisFileInfo['audio']['encoder'] = 'BONK v0.4';
+						}
+						break;
+
+					case "\x00".'INFO':
+						$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.5';
+						break;
+
+					default:
+						break 2;
+				}
+				$BonkTagName = substr($TagHeaderTest, 1, 4);
+				$thisfile_bonk[$BonkTagName]['size']   = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
+				$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
+				$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
+
+			} while (true);
+		}
+
+		// parse META block for v0.6 - v0.8
+		if (empty($thisfile_bonk['INFO']) && isset($thisfile_bonk['META']['tags']['info'])) {
+			fseek($fd, $thisfile_bonk['META']['tags']['info'], SEEK_SET);
+			$TagHeaderTest = fread($fd, 5);
+			if ($TagHeaderTest == "\x00".'INFO') {
+				$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.6 - v0.8';
+
+				$BonkTagName = substr($TagHeaderTest, 1, 4);
+				$thisfile_bonk[$BonkTagName]['size']   = $thisfile_bonk['dataend'] - $thisfile_bonk['dataoffset'];
+				$thisfile_bonk[$BonkTagName]['offset'] = $thisfile_bonk['dataoffset'];
+				$this->HandleBonkTags($fd, $BonkTagName, $ThisFileInfo);
+			}
+		}
+
+		if (empty($ThisFileInfo['audio']['encoder'])) {
+			$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
+		}
+		if (empty($thisfile_bonk['BONK'])) {
+			unset($ThisFileInfo['bonk']);
+		}
+		return true;
+
+	}
+
+	function HandleBonkTags(&$fd, &$BonkTagName, &$ThisFileInfo) {
+
+		switch ($BonkTagName) {
+			case 'BONK':
+				// shortcut
+				$thisfile_bonk_BONK = &$ThisFileInfo['bonk']['BONK'];
+
+				$BonkData = "\x00".'BONK'.fread($fd, 17);
+				$thisfile_bonk_BONK['version']            =        getid3_lib::LittleEndian2Int(substr($BonkData,  5, 1));
+				$thisfile_bonk_BONK['number_samples']     =        getid3_lib::LittleEndian2Int(substr($BonkData,  6, 4));
+				$thisfile_bonk_BONK['sample_rate']        =        getid3_lib::LittleEndian2Int(substr($BonkData, 10, 4));
+
+				$thisfile_bonk_BONK['channels']           =        getid3_lib::LittleEndian2Int(substr($BonkData, 14, 1));
+				$thisfile_bonk_BONK['lossless']           = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 15, 1));
+				$thisfile_bonk_BONK['joint_stereo']       = (bool) getid3_lib::LittleEndian2Int(substr($BonkData, 16, 1));
+				$thisfile_bonk_BONK['number_taps']        =        getid3_lib::LittleEndian2Int(substr($BonkData, 17, 2));
+				$thisfile_bonk_BONK['downsampling_ratio'] =        getid3_lib::LittleEndian2Int(substr($BonkData, 19, 1));
+				$thisfile_bonk_BONK['samples_per_packet'] =        getid3_lib::LittleEndian2Int(substr($BonkData, 20, 2));
+
+				$ThisFileInfo['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
+				$ThisFileInfo['avdataend']    = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
+
+				$ThisFileInfo['fileformat']               = 'bonk';
+				$ThisFileInfo['audio']['dataformat']      = 'bonk';
+				$ThisFileInfo['audio']['bitrate_mode']    = 'vbr'; // assumed
+				$ThisFileInfo['audio']['channels']        = $thisfile_bonk_BONK['channels'];
+				$ThisFileInfo['audio']['sample_rate']     = $thisfile_bonk_BONK['sample_rate'];
+				$ThisFileInfo['audio']['channelmode']     = ($thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo');
+				$ThisFileInfo['audio']['lossless']        = $thisfile_bonk_BONK['lossless'];
+				$ThisFileInfo['audio']['codec']           = 'bonk';
+
+				$ThisFileInfo['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
+				if ($ThisFileInfo['playtime_seconds'] > 0) {
+					$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['bonk']['dataend'] - $ThisFileInfo['bonk']['dataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+				}
+				break;
+
+			case 'INFO':
+				// shortcut
+				$thisfile_bonk_INFO = &$ThisFileInfo['bonk']['INFO'];
+
+				$thisfile_bonk_INFO['version'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
+				$thisfile_bonk_INFO['entries_count'] = 0;
+				$NextInfoDataPair = fread($fd, 5);
+				if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
+					while (!feof($fd)) {
+						//$CurrentSeekInfo['offset']  = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
+						//$CurrentSeekInfo['nextbit'] = getid3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
+						//$thisfile_bonk_INFO[] = $CurrentSeekInfo;
+
+						$NextInfoDataPair = fread($fd, 5);
+						if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
+							fseek($fd, -5, SEEK_CUR);
+							break;
+						}
+						$thisfile_bonk_INFO['entries_count']++;
+					}
+				}
+				break;
+
+			case 'META':
+				$BonkData = "\x00".'META'.fread($fd, $ThisFileInfo['bonk']['META']['size'] - 5);
+				$ThisFileInfo['bonk']['META']['version'] = getid3_lib::LittleEndian2Int(substr($BonkData,  5, 1));
+
+				$MetaTagEntries = floor(((strlen($BonkData) - 8) - 6) / 8); // BonkData - xxxxmeta - ØMETA
+				$offset = 6;
+				for ($i = 0; $i < $MetaTagEntries; $i++) {
+					$MetaEntryTagName   =                  substr($BonkData, $offset, 4);
+					$offset += 4;
+					$MetaEntryTagOffset = getid3_lib::LittleEndian2Int(substr($BonkData, $offset, 4));
+					$offset += 4;
+					$ThisFileInfo['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
+				}
+				break;
+
+			case ' ID3':
+				$ThisFileInfo['audio']['encoder'] = 'Extended BONK v0.9+';
+
+				// ID3v2 checking is optional
+				if (class_exists('getid3_id3v2')) {
+					$ThisFileInfo['bonk'][' ID3']['valid'] = new getid3_id3v2($fd, $ThisFileInfo, $ThisFileInfo['bonk'][' ID3']['offset'] + 2);
+				}
+				break;
+
+			default:
+				$ThisFileInfo['warning'][] = 'Unexpected Bonk tag "'.$BonkTagName.'" at offset '.$ThisFileInfo['bonk'][$BonkTagName]['offset'];
+				break;
+
+		}
+	}
+
+	function BonkIsValidTagName($PossibleBonkTag, $ignorecase=false) {
+		static $BonkIsValidTagName = array('BONK', 'INFO', ' ID3', 'META');
+		foreach ($BonkIsValidTagName as $validtagname) {
+			if ($validtagname == $PossibleBonkTag) {
+				return true;
+			} elseif ($ignorecase && (strtolower($validtagname) == strtolower($PossibleBonkTag))) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.dss.php b/apps/media/getID3/getid3/module.audio.dss.php
new file mode 100644
index 0000000000000000000000000000000000000000..b0887395c4bfd0476f5d53aad4d9780283a47be4
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.dss.php
@@ -0,0 +1,72 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.au.php                                         //
+// module for analyzing Digital Speech Standard (DSS) files    //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_dss
+{
+
+	function getid3_dss(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$DSSheader  = fread($fd, 1256);
+
+		if (substr($DSSheader, 0, 4) != "\x02".'dss') {
+			$ThisFileInfo['error'][] = 'Expecting "[x02]dss" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($DSSheader, 0, 4).'"';
+			return false;
+		}
+
+		// some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
+
+		// shortcut
+		$ThisFileInfo['dss'] = array();
+		$thisfile_dss        = &$ThisFileInfo['dss'];
+
+		$ThisFileInfo['fileformat']            = 'dss';
+		$ThisFileInfo['audio']['dataformat']   = 'dss';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
+		//$thisfile_dss['encoding']              = 'ISO-8859-1';
+
+		$thisfile_dss['date_create']    = $this->DSSdateStringToUnixDate(substr($DSSheader,  38,  12));
+		$thisfile_dss['date_complete']  = $this->DSSdateStringToUnixDate(substr($DSSheader,  50,  12));
+		$thisfile_dss['length']         =                         intval(substr($DSSheader,  62,   6));
+		$thisfile_dss['priority']       =                            ord(substr($DSSheader, 793,   1));
+		$thisfile_dss['comments']       =                           trim(substr($DSSheader, 798, 100));
+
+
+		//$ThisFileInfo['audio']['bits_per_sample']  = ?;
+		//$ThisFileInfo['audio']['sample_rate']      = ?;
+		$ThisFileInfo['audio']['channels']     = 1;
+
+		$ThisFileInfo['playtime_seconds'] = $thisfile_dss['length'];
+		$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['filesize'] * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+	}
+
+	function DSSdateStringToUnixDate($datestring) {
+		$y = substr($datestring,  0, 2);
+		$m = substr($datestring,  2, 2);
+		$d = substr($datestring,  4, 2);
+		$h = substr($datestring,  6, 2);
+		$i = substr($datestring,  8, 2);
+		$s = substr($datestring, 10, 2);
+		$y += (($y < 95) ? 2000 : 1900);
+		return mktime($h, $i, $s, $m, $d, $y);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.dts.php b/apps/media/getID3/getid3/module.audio.dts.php
new file mode 100644
index 0000000000000000000000000000000000000000..1af67fdb5ae0a4d0a1f014c0402c05fa293c5a57
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.dts.php
@@ -0,0 +1,239 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.dts.php                                        //
+// module for analyzing DTS Audio files                        //
+// dependencies: NONE                                          //
+//                                                             //
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_dts
+{
+
+    function getid3_dts(&$fd, &$ThisFileInfo) {
+        // Specs taken from "DTS Coherent Acoustics;Core and Extensions,  ETSI TS 102 114 V1.2.1 (2002-12)"
+        // (http://pda.etsi.org/pda/queryform.asp)
+        // With thanks to Gambit <macteam@users.sourceforge.net> http://mac.sourceforge.net/atl/
+
+        $ThisFileInfo['fileformat'] = 'dts';
+
+        fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+        $DTSheader = fread($fd, 16);
+
+        $ThisFileInfo['dts']['raw']['magic'] = getid3_lib::BigEndian2Int(substr($DTSheader,  0,  4));
+        if ($ThisFileInfo['dts']['raw']['magic'] != 0x7FFE8001) {
+            $ThisFileInfo['error'][] = 'Expecting "0x7FFE8001" at offset '.$ThisFileInfo['avdataoffset'].', found "0x'.str_pad(strtoupper(dechex($ThisFileInfo['dts']['raw']['magic'])), 8, '0', STR_PAD_LEFT).'"';
+            unset($ThisFileInfo['fileformat']);
+            unset($ThisFileInfo['dts']);
+            return false;
+        }
+
+        $fhBS = getid3_lib::BigEndian2Bin(substr($DTSheader,  4,  12));
+        $bsOffset = 0;
+        $ThisFileInfo['dts']['raw']['frame_type']             =        bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['raw']['deficit_samples']        =        bindec(substr($fhBS, $bsOffset,  5)); $bsOffset +=  5;
+        $ThisFileInfo['dts']['flags']['crc_present']          = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['raw']['pcm_sample_blocks']      =        bindec(substr($fhBS, $bsOffset,  7)); $bsOffset +=  7;
+        $ThisFileInfo['dts']['raw']['frame_byte_size']        =        bindec(substr($fhBS, $bsOffset, 14)); $bsOffset += 14;
+        $ThisFileInfo['dts']['raw']['channel_arrangement']    =        bindec(substr($fhBS, $bsOffset,  6)); $bsOffset +=  6;
+        $ThisFileInfo['dts']['raw']['sample_frequency']       =        bindec(substr($fhBS, $bsOffset,  4)); $bsOffset +=  4;
+        $ThisFileInfo['dts']['raw']['bitrate']                =        bindec(substr($fhBS, $bsOffset,  5)); $bsOffset +=  5;
+        $ThisFileInfo['dts']['flags']['embedded_downmix']     = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['dynamicrange']         = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['timestamp']            = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['auxdata']              = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['hdcd']                 = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['raw']['extension_audio']        =        bindec(substr($fhBS, $bsOffset,  3)); $bsOffset +=  3;
+        $ThisFileInfo['dts']['flags']['extended_coding']      = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['audio_sync_insertion'] = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['raw']['lfe_effects']            =        bindec(substr($fhBS, $bsOffset,  2)); $bsOffset +=  2;
+        $ThisFileInfo['dts']['flags']['predictor_history']    = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        if ($ThisFileInfo['dts']['flags']['crc_present']) {
+            $ThisFileInfo['dts']['raw']['crc16']              =        bindec(substr($fhBS, $bsOffset, 16)); $bsOffset += 16;
+        }
+        $ThisFileInfo['dts']['flags']['mri_perfect_reconst']  = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['raw']['encoder_soft_version']   =        bindec(substr($fhBS, $bsOffset,  4)); $bsOffset +=  4;
+        $ThisFileInfo['dts']['raw']['copy_history']           =        bindec(substr($fhBS, $bsOffset,  2)); $bsOffset +=  2;
+        $ThisFileInfo['dts']['raw']['bits_per_sample']        =        bindec(substr($fhBS, $bsOffset,  2)); $bsOffset +=  2;
+        $ThisFileInfo['dts']['flags']['surround_es']          = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['front_sum_diff']       = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['flags']['surround_sum_diff']    = (bool) bindec(substr($fhBS, $bsOffset,  1)); $bsOffset +=  1;
+        $ThisFileInfo['dts']['raw']['dialog_normalization']   =        bindec(substr($fhBS, $bsOffset,  4)); $bsOffset +=  4;
+
+
+        $ThisFileInfo['dts']['bitrate']              = $this->DTSbitrateLookup($ThisFileInfo['dts']['raw']['bitrate']);
+        $ThisFileInfo['dts']['bits_per_sample']      = $this->DTSbitPerSampleLookup($ThisFileInfo['dts']['raw']['bits_per_sample']);
+        $ThisFileInfo['dts']['sample_rate']          = $this->DTSsampleRateLookup($ThisFileInfo['dts']['raw']['sample_frequency']);
+        $ThisFileInfo['dts']['dialog_normalization'] = $this->DTSdialogNormalization($ThisFileInfo['dts']['raw']['dialog_normalization'], $ThisFileInfo['dts']['raw']['encoder_soft_version']);
+        $ThisFileInfo['dts']['flags']['lossless']    = (($ThisFileInfo['dts']['raw']['bitrate'] == 31) ? true  : false);
+        $ThisFileInfo['dts']['bitrate_mode']         = (($ThisFileInfo['dts']['raw']['bitrate'] == 30) ? 'vbr' : 'cbr');
+        $ThisFileInfo['dts']['channels']             = $this->DTSnumChannelsLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
+        $ThisFileInfo['dts']['channel_arrangement']  = $this->DTSchannelArrangementLookup($ThisFileInfo['dts']['raw']['channel_arrangement']);
+
+        $ThisFileInfo['audio']['dataformat']          = 'dts';
+        $ThisFileInfo['audio']['lossless']            = $ThisFileInfo['dts']['flags']['lossless'];
+        $ThisFileInfo['audio']['bitrate_mode']        = $ThisFileInfo['dts']['bitrate_mode'];
+        $ThisFileInfo['audio']['bits_per_sample']     = $ThisFileInfo['dts']['bits_per_sample'];
+        $ThisFileInfo['audio']['sample_rate']         = $ThisFileInfo['dts']['sample_rate'];
+        $ThisFileInfo['audio']['channels']            = $ThisFileInfo['dts']['channels'];
+        $ThisFileInfo['audio']['bitrate']             = $ThisFileInfo['dts']['bitrate'];
+        if (isset($ThisFileInfo['avdataend'])) {
+        	$ThisFileInfo['playtime_seconds']         = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['dts']['bitrate'] / 8);
+        }
+
+        return true;
+    }
+
+    function DTSbitrateLookup($index) {
+        $DTSbitrateLookup = array(
+            0  => 32000,
+            1  => 56000,
+            2  => 64000,
+            3  => 96000,
+            4  => 112000,
+            5  => 128000,
+            6  => 192000,
+            7  => 224000,
+            8  => 256000,
+            9  => 320000,
+            10 => 384000,
+            11 => 448000,
+            12 => 512000,
+            13 => 576000,
+            14 => 640000,
+            15 => 768000,
+            16 => 960000,
+            17 => 1024000,
+            18 => 1152000,
+            19 => 1280000,
+            20 => 1344000,
+            21 => 1408000,
+            22 => 1411200,
+            23 => 1472000,
+            24 => 1536000,
+            25 => 1920000,
+            26 => 2048000,
+            27 => 3072000,
+            28 => 3840000,
+            29 => 'open',
+            30 => 'variable',
+            31 => 'lossless'
+        );
+        return @$DTSbitrateLookup[$index];
+    }
+
+    function DTSsampleRateLookup($index) {
+        $DTSsampleRateLookup = array(
+            0  => 'invalid',
+            1  => 8000,
+            2  => 16000,
+            3  => 32000,
+            4  => 'invalid',
+            5  => 'invalid',
+            6  => 11025,
+            7  => 22050,
+            8  => 44100,
+            9  => 'invalid',
+            10 => 'invalid',
+            11 => 12000,
+            12 => 24000,
+            13 => 48000,
+            14 => 'invalid',
+            15 => 'invalid'
+        );
+        return @$DTSsampleRateLookup[$index];
+    }
+
+    function DTSbitPerSampleLookup($index) {
+        $DTSbitPerSampleLookup = array(
+            0  => 16,
+            1  => 20,
+            2  => 24,
+            3  => 24,
+        );
+        return @$DTSbitPerSampleLookup[$index];
+    }
+
+    function DTSnumChannelsLookup($index) {
+        switch ($index) {
+            case 0:
+                return 1;
+                break;
+            case 1:
+            case 2:
+            case 3:
+            case 4:
+                return 2;
+                break;
+            case 5:
+            case 6:
+                return 3;
+                break;
+            case 7:
+            case 8:
+                return 4;
+                break;
+            case 9:
+                return 5;
+                break;
+            case 10:
+            case 11:
+            case 12:
+                return 6;
+                break;
+            case 13:
+                return 7;
+                break;
+            case 14:
+            case 15:
+                return 8;
+                break;
+        }
+        return false;
+    }
+
+    function DTSchannelArrangementLookup($index) {
+        $DTSchannelArrangementLookup = array(
+            0  => 'A',
+            1  => 'A + B (dual mono)',
+            2  => 'L + R (stereo)',
+            3  => '(L+R) + (L-R) (sum-difference)',
+            4  => 'LT + RT (left and right total)',
+            5  => 'C + L + R',
+            6  => 'L + R + S',
+            7  => 'C + L + R + S',
+            8  => 'L + R + SL + SR',
+            9  => 'C + L + R + SL + SR',
+            10 => 'CL + CR + L + R + SL + SR',
+            11 => 'C + L + R+ LR + RR + OV',
+            12 => 'CF + CR + LF + RF + LR + RR',
+            13 => 'CL + C + CR + L + R + SL + SR',
+            14 => 'CL + CR + L + R + SL1 + SL2 + SR1 + SR2',
+            15 => 'CL + C+ CR + L + R + SL + S + SR',
+        );
+        return (@$DTSchannelArrangementLookup[$index] ? @$DTSchannelArrangementLookup[$index] : 'user-defined');
+    }
+
+    function DTSdialogNormalization($index, $version) {
+        switch ($version) {
+            case 7:
+                return 0 - $index;
+                break;
+            case 6:
+                return 0 - 16 - $index;
+                break;
+        }
+        return false;
+    }
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.flac.php b/apps/media/getID3/getid3/module.audio.flac.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ef431e5131218682fd8e28b8e1df5b16816301c
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.flac.php
@@ -0,0 +1,397 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.flac.php                                       //
+// module for analyzing FLAC and OggFLAC audio files           //
+// dependencies: module.audio.ogg.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ogg.php', __FILE__, true);
+
+class getid3_flac
+{
+
+	function getid3_flac(&$fd, &$ThisFileInfo) {
+		// http://flac.sourceforge.net/format.html
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$StreamMarker = fread($fd, 4);
+		if ($StreamMarker != 'fLaC') {
+			$ThisFileInfo['error'][] = 'Expecting "fLaC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
+			return false;
+		}
+		$ThisFileInfo['fileformat']            = 'flac';
+		$ThisFileInfo['audio']['dataformat']   = 'flac';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+		$ThisFileInfo['audio']['lossless']     = true;
+
+		return getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo);
+	}
+
+
+	function FLACparseMETAdata(&$fd, &$ThisFileInfo) {
+
+		do {
+			$METAdataBlockOffset          = ftell($fd);
+			$METAdataBlockHeader          = fread($fd, 4);
+			$METAdataLastBlockFlag        = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x80);
+			$METAdataBlockType            = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 0, 1)) & 0x7F;
+			$METAdataBlockLength          = getid3_lib::BigEndian2Int(substr($METAdataBlockHeader, 1, 3));
+			$METAdataBlockTypeText        = getid3_flac::FLACmetaBlockTypeLookup($METAdataBlockType);
+
+			if ($METAdataBlockLength < 0) {
+				$ThisFileInfo['error'][] = 'corrupt or invalid METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
+				break;
+			}
+
+			$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'] = array();
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw = &$ThisFileInfo['flac'][$METAdataBlockTypeText]['raw'];
+
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['offset']          = $METAdataBlockOffset;
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['last_meta_block'] = $METAdataLastBlockFlag;
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type']      = $METAdataBlockType;
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_type_text'] = $METAdataBlockTypeText;
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_length']    = $METAdataBlockLength;
+			$ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data']      = @fread($fd, $METAdataBlockLength);
+			$ThisFileInfo['avdataoffset'] = ftell($fd);
+
+			switch ($METAdataBlockTypeText) {
+
+				case 'STREAMINFO':
+					if (!getid3_flac::FLACparseSTREAMINFO($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
+						return false;
+					}
+					break;
+
+				case 'PADDING':
+					// ignore
+					break;
+
+				case 'APPLICATION':
+					if (!getid3_flac::FLACparseAPPLICATION($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
+						return false;
+					}
+					break;
+
+				case 'SEEKTABLE':
+					if (!getid3_flac::FLACparseSEEKTABLE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
+						return false;
+					}
+					break;
+
+				case 'VORBIS_COMMENT':
+					$OldOffset = ftell($fd);
+					fseek($fd, 0 - $METAdataBlockLength, SEEK_CUR);
+					getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
+					fseek($fd, $OldOffset, SEEK_SET);
+					break;
+
+				case 'CUESHEET':
+					if (!getid3_flac::FLACparseCUESHEET($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
+						return false;
+					}
+					break;
+
+                case 'PICTURE':
+                    if (!$this->FLACparsePICTURE($ThisFileInfo_flac_METAdataBlockTypeText_raw['block_data'], $ThisFileInfo)) {
+                        return false;
+                    }
+                    break;
+
+				default:
+					$ThisFileInfo['warning'][] = 'Unhandled METADATA_BLOCK_HEADER.BLOCK_TYPE ('.$METAdataBlockType.') at offset '.$METAdataBlockOffset;
+					break;
+			}
+
+		} while ($METAdataLastBlockFlag === false);
+
+
+		if (isset($ThisFileInfo['flac']['STREAMINFO'])) {
+			$ThisFileInfo['flac']['compressed_audio_bytes']   = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
+			$ThisFileInfo['flac']['uncompressed_audio_bytes'] = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] * $ThisFileInfo['flac']['STREAMINFO']['channels'] * ($ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] / 8);
+			if ($ThisFileInfo['flac']['uncompressed_audio_bytes'] == 0) {
+				$ThisFileInfo['error'][] = 'Corrupt FLAC file: uncompressed_audio_bytes == zero';
+				return false;
+			}
+			$ThisFileInfo['flac']['compression_ratio']        = $ThisFileInfo['flac']['compressed_audio_bytes'] / $ThisFileInfo['flac']['uncompressed_audio_bytes'];
+		}
+
+		// set md5_data_source - built into flac 0.5+
+		if (isset($ThisFileInfo['flac']['STREAMINFO']['audio_signature'])) {
+
+			if ($ThisFileInfo['flac']['STREAMINFO']['audio_signature'] === str_repeat("\x00", 16)) {
+
+				$ThisFileInfo['warning'][] = 'FLAC STREAMINFO.audio_signature is null (known issue with libOggFLAC)';
+
+			} else {
+
+				$ThisFileInfo['md5_data_source'] = '';
+				$md5 = $ThisFileInfo['flac']['STREAMINFO']['audio_signature'];
+				for ($i = 0; $i < strlen($md5); $i++) {
+					$ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
+				}
+				if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
+					unset($ThisFileInfo['md5_data_source']);
+				}
+
+			}
+
+		}
+
+		$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
+		if ($ThisFileInfo['audio']['bits_per_sample'] == 8) {
+			// special case
+			// must invert sign bit on all data bytes before MD5'ing to match FLAC's calculated value
+			// MD5sum calculates on unsigned bytes, but FLAC calculated MD5 on 8-bit audio data as signed
+			$ThisFileInfo['warning'][] = 'FLAC calculates MD5 data strangely on 8-bit audio, so the stored md5_data_source value will not match the decoded WAV file';
+		}
+		if (!empty($ThisFileInfo['ogg']['vendor'])) {
+			$ThisFileInfo['audio']['encoder'] = $ThisFileInfo['ogg']['vendor'];
+		}
+
+		return true;
+	}
+
+	function FLACmetaBlockTypeLookup($blocktype) {
+		static $FLACmetaBlockTypeLookup = array();
+		if (empty($FLACmetaBlockTypeLookup)) {
+			$FLACmetaBlockTypeLookup[0] = 'STREAMINFO';
+			$FLACmetaBlockTypeLookup[1] = 'PADDING';
+			$FLACmetaBlockTypeLookup[2] = 'APPLICATION';
+			$FLACmetaBlockTypeLookup[3] = 'SEEKTABLE';
+			$FLACmetaBlockTypeLookup[4] = 'VORBIS_COMMENT';
+			$FLACmetaBlockTypeLookup[5] = 'CUESHEET';
+			$FLACmetaBlockTypeLookup[6] = 'PICTURE';
+		}
+		return (isset($FLACmetaBlockTypeLookup[$blocktype]) ? $FLACmetaBlockTypeLookup[$blocktype] : 'reserved');
+	}
+
+	function FLACapplicationIDLookup($applicationid) {
+		static $FLACapplicationIDLookup = array();
+		if (empty($FLACapplicationIDLookup)) {
+			// http://flac.sourceforge.net/id.html
+			$FLACapplicationIDLookup[0x46746F6C] = 'flac-tools';      // 'Ftol'
+			$FLACapplicationIDLookup[0x46746F6C] = 'Sound Font FLAC'; // 'SFFL'
+		}
+		return (isset($FLACapplicationIDLookup[$applicationid]) ? $FLACapplicationIDLookup[$applicationid] : 'reserved');
+	}
+
+    function FLACpictureTypeLookup($type_id) {
+        static $lookup = array (
+             0 => 'Other',
+             1 => '32x32 pixels \'file icon\' (PNG only)',
+             2 => 'Other file icon',
+             3 => 'Cover (front)',
+             4 => 'Cover (back)',
+             5 => 'Leaflet page',
+             6 => 'Media (e.g. label side of CD)',
+             7 => 'Lead artist/lead performer/soloist',
+             8 => 'Artist/performer',
+             9 => 'Conductor',
+            10 => 'Band/Orchestra',
+            11 => 'Composer',
+            12 => 'Lyricist/text writer',
+            13 => 'Recording Location',
+            14 => 'During recording',
+            15 => 'During performance',
+            16 => 'Movie/video screen capture',
+            17 => 'A bright coloured fish',
+            18 => 'Illustration',
+            19 => 'Band/artist logotype',
+            20 => 'Publisher/Studio logotype',
+        );
+        return (isset($lookup[$type_id]) ? $lookup[$type_id] : 'reserved');
+    }
+
+	function FLACparseSTREAMINFO($METAdataBlockData, &$ThisFileInfo) {
+		$offset = 0;
+		$ThisFileInfo['flac']['STREAMINFO']['min_block_size']  = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
+		$offset += 2;
+		$ThisFileInfo['flac']['STREAMINFO']['max_block_size']  = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
+		$offset += 2;
+		$ThisFileInfo['flac']['STREAMINFO']['min_frame_size']  = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
+		$offset += 3;
+		$ThisFileInfo['flac']['STREAMINFO']['max_frame_size']  = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 3));
+		$offset += 3;
+
+		$SampleRateChannelsSampleBitsStreamSamples             = getid3_lib::BigEndian2Bin(substr($METAdataBlockData, $offset, 8));
+		$ThisFileInfo['flac']['STREAMINFO']['sample_rate']     = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples,  0, 20));
+		$ThisFileInfo['flac']['STREAMINFO']['channels']        = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 20,  3)) + 1;
+		$ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'] = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 23,  5)) + 1;
+		$ThisFileInfo['flac']['STREAMINFO']['samples_stream']  = getid3_lib::Bin2Dec(substr($SampleRateChannelsSampleBitsStreamSamples, 28, 36));
+		$offset += 8;
+
+		$ThisFileInfo['flac']['STREAMINFO']['audio_signature'] =               substr($METAdataBlockData, $offset, 16);
+		$offset += 16;
+
+		if (!empty($ThisFileInfo['flac']['STREAMINFO']['sample_rate'])) {
+
+			$ThisFileInfo['audio']['bitrate_mode']     = 'vbr';
+			$ThisFileInfo['audio']['sample_rate']      = $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
+			$ThisFileInfo['audio']['channels']         = $ThisFileInfo['flac']['STREAMINFO']['channels'];
+			$ThisFileInfo['audio']['bits_per_sample']  = $ThisFileInfo['flac']['STREAMINFO']['bits_per_sample'];
+			$ThisFileInfo['playtime_seconds']          = $ThisFileInfo['flac']['STREAMINFO']['samples_stream'] / $ThisFileInfo['flac']['STREAMINFO']['sample_rate'];
+			$ThisFileInfo['audio']['bitrate']          = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Corrupt METAdata block: STREAMINFO';
+			return false;
+
+		}
+
+		unset($ThisFileInfo['flac']['STREAMINFO']['raw']);
+
+		return true;
+	}
+
+
+	function FLACparseAPPLICATION($METAdataBlockData, &$ThisFileInfo) {
+		$offset = 0;
+		$ApplicationID = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 4));
+		$offset += 4;
+		$ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['name'] = getid3_flac::FLACapplicationIDLookup($ApplicationID);
+		$ThisFileInfo['flac']['APPLICATION'][$ApplicationID]['data'] = substr($METAdataBlockData, $offset);
+		$offset = $METAdataBlockLength;
+
+		unset($ThisFileInfo['flac']['APPLICATION']['raw']);
+
+		return true;
+	}
+
+
+	function FLACparseSEEKTABLE($METAdataBlockData, &$ThisFileInfo) {
+		$offset = 0;
+		$METAdataBlockLength = strlen($METAdataBlockData);
+		$placeholderpattern = str_repeat("\xFF", 8);
+		while ($offset < $METAdataBlockLength) {
+			$SampleNumberString = substr($METAdataBlockData, $offset, 8);
+			$offset += 8;
+			if ($SampleNumberString == $placeholderpattern) {
+
+				// placeholder point
+				@$ThisFileInfo['flac']['SEEKTABLE']['placeholders']++;
+				$offset += 10;
+
+			} else {
+
+				$SampleNumber                                                = getid3_lib::BigEndian2Int($SampleNumberString);
+				$ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['offset']  = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
+				$offset += 8;
+				$ThisFileInfo['flac']['SEEKTABLE'][$SampleNumber]['samples'] = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 2));
+				$offset += 2;
+
+			}
+		}
+
+		unset($ThisFileInfo['flac']['SEEKTABLE']['raw']);
+
+		return true;
+	}
+
+	function FLACparseCUESHEET($METAdataBlockData, &$ThisFileInfo) {
+		$offset = 0;
+		$ThisFileInfo['flac']['CUESHEET']['media_catalog_number'] =          trim(substr($METAdataBlockData, $offset, 128), "\0");
+		$offset += 128;
+		$ThisFileInfo['flac']['CUESHEET']['lead_in_samples']      = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
+		$offset += 8;
+		$ThisFileInfo['flac']['CUESHEET']['flags']['is_cd']       = (bool) (getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1)) & 0x80);
+		$offset += 1;
+
+		$offset += 258; // reserved
+
+		$ThisFileInfo['flac']['CUESHEET']['number_tracks']        = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
+		$offset += 1;
+
+		for ($track = 0; $track < $ThisFileInfo['flac']['CUESHEET']['number_tracks']; $track++) {
+			$TrackSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
+			$offset += 8;
+			$TrackNumber       = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
+			$offset += 1;
+
+			$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['sample_offset']         = $TrackSampleOffset;
+
+			$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['isrc']                  =               substr($METAdataBlockData, $offset, 12);
+			$offset += 12;
+
+			$TrackFlagsRaw                                                                     = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
+			$offset += 1;
+			$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['is_audio']     = (bool) ($TrackFlagsRaw & 0x80);
+			$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['flags']['pre_emphasis'] = (bool) ($TrackFlagsRaw & 0x40);
+
+			$offset += 13; // reserved
+
+			$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']          = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
+			$offset += 1;
+
+			for ($index = 0; $index < $ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['index_points']; $index++) {
+				$IndexSampleOffset = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 8));
+				$offset += 8;
+				$IndexNumber       = getid3_lib::BigEndian2Int(substr($METAdataBlockData, $offset, 1));
+				$offset += 1;
+
+				$offset += 3; // reserved
+
+				$ThisFileInfo['flac']['CUESHEET']['tracks'][$TrackNumber]['indexes'][$IndexNumber] = $IndexSampleOffset;
+			}
+		}
+
+		unset($ThisFileInfo['flac']['CUESHEET']['raw']);
+
+		return true;
+	}
+
+
+    function FLACparsePICTURE($meta_data_block_data, &$ThisFileInfo) {
+        $picture = &$ThisFileInfo['flac']['PICTURE'][sizeof($ThisFileInfo['flac']['PICTURE']) - 1];
+
+        $offset = 0;
+
+        $picture['type'] = $this->FLACpictureTypeLookup(getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4)));
+        $offset += 4;
+
+        $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $picture['mime_type'] = substr($meta_data_block_data, $offset, $length);
+        $offset += $length;
+
+        $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $picture['description'] = substr($meta_data_block_data, $offset, $length);
+        $offset += $length;
+
+        $picture['width'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $picture['height'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $picture['color_depth'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $picture['colors_indexed'] = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $length = getid3_lib::BigEndian2Int(substr($meta_data_block_data, $offset, 4));
+        $offset += 4;
+
+        $picture['image_data'] = substr($meta_data_block_data, $offset, $length);
+        $offset += $length;
+
+        unset($ThisFileInfo['flac']['PICTURE']['raw']);
+
+        return true;
+    }
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.la.php b/apps/media/getID3/getid3/module.audio.la.php
new file mode 100644
index 0000000000000000000000000000000000000000..c1d4f52d14feecf7e77568622d2086b0dfd3cf68
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.la.php
@@ -0,0 +1,228 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.la.php                                         //
+// module for analyzing LA audio files                         //
+// dependencies: module.audio.riff.php                         //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+class getid3_la
+{
+
+	function getid3_la(&$fd, &$ThisFileInfo) {
+		$offset = 0;
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$rawdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
+
+		switch (substr($rawdata, $offset, 4)) {
+			case 'LA02':
+			case 'LA03':
+			case 'LA04':
+				$ThisFileInfo['fileformat']          = 'la';
+				$ThisFileInfo['audio']['dataformat'] = 'la';
+				$ThisFileInfo['audio']['lossless']   = true;
+
+				$ThisFileInfo['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1);
+				$ThisFileInfo['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1);
+				$ThisFileInfo['la']['version']       = (float) $ThisFileInfo['la']['version_major'] + ($ThisFileInfo['la']['version_minor'] / 10);
+				$offset += 4;
+
+				$ThisFileInfo['la']['uncompressed_size'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+				$offset += 4;
+				if ($ThisFileInfo['la']['uncompressed_size'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt LA file: uncompressed_size == zero';
+					return false;
+				}
+
+				$WAVEchunk = substr($rawdata, $offset, 4);
+				if ($WAVEchunk !== 'WAVE') {
+					$ThisFileInfo['error'][] = 'Expected "WAVE" ('.getid3_lib::PrintHexBytes('WAVE').') at offset '.$offset.', found "'.$WAVEchunk.'" ('.getid3_lib::PrintHexBytes($WAVEchunk).') instead.';
+					return false;
+				}
+				$offset += 4;
+
+				$ThisFileInfo['la']['fmt_size'] = 24;
+				if ($ThisFileInfo['la']['version'] >= 0.3) {
+
+					$ThisFileInfo['la']['fmt_size']    = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+					$ThisFileInfo['la']['header_size'] = 49 + $ThisFileInfo['la']['fmt_size'] - 24;
+					$offset += 4;
+
+				} else {
+
+					// version 0.2 didn't support additional data blocks
+					$ThisFileInfo['la']['header_size'] = 41;
+
+				}
+
+				$fmt_chunk = substr($rawdata, $offset, 4);
+				if ($fmt_chunk !== 'fmt ') {
+					$ThisFileInfo['error'][] = 'Expected "fmt " ('.getid3_lib::PrintHexBytes('fmt ').') at offset '.$offset.', found "'.$fmt_chunk.'" ('.getid3_lib::PrintHexBytes($fmt_chunk).') instead.';
+					return false;
+				}
+				$offset += 4;
+				$fmt_size = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+				$offset += 4;
+
+				$ThisFileInfo['la']['raw']['format']  = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
+				$offset += 2;
+
+				$ThisFileInfo['la']['channels']       = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
+				$offset += 2;
+				if ($ThisFileInfo['la']['channels'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt LA file: channels == zero';
+						return false;
+				}
+
+				$ThisFileInfo['la']['sample_rate'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+				$offset += 4;
+				if ($ThisFileInfo['la']['sample_rate'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt LA file: sample_rate == zero';
+						return false;
+				}
+
+				$ThisFileInfo['la']['bytes_per_second']     = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+				$offset += 4;
+				$ThisFileInfo['la']['bytes_per_sample']     = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
+				$offset += 2;
+				$ThisFileInfo['la']['bits_per_sample']      = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 2));
+				$offset += 2;
+
+				$ThisFileInfo['la']['samples']              = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+				$offset += 4;
+
+				$ThisFileInfo['la']['raw']['flags']         = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 1));
+				$offset += 1;
+				$ThisFileInfo['la']['flags']['seekable']             = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x01);
+				if ($ThisFileInfo['la']['version'] >= 0.4) {
+					$ThisFileInfo['la']['flags']['high_compression'] = (bool) ($ThisFileInfo['la']['raw']['flags'] & 0x02);
+				}
+
+				$ThisFileInfo['la']['original_crc']         = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+				$offset += 4;
+
+				// mikeØbevin*de
+				// Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
+				// in earlier versions. A seekpoint is added every blocksize * seekevery
+				// samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should
+				// give the number of bytes used for the seekpoints. Of course, if seeking
+				// is disabled, there are no seekpoints stored.
+				if ($ThisFileInfo['la']['version'] >= 0.4) {
+					$ThisFileInfo['la']['blocksize'] = 61440;
+					$ThisFileInfo['la']['seekevery'] = 19;
+				} else {
+					$ThisFileInfo['la']['blocksize'] = 73728;
+					$ThisFileInfo['la']['seekevery'] = 16;
+				}
+
+				$ThisFileInfo['la']['seekpoint_count'] = 0;
+				if ($ThisFileInfo['la']['flags']['seekable']) {
+					$ThisFileInfo['la']['seekpoint_count'] = floor($ThisFileInfo['la']['samples'] / ($ThisFileInfo['la']['blocksize'] * $ThisFileInfo['la']['seekevery']));
+
+					for ($i = 0; $i < $ThisFileInfo['la']['seekpoint_count']; $i++) {
+						$ThisFileInfo['la']['seekpoints'][] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+						$offset += 4;
+					}
+				}
+
+				if ($ThisFileInfo['la']['version'] >= 0.3) {
+
+					// Following the main header information, the program outputs all of the
+					// seekpoints. Following these is what I called the 'footer start',
+					// i.e. the position immediately after the La audio data is finished.
+					$ThisFileInfo['la']['footerstart'] = getid3_lib::LittleEndian2Int(substr($rawdata, $offset, 4));
+					$offset += 4;
+
+					if ($ThisFileInfo['la']['footerstart'] > $ThisFileInfo['filesize']) {
+						$ThisFileInfo['warning'][] = 'FooterStart value points to offset '.$ThisFileInfo['la']['footerstart'].' which is beyond end-of-file ('.$ThisFileInfo['filesize'].')';
+						$ThisFileInfo['la']['footerstart'] = $ThisFileInfo['filesize'];
+					}
+
+				} else {
+
+					// La v0.2 didn't have FooterStart value
+					$ThisFileInfo['la']['footerstart'] = $ThisFileInfo['avdataend'];
+
+				}
+
+				if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
+					if ($RIFFtempfilename = tempnam('*', 'id3')) {
+						if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
+							$RIFFdata = 'WAVE';
+							if ($ThisFileInfo['la']['version'] == 0.2) {
+								$RIFFdata .= substr($rawdata, 12, 24);
+							} else {
+								$RIFFdata .= substr($rawdata, 16, 24);
+							}
+							if ($ThisFileInfo['la']['footerstart'] < $ThisFileInfo['avdataend']) {
+								fseek($fd, $ThisFileInfo['la']['footerstart'], SEEK_SET);
+								$RIFFdata .= fread($fd, $ThisFileInfo['avdataend'] - $ThisFileInfo['la']['footerstart']);
+							}
+							$RIFFdata = 'RIFF'.getid3_lib::LittleEndian2String(strlen($RIFFdata), 4, false).$RIFFdata;
+							fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
+							$dummy = $ThisFileInfo;
+							$dummy['filesize']     = strlen($RIFFdata);
+							$dummy['avdataoffset'] = 0;
+							$dummy['avdataend']    = $dummy['filesize'];
+
+							$riff = new getid3_riff($RIFF_fp, $dummy);
+							if (empty($dummy['error'])) {
+								$ThisFileInfo['riff'] = $dummy['riff'];
+							} else {
+								$ThisFileInfo['warning'][] = 'Error parsing RIFF portion of La file: '.implode($dummy['error']);
+							}
+							unset($riff);
+							unset($dummy);
+							fclose($RIFF_fp);
+						}
+						unlink($RIFFtempfilename);
+					}
+				}
+
+				// $ThisFileInfo['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
+				$ThisFileInfo['avdataend']    = $ThisFileInfo['avdataoffset'] + $ThisFileInfo['la']['footerstart'];
+				$ThisFileInfo['avdataoffset'] = $ThisFileInfo['avdataoffset'] + $offset;
+
+				//$ThisFileInfo['la']['codec']                = RIFFwFormatTagLookup($ThisFileInfo['la']['raw']['format']);
+				$ThisFileInfo['la']['compression_ratio']    = (float) (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['la']['uncompressed_size']);
+				$ThisFileInfo['playtime_seconds']           = (float) ($ThisFileInfo['la']['samples'] / $ThisFileInfo['la']['sample_rate']) / $ThisFileInfo['la']['channels'];
+				if ($ThisFileInfo['playtime_seconds'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt LA file: playtime_seconds == zero';
+					return false;
+				}
+
+				$ThisFileInfo['audio']['bitrate']            = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['playtime_seconds'];
+				//$ThisFileInfo['audio']['codec']              = $ThisFileInfo['la']['codec'];
+				$ThisFileInfo['audio']['bits_per_sample']    = $ThisFileInfo['la']['bits_per_sample'];
+				break;
+
+			default:
+				if (substr($rawdata, $offset, 2) == 'LA') {
+					$ThisFileInfo['error'][] = 'This version of getID3() (v'.GETID3_VERSION.') doesn\'t support LA version '.substr($rawdata, $offset + 2, 1).'.'.substr($rawdata, $offset + 3, 1).' which this appears to be - check http://getid3.sourceforge.net for updates.';
+				} else {
+					$ThisFileInfo['error'][] = 'Not a LA (Lossless-Audio) file';
+				}
+				return false;
+				break;
+		}
+
+		$ThisFileInfo['audio']['channels']    = $ThisFileInfo['la']['channels'];
+		$ThisFileInfo['audio']['sample_rate'] = (int) $ThisFileInfo['la']['sample_rate'];
+		$ThisFileInfo['audio']['encoder']     = 'LA v'.$ThisFileInfo['la']['version'];
+
+		return true;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.lpac.php b/apps/media/getID3/getid3/module.audio.lpac.php
new file mode 100644
index 0000000000000000000000000000000000000000..3c04492117efad4541172eb1558ed28da8d9debb
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.lpac.php
@@ -0,0 +1,126 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.lpac.php                                       //
+// module for analyzing LPAC Audio files                       //
+// dependencies: module.audio-video.riff.php                   //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+class getid3_lpac
+{
+
+	function getid3_lpac(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$LPACheader = fread($fd, 14);
+		if (substr($LPACheader, 0, 4) != 'LPAC') {
+			$ThisFileInfo['error'][] = 'Expected "LPAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$StreamMarker.'"';
+			return false;
+		}
+		$ThisFileInfo['avdataoffset'] += 14;
+
+		$ThisFileInfo['fileformat']            = 'lpac';
+		$ThisFileInfo['audio']['dataformat']   = 'lpac';
+		$ThisFileInfo['audio']['lossless']     = true;
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+
+		$ThisFileInfo['lpac']['file_version'] = getid3_lib::BigEndian2Int(substr($LPACheader,  4, 1));
+		$flags['audio_type']                  = getid3_lib::BigEndian2Int(substr($LPACheader,  5, 1));
+		$ThisFileInfo['lpac']['total_samples']= getid3_lib::BigEndian2Int(substr($LPACheader,  6, 4));
+		$flags['parameters']                  = getid3_lib::BigEndian2Int(substr($LPACheader, 10, 4));
+
+		$ThisFileInfo['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
+		$ThisFileInfo['lpac']['flags']['stereo']  = (bool) ($flags['audio_type'] & 0x04);
+		$ThisFileInfo['lpac']['flags']['24_bit']  = (bool) ($flags['audio_type'] & 0x02);
+		$ThisFileInfo['lpac']['flags']['16_bit']  = (bool) ($flags['audio_type'] & 0x01);
+
+		if ($ThisFileInfo['lpac']['flags']['24_bit'] && $ThisFileInfo['lpac']['flags']['16_bit']) {
+			$ThisFileInfo['warning'][] = '24-bit and 16-bit flags cannot both be set';
+		}
+
+		$ThisFileInfo['lpac']['flags']['fast_compress']             =  (bool) ($flags['parameters'] & 0x40000000);
+		$ThisFileInfo['lpac']['flags']['random_access']             =  (bool) ($flags['parameters'] & 0x08000000);
+		$ThisFileInfo['lpac']['block_length']                       = pow(2, (($flags['parameters'] & 0x07000000) >> 24)) * 256;
+		$ThisFileInfo['lpac']['flags']['adaptive_prediction_order'] =  (bool) ($flags['parameters'] & 0x00800000);
+		$ThisFileInfo['lpac']['flags']['adaptive_quantization']     =  (bool) ($flags['parameters'] & 0x00400000);
+		$ThisFileInfo['lpac']['flags']['joint_stereo']              =  (bool) ($flags['parameters'] & 0x00040000);
+		$ThisFileInfo['lpac']['quantization']                       =         ($flags['parameters'] & 0x00001F00) >> 8;
+		$ThisFileInfo['lpac']['max_prediction_order']               =         ($flags['parameters'] & 0x0000003F);
+
+		if ($ThisFileInfo['lpac']['flags']['fast_compress'] && ($ThisFileInfo['lpac']['max_prediction_order'] != 3)) {
+			$ThisFileInfo['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "'.$ThisFileInfo['lpac']['max_prediction_order'].'"';
+		}
+		switch ($ThisFileInfo['lpac']['file_version']) {
+			case 6:
+				if ($ThisFileInfo['lpac']['flags']['adaptive_quantization']) {
+					$ThisFileInfo['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
+				}
+				if ($ThisFileInfo['lpac']['quantization'] != 20) {
+					$ThisFileInfo['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually '.$ThisFileInfo['lpac']['flags']['Q'];
+				}
+				break;
+
+			default:
+				//$ThisFileInfo['warning'][] = 'This version of getID3() only supports LPAC file format version 6, this file is version '.$ThisFileInfo['lpac']['file_version'].' - please report to info@getid3.org';
+				break;
+		}
+
+		$dummy = $ThisFileInfo;
+		$riff = new getid3_riff($fd, $dummy);
+		unset($riff);
+		$ThisFileInfo['avdataoffset']                = $dummy['avdataoffset'];
+		$ThisFileInfo['riff']                        = $dummy['riff'];
+		$ThisFileInfo['error']                       = $dummy['error'];
+		$ThisFileInfo['warning']                     = $dummy['warning'];
+		$ThisFileInfo['lpac']['comments']['comment'] = $dummy['comments'];
+		$ThisFileInfo['audio']['sample_rate']        = $dummy['audio']['sample_rate'];
+
+		$ThisFileInfo['audio']['channels']    = ($ThisFileInfo['lpac']['flags']['stereo'] ? 2 : 1);
+
+		if ($ThisFileInfo['lpac']['flags']['24_bit']) {
+			$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
+		} elseif ($ThisFileInfo['lpac']['flags']['16_bit']) {
+			$ThisFileInfo['audio']['bits_per_sample'] = 16;
+		} else {
+			$ThisFileInfo['audio']['bits_per_sample'] = 8;
+		}
+
+		if ($ThisFileInfo['lpac']['flags']['fast_compress']) {
+			 // fast
+			$ThisFileInfo['audio']['encoder_options'] = '-1';
+		} else {
+			switch ($ThisFileInfo['lpac']['max_prediction_order']) {
+				case 20: // simple
+					$ThisFileInfo['audio']['encoder_options'] = '-2';
+					break;
+				case 30: // medium
+					$ThisFileInfo['audio']['encoder_options'] = '-3';
+					break;
+				case 40: // high
+					$ThisFileInfo['audio']['encoder_options'] = '-4';
+					break;
+				case 60: // extrahigh
+					$ThisFileInfo['audio']['encoder_options'] = '-5';
+					break;
+			}
+		}
+
+		$ThisFileInfo['playtime_seconds'] = $ThisFileInfo['lpac']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
+		$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.midi.php b/apps/media/getID3/getid3/module.audio.midi.php
new file mode 100644
index 0000000000000000000000000000000000000000..0fd7c9bdbfdaf3fbaff602438b4220a4325d506c
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.midi.php
@@ -0,0 +1,522 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.midi.php                                       //
+// module for Midi Audio files                                 //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_midi
+{
+
+	function getid3_midi(&$fd, &$ThisFileInfo, $scanwholefile=true) {
+
+		// shortcut
+		$ThisFileInfo['midi']['raw'] = array();
+		$thisfile_midi               = &$ThisFileInfo['midi'];
+		$thisfile_midi_raw           = &$thisfile_midi['raw'];
+
+		$ThisFileInfo['fileformat']          = 'midi';
+		$ThisFileInfo['audio']['dataformat'] = 'midi';
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$MIDIdata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
+		$offset = 0;
+		$MIDIheaderID = substr($MIDIdata, $offset, 4); // 'MThd'
+		if ($MIDIheaderID != 'MThd') {
+			$ThisFileInfo['error'][] = 'Expecting "MThd" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$MIDIheaderID.'"';
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+		$offset += 4;
+		$thisfile_midi_raw['headersize']    = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
+		$offset += 4;
+		$thisfile_midi_raw['fileformat']    = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
+		$offset += 2;
+		$thisfile_midi_raw['tracks']        = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
+		$offset += 2;
+		$thisfile_midi_raw['ticksperqnote'] = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 2));
+		$offset += 2;
+
+		for ($i = 0; $i < $thisfile_midi_raw['tracks']; $i++) {
+			if ((strlen($MIDIdata) - $offset) < 8) {
+				$MIDIdata .= fread($fd, GETID3_FREAD_BUFFER_SIZE);
+			}
+			$trackID = substr($MIDIdata, $offset, 4);
+			$offset += 4;
+			if ($trackID == 'MTrk') {
+				$tracksize = getid3_lib::BigEndian2Int(substr($MIDIdata, $offset, 4));
+				$offset += 4;
+				// $thisfile_midi['tracks'][$i]['size'] = $tracksize;
+				$trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
+				$offset += $tracksize;
+			} else {
+				$ThisFileInfo['error'][] = 'Expecting "MTrk" at '.$offset.', found '.$trackID.' instead';
+				return false;
+			}
+		}
+
+		if (!isset($trackdataarray) || !is_array($trackdataarray)) {
+			$ThisFileInfo['error'][] = 'Cannot find MIDI track information';
+			unset($thisfile_midi);
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+
+		if ($scanwholefile) { // this can take quite a long time, so have the option to bypass it if speed is very important
+			$thisfile_midi['totalticks']      = 0;
+			$ThisFileInfo['playtime_seconds'] = 0;
+			$CurrentMicroSecondsPerBeat       = 500000; // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
+			$CurrentBeatsPerMinute            = 120;    // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
+			$MicroSecondsPerQuarterNoteAfter  = array ();
+
+			foreach ($trackdataarray as $tracknumber => $trackdata) {
+
+				$eventsoffset               = 0;
+				$LastIssuedMIDIcommand      = 0;
+				$LastIssuedMIDIchannel      = 0;
+				$CumulativeDeltaTime        = 0;
+				$TicksAtCurrentBPM = 0;
+				while ($eventsoffset < strlen($trackdata)) {
+					$eventid = 0;
+					if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
+						$eventid = count($MIDIevents[$tracknumber]);
+					}
+					$deltatime = 0;
+					for ($i = 0; $i < 4; $i++) {
+						$deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
+						$deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7F);
+						if ($deltatimebyte & 0x80) {
+							// another byte follows
+						} else {
+							break;
+						}
+					}
+					$CumulativeDeltaTime += $deltatime;
+					$TicksAtCurrentBPM   += $deltatime;
+					$MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
+					$MIDI_event_channel                                  = ord(substr($trackdata, $eventsoffset++, 1));
+					if ($MIDI_event_channel & 0x80) {
+						// OK, normal event - MIDI command has MSB set
+						$LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
+						$LastIssuedMIDIchannel = $MIDI_event_channel & 0x0F;
+					} else {
+						// running event - assume last command
+						$eventsoffset--;
+					}
+					$MIDIevents[$tracknumber][$eventid]['eventid']   = $LastIssuedMIDIcommand;
+					$MIDIevents[$tracknumber][$eventid]['channel']   = $LastIssuedMIDIchannel;
+					if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x08) { // Note off (key is released)
+
+						$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
+						$velocity   = ord(substr($trackdata, $eventsoffset++, 1));
+
+					} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x09) { // Note on (key is pressed)
+
+						$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
+						$velocity   = ord(substr($trackdata, $eventsoffset++, 1));
+
+					} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0A) { // Key after-touch
+
+						$notenumber = ord(substr($trackdata, $eventsoffset++, 1));
+						$velocity   = ord(substr($trackdata, $eventsoffset++, 1));
+
+					} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0B) { // Control Change
+
+						$controllernum = ord(substr($trackdata, $eventsoffset++, 1));
+						$newvalue      = ord(substr($trackdata, $eventsoffset++, 1));
+
+					} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0C) { // Program (patch) change
+
+						$newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
+
+						$thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
+						if ($tracknumber == 10) {
+							$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
+						} else {
+							$thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
+						}
+
+					} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0D) { // Channel after-touch
+
+						$channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
+
+					} elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0E) { // Pitch wheel change (2000H is normal or no change)
+
+						$changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
+						$changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
+						$pitchwheelchange = (($changeMSB & 0x7F) << 7) & ($changeLSB & 0x7F);
+
+					} elseif (($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x0F) && ($MIDIevents[$tracknumber][$eventid]['channel'] == 0x0F)) {
+
+						$METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
+						$METAeventLength  = ord(substr($trackdata, $eventsoffset++, 1));
+						$METAeventData    = substr($trackdata, $eventsoffset, $METAeventLength);
+						$eventsoffset += $METAeventLength;
+						switch ($METAeventCommand) {
+							case 0x00: // Set track sequence number
+								$track_sequence_number = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
+								break;
+
+							case 0x01: // Text: generic
+								$text_generic = substr($METAeventData, 0, $METAeventLength);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
+								$thisfile_midi['comments']['comment'][] = $text_generic;
+								break;
+
+							case 0x02: // Text: copyright
+								$text_copyright = substr($METAeventData, 0, $METAeventLength);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
+								$thisfile_midi['comments']['copyright'][] = $text_copyright;
+								break;
+
+							case 0x03: // Text: track name
+								$text_trackname = substr($METAeventData, 0, $METAeventLength);
+								$thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
+								break;
+
+							case 0x04: // Text: track instrument name
+								$text_instrument = substr($METAeventData, 0, $METAeventLength);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
+								break;
+
+							case 0x05: // Text: lyrics
+								$text_lyrics  = substr($METAeventData, 0, $METAeventLength);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
+								if (!isset($thisfile_midi['lyrics'])) {
+									$thisfile_midi['lyrics'] = '';
+								}
+								$thisfile_midi['lyrics'] .= $text_lyrics."\n";
+								break;
+
+							case 0x06: // Text: marker
+								$text_marker = substr($METAeventData, 0, $METAeventLength);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
+								break;
+
+							case 0x07: // Text: cue point
+								$text_cuepoint = substr($METAeventData, 0, $METAeventLength);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
+								break;
+
+							case 0x2F: // End Of Track
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
+								break;
+
+							case 0x51: // Tempo: microseconds / quarter note
+								$CurrentMicroSecondsPerBeat = getid3_lib::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
+								if ($CurrentMicroSecondsPerBeat == 0) {
+									$ThisFileInfo['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
+									return false;
+								}
+								$thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
+								$CurrentBeatsPerMinute = (1000000 / $CurrentMicroSecondsPerBeat) * 60;
+								$MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
+								$TicksAtCurrentBPM = 0;
+								break;
+
+							case 0x58: // Time signature
+								$timesig_numerator   = getid3_lib::BigEndian2Int($METAeventData{0});
+								$timesig_denominator = pow(2, getid3_lib::BigEndian2Int($METAeventData{1})); // $02 -> x/4, $03 -> x/8, etc
+								$timesig_32inqnote   = getid3_lib::BigEndian2Int($METAeventData{2});         // number of 32nd notes to the quarter note
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote']   = $timesig_32inqnote;
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator']   = $timesig_numerator;
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text']        = $timesig_numerator.'/'.$timesig_denominator;
+								$thisfile_midi['timesignature'][] = $timesig_numerator.'/'.$timesig_denominator;
+								break;
+
+							case 0x59: // Keysignature
+								$keysig_sharpsflats = getid3_lib::BigEndian2Int($METAeventData{0});
+								if ($keysig_sharpsflats & 0x80) {
+									// (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
+									$keysig_sharpsflats -= 256;
+								}
+
+								$keysig_majorminor  = getid3_lib::BigEndian2Int($METAeventData{1}); // 0 -> major, 1 -> minor
+								$keysigs = array(-7=>'Cb', -6=>'Gb', -5=>'Db', -4=>'Ab', -3=>'Eb', -2=>'Bb', -1=>'F', 0=>'C', 1=>'G', 2=>'D', 3=>'A', 4=>'E', 5=>'B', 6=>'F#', 7=>'C#');
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats']  = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor']  = (bool) $keysig_majorminor;
+								//$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text']   = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
+
+								// $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
+								$thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats].' '.((bool) $keysig_majorminor ? 'minor' : 'major');
+								break;
+
+							case 0x7F: // Sequencer specific information
+								$custom_data = substr($METAeventData, 0, $METAeventLength);
+								break;
+
+							default:
+								$ThisFileInfo['warning'][] = 'Unhandled META Event Command: '.$METAeventCommand;
+								break;
+						}
+
+					} else {
+
+						$ThisFileInfo['warning'][] = 'Unhandled MIDI Event ID: '.$MIDIevents[$tracknumber][$eventid]['eventid'].' + Channel ID: '.$MIDIevents[$tracknumber][$eventid]['channel'];
+
+					}
+				}
+				if (($tracknumber > 0) || (count($trackdataarray) == 1)) {
+					$thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
+				}
+			}
+			$previoustickoffset = null;
+
+			ksort($MicroSecondsPerQuarterNoteAfter);
+			foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
+				if (is_null($previoustickoffset)) {
+					$prevmicrosecondsperbeat = $microsecondsperbeat;
+					$previoustickoffset = $tickoffset;
+					continue;
+				}
+				if ($thisfile_midi['totalticks'] > $tickoffset) {
+
+					if ($thisfile_midi_raw['ticksperqnote'] == 0) {
+						$ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
+						return false;
+					}
+
+					$ThisFileInfo['playtime_seconds'] += (($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($prevmicrosecondsperbeat / 1000000);
+
+					$prevmicrosecondsperbeat = $microsecondsperbeat;
+					$previoustickoffset = $tickoffset;
+				}
+			}
+			if ($thisfile_midi['totalticks'] > $previoustickoffset) {
+
+				if ($thisfile_midi_raw['ticksperqnote'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
+					return false;
+				}
+
+				$ThisFileInfo['playtime_seconds'] += (($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote']) * ($microsecondsperbeat / 1000000);
+
+			}
+		}
+		
+
+		if (@$ThisFileInfo['playtime_seconds'] > 0) {
+			$ThisFileInfo['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+		}
+
+		if (!empty($thisfile_midi['lyrics'])) {
+			$thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
+		}
+
+		return true;
+	}
+
+	function GeneralMIDIinstrumentLookup($instrumentid) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			0	Acoustic Grand
+			1	Bright Acoustic
+			2	Electric Grand
+			3	Honky-Tonk
+			4	Electric Piano 1
+			5	Electric Piano 2
+			6	Harpsichord
+			7	Clavier
+			8	Celesta
+			9	Glockenspiel
+			10	Music Box
+			11	Vibraphone
+			12	Marimba
+			13	Xylophone
+			14	Tubular Bells
+			15	Dulcimer
+			16	Drawbar Organ
+			17	Percussive Organ
+			18	Rock Organ
+			19	Church Organ
+			20	Reed Organ
+			21	Accordian
+			22	Harmonica
+			23	Tango Accordian
+			24	Acoustic Guitar (nylon)
+			25	Acoustic Guitar (steel)
+			26	Electric Guitar (jazz)
+			27	Electric Guitar (clean)
+			28	Electric Guitar (muted)
+			29	Overdriven Guitar
+			30	Distortion Guitar
+			31	Guitar Harmonics
+			32	Acoustic Bass
+			33	Electric Bass (finger)
+			34	Electric Bass (pick)
+			35	Fretless Bass
+			36	Slap Bass 1
+			37	Slap Bass 2
+			38	Synth Bass 1
+			39	Synth Bass 2
+			40	Violin
+			41	Viola
+			42	Cello
+			43	Contrabass
+			44	Tremolo Strings
+			45	Pizzicato Strings
+			46	Orchestral Strings
+			47	Timpani
+			48	String Ensemble 1
+			49	String Ensemble 2
+			50	SynthStrings 1
+			51	SynthStrings 2
+			52	Choir Aahs
+			53	Voice Oohs
+			54	Synth Voice
+			55	Orchestra Hit
+			56	Trumpet
+			57	Trombone
+			58	Tuba
+			59	Muted Trumpet
+			60	French Horn
+			61	Brass Section
+			62	SynthBrass 1
+			63	SynthBrass 2
+			64	Soprano Sax
+			65	Alto Sax
+			66	Tenor Sax
+			67	Baritone Sax
+			68	Oboe
+			69	English Horn
+			70	Bassoon
+			71	Clarinet
+			72	Piccolo
+			73	Flute
+			74	Recorder
+			75	Pan Flute
+			76	Blown Bottle
+			77	Shakuhachi
+			78	Whistle
+			79	Ocarina
+			80	Lead 1 (square)
+			81	Lead 2 (sawtooth)
+			82	Lead 3 (calliope)
+			83	Lead 4 (chiff)
+			84	Lead 5 (charang)
+			85	Lead 6 (voice)
+			86	Lead 7 (fifths)
+			87	Lead 8 (bass + lead)
+			88	Pad 1 (new age)
+			89	Pad 2 (warm)
+			90	Pad 3 (polysynth)
+			91	Pad 4 (choir)
+			92	Pad 5 (bowed)
+			93	Pad 6 (metallic)
+			94	Pad 7 (halo)
+			95	Pad 8 (sweep)
+			96	FX 1 (rain)
+			97	FX 2 (soundtrack)
+			98	FX 3 (crystal)
+			99	FX 4 (atmosphere)
+			100	FX 5 (brightness)
+			101	FX 6 (goblins)
+			102	FX 7 (echoes)
+			103	FX 8 (sci-fi)
+			104	Sitar
+			105	Banjo
+			106	Shamisen
+			107	Koto
+			108	Kalimba
+			109	Bagpipe
+			110	Fiddle
+			111	Shanai
+			112	Tinkle Bell
+			113	Agogo
+			114	Steel Drums
+			115	Woodblock
+			116	Taiko Drum
+			117	Melodic Tom
+			118	Synth Drum
+			119	Reverse Cymbal
+			120	Guitar Fret Noise
+			121	Breath Noise
+			122	Seashore
+			123	Bird Tweet
+			124	Telephone Ring
+			125	Helicopter
+			126	Applause
+			127	Gunshot
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIinstrument');
+	}
+
+	function GeneralMIDIpercussionLookup($instrumentid) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			35	Acoustic Bass Drum
+			36	Bass Drum 1
+			37	Side Stick
+			38	Acoustic Snare
+			39	Hand Clap
+			40	Electric Snare
+			41	Low Floor Tom
+			42	Closed Hi-Hat
+			43	High Floor Tom
+			44	Pedal Hi-Hat
+			45	Low Tom
+			46	Open Hi-Hat
+			47	Low-Mid Tom
+			48	Hi-Mid Tom
+			49	Crash Cymbal 1
+			50	High Tom
+			51	Ride Cymbal 1
+			52	Chinese Cymbal
+			53	Ride Bell
+			54	Tambourine
+			55	Splash Cymbal
+			56	Cowbell
+			57	Crash Cymbal 2
+			59	Ride Cymbal 2
+			60	Hi Bongo
+			61	Low Bongo
+			62	Mute Hi Conga
+			63	Open Hi Conga
+			64	Low Conga
+			65	High Timbale
+			66	Low Timbale
+			67	High Agogo
+			68	Low Agogo
+			69	Cabasa
+			70	Maracas
+			71	Short Whistle
+			72	Long Whistle
+			73	Short Guiro
+			74	Long Guiro
+			75	Claves
+			76	Hi Wood Block
+			77	Low Wood Block
+			78	Mute Cuica
+			79	Open Cuica
+			80	Mute Triangle
+			81	Open Triangle
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($instrumentid, $begin, __LINE__, __FILE__, 'GeneralMIDIpercussion');
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.mod.php b/apps/media/getID3/getid3/module.audio.mod.php
new file mode 100644
index 0000000000000000000000000000000000000000..7f81ff360be5eff063283b2535e3df640dd8ae9c
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.mod.php
@@ -0,0 +1,101 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.mod.php                                        //
+// module for analyzing MOD Audio files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_mod
+{
+
+	// new combined constructor
+	function getid3_mod(&$fd, &$ThisFileInfo, $option) {
+
+		if ($option === 'mod') {
+			$this->getMODheaderFilepointer($fd, $ThisFileInfo);
+		}
+		elseif ($option === 'xm') {
+			$this->getXMheaderFilepointer($fd, $ThisFileInfo);
+		}
+		elseif ($option === 'it') {
+			$this->getITheaderFilepointer($fd, $ThisFileInfo);
+		}
+		elseif ($option === 's3m') {
+			$this->getS3MheaderFilepointer($fd, $ThisFileInfo);
+		}
+	}
+
+
+	function getMODheaderFilepointer(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'] + 1080);
+		$FormatID = fread($fd, 4);
+		if (!ereg('^(M.K.|[5-9]CHN|[1-3][0-9]CH)$', $FormatID)) {
+			$ThisFileInfo['error'][] = 'This is not a known type of MOD file';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat'] = 'mod';
+
+		$ThisFileInfo['error'][] = 'MOD parsing not enabled in this version of getID3()';
+		return false;
+	}
+
+	function getXMheaderFilepointer(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset']);
+		$FormatID = fread($fd, 15);
+		if (!ereg('^Extended Module$', $FormatID)) {
+			$ThisFileInfo['error'][] = 'This is not a known type of XM-MOD file';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat'] = 'xm';
+
+		$ThisFileInfo['error'][] = 'XM-MOD parsing not enabled in this version of getID3()';
+		return false;
+	}
+
+	function getS3MheaderFilepointer(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'] + 44);
+		$FormatID = fread($fd, 4);
+		if (!ereg('^SCRM$', $FormatID)) {
+			$ThisFileInfo['error'][] = 'This is not a ScreamTracker MOD file';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat'] = 's3m';
+
+		$ThisFileInfo['error'][] = 'ScreamTracker parsing not enabled in this version of getID3()';
+		return false;
+	}
+
+	function getITheaderFilepointer(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset']);
+		$FormatID = fread($fd, 4);
+		if (!ereg('^IMPM$', $FormatID)) {
+			$ThisFileInfo['error'][] = 'This is not an ImpulseTracker MOD file';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat'] = 'it';
+
+		$ThisFileInfo['error'][] = 'ImpulseTracker parsing not enabled in this version of getID3()';
+		return false;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.monkey.php b/apps/media/getID3/getid3/module.audio.monkey.php
new file mode 100644
index 0000000000000000000000000000000000000000..42382ad15e6c3ccc202bb3cd2a616159b5a9fc15
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.monkey.php
@@ -0,0 +1,202 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.monkey.php                                     //
+// module for analyzing Monkey's Audio files                   //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_monkey
+{
+
+	function getid3_monkey(&$fd, &$ThisFileInfo) {
+		// based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
+		// http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
+
+		$ThisFileInfo['fileformat']            = 'mac';
+		$ThisFileInfo['audio']['dataformat']   = 'mac';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+		$ThisFileInfo['audio']['lossless']     = true;
+
+		$ThisFileInfo['monkeys_audio']['raw'] = array();
+		$thisfile_monkeysaudio                = &$ThisFileInfo['monkeys_audio'];
+		$thisfile_monkeysaudio_raw            = &$thisfile_monkeysaudio['raw'];
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$MACheaderData = fread($fd, 74);
+
+		$thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
+		if ($thisfile_monkeysaudio_raw['magic'] != 'MAC ') {
+			$ThisFileInfo['error'][] = 'Expecting "MAC" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_monkeysaudio_raw['magic'].'"';
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+		$thisfile_monkeysaudio_raw['nVersion']             = getid3_lib::LittleEndian2Int(substr($MACheaderData, 4, 2)); // appears to be uint32 in 3.98+
+
+		if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
+			$thisfile_monkeysaudio_raw['nCompressionLevel']    = getid3_lib::LittleEndian2Int(substr($MACheaderData, 6, 2));
+			$thisfile_monkeysaudio_raw['nFormatFlags']         = getid3_lib::LittleEndian2Int(substr($MACheaderData, 8, 2));
+			$thisfile_monkeysaudio_raw['nChannels']            = getid3_lib::LittleEndian2Int(substr($MACheaderData, 10, 2));
+			$thisfile_monkeysaudio_raw['nSampleRate']          = getid3_lib::LittleEndian2Int(substr($MACheaderData, 12, 4));
+			$thisfile_monkeysaudio_raw['nHeaderDataBytes']     = getid3_lib::LittleEndian2Int(substr($MACheaderData, 16, 4));
+			$thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, 20, 4));
+			$thisfile_monkeysaudio_raw['nTotalFrames']         = getid3_lib::LittleEndian2Int(substr($MACheaderData, 24, 4));
+			$thisfile_monkeysaudio_raw['nFinalFrameSamples']   = getid3_lib::LittleEndian2Int(substr($MACheaderData, 28, 4));
+			$thisfile_monkeysaudio_raw['nPeakLevel']           = getid3_lib::LittleEndian2Int(substr($MACheaderData, 32, 4));
+			$thisfile_monkeysaudio_raw['nSeekElements']        = getid3_lib::LittleEndian2Int(substr($MACheaderData, 38, 2));
+			$offset = 8;
+		} else {
+			$offset = 8;
+			// APE_DESCRIPTOR
+			$thisfile_monkeysaudio_raw['nDescriptorBytes']       = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nHeaderBytes']           = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nSeekTableBytes']        = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nHeaderDataBytes']       = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nAPEFrameDataBytes']     = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nTerminatingDataBytes']  = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset,  4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['cFileMD5']               =                              substr($MACheaderData, $offset, 16);
+			$offset += 16;
+
+			// APE_HEADER
+			$thisfile_monkeysaudio_raw['nCompressionLevel']    = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
+			$offset += 2;
+			$thisfile_monkeysaudio_raw['nFormatFlags']         = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
+			$offset += 2;
+			$thisfile_monkeysaudio_raw['nBlocksPerFrame']      = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nFinalFrameBlocks']    = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nTotalFrames']         = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
+			$offset += 4;
+			$thisfile_monkeysaudio_raw['nBitsPerSample']       = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
+			$offset += 2;
+			$thisfile_monkeysaudio_raw['nChannels']            = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 2));
+			$offset += 2;
+			$thisfile_monkeysaudio_raw['nSampleRate']          = getid3_lib::LittleEndian2Int(substr($MACheaderData, $offset, 4));
+			$offset += 4;
+		}
+
+		$thisfile_monkeysaudio['flags']['8-bit']         = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0001);
+		$thisfile_monkeysaudio['flags']['crc-32']        = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0002);
+		$thisfile_monkeysaudio['flags']['peak_level']    = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0004);
+		$thisfile_monkeysaudio['flags']['24-bit']        = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0008);
+		$thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0010);
+		$thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x0020);
+		$thisfile_monkeysaudio['version']                = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
+		$thisfile_monkeysaudio['compression']            = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
+		if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
+			$thisfile_monkeysaudio['samples_per_frame']      = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
+		}
+		$thisfile_monkeysaudio['bits_per_sample']        = ($thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16));
+		$thisfile_monkeysaudio['channels']               = $thisfile_monkeysaudio_raw['nChannels'];
+		$ThisFileInfo['audio']['channels']               = $thisfile_monkeysaudio['channels'];
+		$thisfile_monkeysaudio['sample_rate']            = $thisfile_monkeysaudio_raw['nSampleRate'];
+		if ($thisfile_monkeysaudio['sample_rate'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt MAC file: frequency == zero';
+			return false;
+		}
+		$ThisFileInfo['audio']['sample_rate']            = $thisfile_monkeysaudio['sample_rate'];
+		if ($thisfile_monkeysaudio['flags']['peak_level']) {
+			$thisfile_monkeysaudio['peak_level']         = $thisfile_monkeysaudio_raw['nPeakLevel'];
+			$thisfile_monkeysaudio['peak_ratio']         = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
+		}
+		if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
+			$thisfile_monkeysaudio['samples']            = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame']) + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
+		} else {
+			$thisfile_monkeysaudio['samples']            = (($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame']) + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
+		}
+		$thisfile_monkeysaudio['playtime']               = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
+		if ($thisfile_monkeysaudio['playtime'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt MAC file: playtime == zero';
+			return false;
+		}
+		$ThisFileInfo['playtime_seconds']                = $thisfile_monkeysaudio['playtime'];
+		$thisfile_monkeysaudio['compressed_size']        = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'];
+		$thisfile_monkeysaudio['uncompressed_size']      = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
+		if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt MAC file: uncompressed_size == zero';
+			return false;
+		}
+		$thisfile_monkeysaudio['compression_ratio']      = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']);
+		$thisfile_monkeysaudio['bitrate']                = (($thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample']) / $thisfile_monkeysaudio['playtime']) * $thisfile_monkeysaudio['compression_ratio'];
+		$ThisFileInfo['audio']['bitrate']                = $thisfile_monkeysaudio['bitrate'];
+
+		// add size of MAC header to avdataoffset
+		if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
+			$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
+			$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
+			$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
+			$ThisFileInfo['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
+
+			$ThisFileInfo['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
+		} else {
+			$ThisFileInfo['avdataoffset'] += $offset;
+		}
+
+		if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
+			if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("\x00", 16)) {
+				//$ThisFileInfo['warning'][] = 'cFileMD5 is null';
+			} else {
+				$ThisFileInfo['md5_data_source'] = '';
+				$md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
+				for ($i = 0; $i < strlen($md5); $i++) {
+					$ThisFileInfo['md5_data_source'] .= str_pad(dechex(ord($md5{$i})), 2, '00', STR_PAD_LEFT);
+				}
+				if (!preg_match('/^[0-9a-f]{32}$/', $ThisFileInfo['md5_data_source'])) {
+					unset($ThisFileInfo['md5_data_source']);
+				}
+			}
+		}
+
+
+
+		$ThisFileInfo['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
+		$ThisFileInfo['audio']['encoder']         = 'MAC v'.number_format($thisfile_monkeysaudio['version'], 2);
+		$ThisFileInfo['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']).' compression';
+
+		return true;
+	}
+
+	function MonkeyCompressionLevelNameLookup($compressionlevel) {
+		static $MonkeyCompressionLevelNameLookup = array(
+			0     => 'unknown',
+			1000  => 'fast',
+			2000  => 'normal',
+			3000  => 'high',
+			4000  => 'extra-high',
+			5000  => 'insane'
+		);
+		return (isset($MonkeyCompressionLevelNameLookup[$compressionlevel]) ? $MonkeyCompressionLevelNameLookup[$compressionlevel] : 'invalid');
+	}
+
+	function MonkeySamplesPerFrame($versionid, $compressionlevel) {
+		if ($versionid >= 3950) {
+			return 73728 * 4;
+		} elseif ($versionid >= 3900) {
+			return 73728;
+		} elseif (($versionid >= 3800) && ($compressionlevel == 4000)) {
+			return 73728;
+		} else {
+			return 9216;
+		}
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.mp3.php b/apps/media/getID3/getid3/module.audio.mp3.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac9a27380e4e0d71ecfc4cc66f2224aaed49c470
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.mp3.php
@@ -0,0 +1,2022 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.mp3.php                                        //
+// module for analyzing MP3 files                              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+// number of frames to scan to determine if MPEG-audio sequence is valid
+// Lower this number to 5-20 for faster scanning
+// Increase this number to 50+ for most accurate detection of valid VBR/CBR
+// mpeg-audio streams
+define('GETID3_MP3_VALID_CHECK_FRAMES', 35);
+
+
+class getid3_mp3
+{
+
+	var $allow_bruteforce = false; // forces getID3() to scan the file byte-by-byte and log all the valid audio frame headers - extremely slow, unrecommended, but may provide data from otherwise-unusuable files
+
+	function getid3_mp3(&$fd, &$ThisFileInfo) {
+
+		if (!$this->getOnlyMPEGaudioInfo($fd, $ThisFileInfo, $ThisFileInfo['avdataoffset'])) {
+			if ($this->allow_bruteforce) {
+				$ThisFileInfo['error'][] = 'Rescanning file in BruteForce mode';
+				$this->getOnlyMPEGaudioInfoBruteForce($fd, $ThisFileInfo);
+			}
+		}
+
+
+		if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode'])) {
+			$ThisFileInfo['audio']['bitrate_mode'] = strtolower($ThisFileInfo['mpeg']['audio']['bitrate_mode']);
+		}
+
+		if (((isset($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] > $ThisFileInfo['id3v2']['headerlength'])) || (!isset($ThisFileInfo['id3v2']) && ($ThisFileInfo['avdataoffset'] > 0)))) {
+
+			$synchoffsetwarning = 'Unknown data before synch ';
+			if (isset($ThisFileInfo['id3v2']['headerlength'])) {
+				$synchoffsetwarning .= '(ID3v2 header ends at '.$ThisFileInfo['id3v2']['headerlength'].', then '.($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']).' bytes garbage, ';
+			} else {
+				$synchoffsetwarning .= '(should be at beginning of file, ';
+			}
+			$synchoffsetwarning .= 'synch detected at '.$ThisFileInfo['avdataoffset'].')';
+			if (@$ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
+
+				if (!empty($ThisFileInfo['id3v2']['headerlength']) && (($ThisFileInfo['avdataoffset'] - $ThisFileInfo['id3v2']['headerlength']) == $ThisFileInfo['mpeg']['audio']['framelength'])) {
+
+					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90-3.92) DLL in CBR mode.';
+					$ThisFileInfo['audio']['codec'] = 'LAME';
+					$CurrentDataLAMEversionString = 'LAME3.';
+
+				} elseif (empty($ThisFileInfo['id3v2']['headerlength']) && ($ThisFileInfo['avdataoffset'] == $ThisFileInfo['mpeg']['audio']['framelength'])) {
+
+					$synchoffsetwarning .= '. This is a known problem with some versions of LAME (3.90 - 3.92) DLL in CBR mode.';
+					$ThisFileInfo['audio']['codec'] = 'LAME';
+					$CurrentDataLAMEversionString = 'LAME3.';
+
+				}
+
+			}
+			$ThisFileInfo['warning'][] = $synchoffsetwarning;
+
+		}
+
+		if (isset($ThisFileInfo['mpeg']['audio']['LAME'])) {
+			$ThisFileInfo['audio']['codec'] = 'LAME';
+			if (!empty($ThisFileInfo['mpeg']['audio']['LAME']['long_version'])) {
+				$ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['long_version'], "\x00");
+			} elseif (!empty($ThisFileInfo['mpeg']['audio']['LAME']['short_version'])) {
+				$ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['mpeg']['audio']['LAME']['short_version'], "\x00");
+			}
+		}
+
+		$CurrentDataLAMEversionString = (!empty($CurrentDataLAMEversionString) ? $CurrentDataLAMEversionString : @$ThisFileInfo['audio']['encoder']);
+		if (!empty($CurrentDataLAMEversionString) && (substr($CurrentDataLAMEversionString, 0, 6) == 'LAME3.') && !preg_match('[0-9\)]', substr($CurrentDataLAMEversionString, -1))) {
+			// a version number of LAME that does not end with a number like "LAME3.92"
+			// or with a closing parenthesis like "LAME3.88 (alpha)"
+			// or a version of LAME with the LAMEtag-not-filled-in-DLL-mode bug (3.90-3.92)
+
+			// not sure what the actual last frame length will be, but will be less than or equal to 1441
+			$PossiblyLongerLAMEversion_FrameLength = 1441;
+
+			// Not sure what version of LAME this is - look in padding of last frame for longer version string
+			$PossibleLAMEversionStringOffset = $ThisFileInfo['avdataend'] - $PossiblyLongerLAMEversion_FrameLength;
+			fseek($fd, $PossibleLAMEversionStringOffset);
+			$PossiblyLongerLAMEversion_Data = fread($fd, $PossiblyLongerLAMEversion_FrameLength);
+			switch (substr($CurrentDataLAMEversionString, -1)) {
+				case 'a':
+				case 'b':
+					// "LAME3.94a" will have a longer version string of "LAME3.94 (alpha)" for example
+					// need to trim off "a" to match longer string
+					$CurrentDataLAMEversionString = substr($CurrentDataLAMEversionString, 0, -1);
+					break;
+			}
+			if (($PossiblyLongerLAMEversion_String = strstr($PossiblyLongerLAMEversion_Data, $CurrentDataLAMEversionString)) !== false) {
+				if (substr($PossiblyLongerLAMEversion_String, 0, strlen($CurrentDataLAMEversionString)) == $CurrentDataLAMEversionString) {
+					$PossiblyLongerLAMEversion_NewString = substr($PossiblyLongerLAMEversion_String, 0, strspn($PossiblyLongerLAMEversion_String, 'LAME0123456789., (abcdefghijklmnopqrstuvwxyzJFSOND)')); //"LAME3.90.3"  "LAME3.87 (beta 1, Sep 27 2000)" "LAME3.88 (beta)"
+					if (strlen($PossiblyLongerLAMEversion_NewString) > strlen(@$ThisFileInfo['audio']['encoder'])) {
+						$ThisFileInfo['audio']['encoder'] = $PossiblyLongerLAMEversion_NewString;
+					}
+				}
+			}
+		}
+		if (!empty($ThisFileInfo['audio']['encoder'])) {
+			$ThisFileInfo['audio']['encoder'] = rtrim($ThisFileInfo['audio']['encoder'], "\x00 ");
+		}
+
+		switch (@$ThisFileInfo['mpeg']['audio']['layer']) {
+			case 1:
+			case 2:
+				$ThisFileInfo['audio']['dataformat'] = 'mp'.$ThisFileInfo['mpeg']['audio']['layer'];
+				break;
+		}
+		if (@$ThisFileInfo['fileformat'] == 'mp3') {
+			switch ($ThisFileInfo['audio']['dataformat']) {
+				case 'mp1':
+				case 'mp2':
+				case 'mp3':
+					$ThisFileInfo['fileformat'] = $ThisFileInfo['audio']['dataformat'];
+					break;
+
+				default:
+					$ThisFileInfo['warning'][] = 'Expecting [audio][dataformat] to be mp1/mp2/mp3 when fileformat == mp3, [audio][dataformat] actually "'.$ThisFileInfo['audio']['dataformat'].'"';
+					break;
+			}
+		}
+
+		if (empty($ThisFileInfo['fileformat'])) {
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['audio']['bitrate_mode']);
+			unset($ThisFileInfo['avdataoffset']);
+			unset($ThisFileInfo['avdataend']);
+			return false;
+		}
+
+		$ThisFileInfo['mime_type']         = 'audio/mpeg';
+		$ThisFileInfo['audio']['lossless'] = false;
+
+		// Calculate playtime
+		if (!isset($ThisFileInfo['playtime_seconds']) && isset($ThisFileInfo['audio']['bitrate']) && ($ThisFileInfo['audio']['bitrate'] > 0)) {
+			$ThisFileInfo['playtime_seconds'] = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 / $ThisFileInfo['audio']['bitrate'];
+		}
+
+		$ThisFileInfo['audio']['encoder_options'] = $this->GuessEncoderOptions($ThisFileInfo);
+
+		return true;
+	}
+
+
+	function GuessEncoderOptions(&$ThisFileInfo) {
+		// shortcuts
+		if (!empty($ThisFileInfo['mpeg']['audio'])) {
+			$thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio'];
+			if (!empty($thisfile_mpeg_audio['LAME'])) {
+				$thisfile_mpeg_audio_lame = &$thisfile_mpeg_audio['LAME'];
+			}
+		}
+
+		$encoder_options = '';
+		static $NamedPresetBitrates = array(16, 24, 40, 56, 112, 128, 160, 192, 256);
+
+		if ((@$thisfile_mpeg_audio['VBR_method'] == 'Fraunhofer') && !empty($thisfile_mpeg_audio['VBR_quality'])) {
+
+			$encoder_options = 'VBR q'.$thisfile_mpeg_audio['VBR_quality'];
+
+		} elseif (!empty($thisfile_mpeg_audio_lame['preset_used']) && (!in_array($thisfile_mpeg_audio_lame['preset_used_id'], $NamedPresetBitrates))) {
+
+			$encoder_options = $thisfile_mpeg_audio_lame['preset_used'];
+
+		} elseif (!empty($thisfile_mpeg_audio_lame['vbr_quality'])) {
+
+			static $KnownEncoderValues = array();
+			if (empty($KnownEncoderValues)) {
+
+				//$KnownEncoderValues[abrbitrate_minbitrate][vbr_quality][raw_vbr_method][raw_noise_shaping][raw_stereo_mode][ath_type][lowpass_frequency] = 'preset name';
+				$KnownEncoderValues[0xFF][58][1][1][3][2][20500] = '--alt-preset insane';        // 3.90,   3.90.1, 3.92
+				$KnownEncoderValues[0xFF][58][1][1][3][2][20600] = '--alt-preset insane';        // 3.90.2, 3.90.3, 3.91
+				$KnownEncoderValues[0xFF][57][1][1][3][4][20500] = '--alt-preset insane';        // 3.94,   3.95
+				$KnownEncoderValues['**'][78][3][2][3][2][19500] = '--alt-preset extreme';       // 3.90,   3.90.1, 3.92
+				$KnownEncoderValues['**'][78][3][2][3][2][19600] = '--alt-preset extreme';       // 3.90.2, 3.91
+				$KnownEncoderValues['**'][78][3][1][3][2][19600] = '--alt-preset extreme';       // 3.90.3
+				$KnownEncoderValues['**'][78][4][2][3][2][19500] = '--alt-preset fast extreme';  // 3.90,   3.90.1, 3.92
+				$KnownEncoderValues['**'][78][4][2][3][2][19600] = '--alt-preset fast extreme';  // 3.90.2, 3.90.3, 3.91
+				$KnownEncoderValues['**'][78][3][2][3][4][19000] = '--alt-preset standard';      // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
+				$KnownEncoderValues['**'][78][3][1][3][4][19000] = '--alt-preset standard';      // 3.90.3
+				$KnownEncoderValues['**'][78][4][2][3][4][19000] = '--alt-preset fast standard'; // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
+				$KnownEncoderValues['**'][78][4][1][3][4][19000] = '--alt-preset fast standard'; // 3.90.3
+				$KnownEncoderValues['**'][88][4][1][3][3][19500] = '--r3mix';                    // 3.90,   3.90.1, 3.92
+				$KnownEncoderValues['**'][88][4][1][3][3][19600] = '--r3mix';                    // 3.90.2, 3.90.3, 3.91
+				$KnownEncoderValues['**'][67][4][1][3][4][18000] = '--r3mix';                    // 3.94,   3.95
+				$KnownEncoderValues['**'][68][3][2][3][4][18000] = '--alt-preset medium';        // 3.90.3
+				$KnownEncoderValues['**'][68][4][2][3][4][18000] = '--alt-preset fast medium';   // 3.90.3
+
+				$KnownEncoderValues[0xFF][99][1][1][1][2][0]     = '--preset studio';            // 3.90,   3.90.1, 3.90.2, 3.91, 3.92
+				$KnownEncoderValues[0xFF][58][2][1][3][2][20600] = '--preset studio';            // 3.90.3, 3.93.1
+				$KnownEncoderValues[0xFF][58][2][1][3][2][20500] = '--preset studio';            // 3.93
+				$KnownEncoderValues[0xFF][57][2][1][3][4][20500] = '--preset studio';            // 3.94,   3.95
+				$KnownEncoderValues[0xC0][88][1][1][1][2][0]     = '--preset cd';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
+				$KnownEncoderValues[0xC0][58][2][2][3][2][19600] = '--preset cd';                // 3.90.3, 3.93.1
+				$KnownEncoderValues[0xC0][58][2][2][3][2][19500] = '--preset cd';                // 3.93
+				$KnownEncoderValues[0xC0][57][2][1][3][4][19500] = '--preset cd';                // 3.94,   3.95
+				$KnownEncoderValues[0xA0][78][1][1][3][2][18000] = '--preset hifi';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
+				$KnownEncoderValues[0xA0][58][2][2][3][2][18000] = '--preset hifi';              // 3.90.3, 3.93,   3.93.1
+				$KnownEncoderValues[0xA0][57][2][1][3][4][18000] = '--preset hifi';              // 3.94,   3.95
+				$KnownEncoderValues[0x80][67][1][1][3][2][18000] = '--preset tape';              // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
+				$KnownEncoderValues[0x80][67][1][1][3][2][15000] = '--preset radio';             // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
+				$KnownEncoderValues[0x70][67][1][1][3][2][15000] = '--preset fm';                // 3.90,   3.90.1, 3.90.2,   3.91, 3.92
+				$KnownEncoderValues[0x70][58][2][2][3][2][16000] = '--preset tape/radio/fm';     // 3.90.3, 3.93,   3.93.1
+				$KnownEncoderValues[0x70][57][2][1][3][4][16000] = '--preset tape/radio/fm';     // 3.94,   3.95
+				$KnownEncoderValues[0x38][58][2][2][0][2][10000] = '--preset voice';             // 3.90.3, 3.93,   3.93.1
+				$KnownEncoderValues[0x38][57][2][1][0][4][15000] = '--preset voice';             // 3.94,   3.95
+				$KnownEncoderValues[0x38][57][2][1][0][4][16000] = '--preset voice';             // 3.94a14
+				$KnownEncoderValues[0x28][65][1][1][0][2][7500]  = '--preset mw-us';             // 3.90,   3.90.1, 3.92
+				$KnownEncoderValues[0x28][65][1][1][0][2][7600]  = '--preset mw-us';             // 3.90.2, 3.91
+				$KnownEncoderValues[0x28][58][2][2][0][2][7000]  = '--preset mw-us';             // 3.90.3, 3.93,   3.93.1
+				$KnownEncoderValues[0x28][57][2][1][0][4][10500] = '--preset mw-us';             // 3.94,   3.95
+				$KnownEncoderValues[0x28][57][2][1][0][4][11200] = '--preset mw-us';             // 3.94a14
+				$KnownEncoderValues[0x28][57][2][1][0][4][8800]  = '--preset mw-us';             // 3.94a15
+				$KnownEncoderValues[0x18][58][2][2][0][2][4000]  = '--preset phon+/lw/mw-eu/sw'; // 3.90.3, 3.93.1
+				$KnownEncoderValues[0x18][58][2][2][0][2][3900]  = '--preset phon+/lw/mw-eu/sw'; // 3.93
+				$KnownEncoderValues[0x18][57][2][1][0][4][5900]  = '--preset phon+/lw/mw-eu/sw'; // 3.94,   3.95
+				$KnownEncoderValues[0x18][57][2][1][0][4][6200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a14
+				$KnownEncoderValues[0x18][57][2][1][0][4][3200]  = '--preset phon+/lw/mw-eu/sw'; // 3.94a15
+				$KnownEncoderValues[0x10][58][2][2][0][2][3800]  = '--preset phone';             // 3.90.3, 3.93.1
+				$KnownEncoderValues[0x10][58][2][2][0][2][3700]  = '--preset phone';             // 3.93
+				$KnownEncoderValues[0x10][57][2][1][0][4][5600]  = '--preset phone';             // 3.94,   3.95
+			}
+
+			if (isset($KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
+
+				$encoder_options = $KnownEncoderValues[$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate']][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
+
+			} elseif (isset($KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']])) {
+
+				$encoder_options = $KnownEncoderValues['**'][$thisfile_mpeg_audio_lame['vbr_quality']][$thisfile_mpeg_audio_lame['raw']['vbr_method']][$thisfile_mpeg_audio_lame['raw']['noise_shaping']][$thisfile_mpeg_audio_lame['raw']['stereo_mode']][$thisfile_mpeg_audio_lame['ath_type']][$thisfile_mpeg_audio_lame['lowpass_frequency']];
+
+			} elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') {
+
+				// http://gabriel.mp3-tech.org/mp3infotag.html
+				// int    Quality = (100 - 10 * gfp->VBR_q - gfp->quality)h
+
+
+				$LAME_V_value = 10 - ceil($thisfile_mpeg_audio_lame['vbr_quality'] / 10);
+				$LAME_q_value = 100 - $thisfile_mpeg_audio_lame['vbr_quality'] - ($LAME_V_value * 10);
+				$encoder_options = '-V'.$LAME_V_value.' -q'.$LAME_q_value;
+
+			} elseif ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
+
+				$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
+
+			} else {
+
+				$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
+
+			}
+
+		} elseif (!empty($thisfile_mpeg_audio_lame['bitrate_abr'])) {
+
+			$encoder_options = 'ABR'.$thisfile_mpeg_audio_lame['bitrate_abr'];
+
+		} elseif (!empty($ThisFileInfo['audio']['bitrate'])) {
+
+			if ($ThisFileInfo['audio']['bitrate_mode'] == 'cbr') {
+				$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
+			} else {
+				$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
+			}
+
+		}
+		if (!empty($thisfile_mpeg_audio_lame['bitrate_min'])) {
+			$encoder_options .= ' -b'.$thisfile_mpeg_audio_lame['bitrate_min'];
+		}
+
+		if (@$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev'] || @$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']) {
+			$encoder_options .= ' --nogap';
+		}
+
+		if (!empty($thisfile_mpeg_audio_lame['lowpass_frequency'])) {
+			$ExplodedOptions = explode(' ', $encoder_options, 4);
+			if ($ExplodedOptions[0] == '--r3mix') {
+				$ExplodedOptions[1] = 'r3mix';
+			}
+			switch ($ExplodedOptions[0]) {
+				case '--preset':
+				case '--alt-preset':
+				case '--r3mix':
+					if ($ExplodedOptions[1] == 'fast') {
+						$ExplodedOptions[1] .= ' '.$ExplodedOptions[2];
+					}
+					switch ($ExplodedOptions[1]) {
+						case 'portable':
+						case 'medium':
+						case 'standard':
+						case 'extreme':
+						case 'insane':
+						case 'fast portable':
+						case 'fast medium':
+						case 'fast standard':
+						case 'fast extreme':
+						case 'fast insane':
+						case 'r3mix':
+							static $ExpectedLowpass = array(
+									'insane|20500'        => 20500,
+									'insane|20600'        => 20600,  // 3.90.2, 3.90.3, 3.91
+									'medium|18000'        => 18000,
+									'fast medium|18000'   => 18000,
+									'extreme|19500'       => 19500,  // 3.90,   3.90.1, 3.92, 3.95
+									'extreme|19600'       => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
+									'fast extreme|19500'  => 19500,  // 3.90,   3.90.1, 3.92, 3.95
+									'fast extreme|19600'  => 19600,  // 3.90.2, 3.90.3, 3.91, 3.93.1
+									'standard|19000'      => 19000,
+									'fast standard|19000' => 19000,
+									'r3mix|19500'         => 19500,  // 3.90,   3.90.1, 3.92
+									'r3mix|19600'         => 19600,  // 3.90.2, 3.90.3, 3.91
+									'r3mix|18000'         => 18000,  // 3.94,   3.95
+								);
+							if (!isset($ExpectedLowpass[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio_lame['lowpass_frequency']]) && ($thisfile_mpeg_audio_lame['lowpass_frequency'] < 22050) && (round($thisfile_mpeg_audio_lame['lowpass_frequency'] / 1000) < round($thisfile_mpeg_audio['sample_rate'] / 2000))) {
+								$encoder_options .= ' --lowpass '.$thisfile_mpeg_audio_lame['lowpass_frequency'];
+							}
+							break;
+
+						default:
+							break;
+					}
+					break;
+			}
+		}
+
+		if (isset($thisfile_mpeg_audio_lame['raw']['source_sample_freq'])) {
+			if (($thisfile_mpeg_audio['sample_rate'] == 44100) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 1)) {
+				$encoder_options .= ' --resample 44100';
+			} elseif (($thisfile_mpeg_audio['sample_rate'] == 48000) && ($thisfile_mpeg_audio_lame['raw']['source_sample_freq'] != 2)) {
+				$encoder_options .= ' --resample 48000';
+			} elseif ($thisfile_mpeg_audio['sample_rate'] < 44100) {
+				switch ($thisfile_mpeg_audio_lame['raw']['source_sample_freq']) {
+					case 0: // <= 32000
+						// may or may not be same as source frequency - ignore
+						break;
+					case 1: // 44100
+					case 2: // 48000
+					case 3: // 48000+
+						$ExplodedOptions = explode(' ', $encoder_options, 4);
+						switch ($ExplodedOptions[0]) {
+							case '--preset':
+							case '--alt-preset':
+								switch ($ExplodedOptions[1]) {
+									case 'fast':
+									case 'portable':
+									case 'medium':
+									case 'standard':
+									case 'extreme':
+									case 'insane':
+										$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
+										break;
+
+									default:
+										static $ExpectedResampledRate = array(
+												'phon+/lw/mw-eu/sw|16000' => 16000,
+												'mw-us|24000'             => 24000, // 3.95
+												'mw-us|32000'             => 32000, // 3.93
+												'mw-us|16000'             => 16000, // 3.92
+												'phone|16000'             => 16000,
+												'phone|11025'             => 11025, // 3.94a15
+												'radio|32000'             => 32000, // 3.94a15
+												'fm/radio|32000'          => 32000, // 3.92
+												'fm|32000'                => 32000, // 3.90
+												'voice|32000'             => 32000);
+										if (!isset($ExpectedResampledRate[$ExplodedOptions[1].'|'.$thisfile_mpeg_audio['sample_rate']])) {
+											$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
+										}
+										break;
+								}
+								break;
+
+							case '--r3mix':
+							default:
+								$encoder_options .= ' --resample '.$thisfile_mpeg_audio['sample_rate'];
+								break;
+						}
+						break;
+				}
+			}
+		}
+		if (empty($encoder_options) && !empty($ThisFileInfo['audio']['bitrate']) && !empty($ThisFileInfo['audio']['bitrate_mode'])) {
+			//$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']).ceil($ThisFileInfo['audio']['bitrate'] / 1000);
+			$encoder_options = strtoupper($ThisFileInfo['audio']['bitrate_mode']);
+		}
+
+		return $encoder_options;
+	}
+
+
+	function decodeMPEGaudioHeader($fd, $offset, &$ThisFileInfo, $recursivesearch=true, $ScanAsCBR=false, $FastMPEGheaderScan=false) {
+
+		static $MPEGaudioVersionLookup;
+		static $MPEGaudioLayerLookup;
+		static $MPEGaudioBitrateLookup;
+		static $MPEGaudioFrequencyLookup;
+		static $MPEGaudioChannelModeLookup;
+		static $MPEGaudioModeExtensionLookup;
+		static $MPEGaudioEmphasisLookup;
+		if (empty($MPEGaudioVersionLookup)) {
+			$MPEGaudioVersionLookup       = getid3_mp3::MPEGaudioVersionArray();
+			$MPEGaudioLayerLookup         = getid3_mp3::MPEGaudioLayerArray();
+			$MPEGaudioBitrateLookup       = getid3_mp3::MPEGaudioBitrateArray();
+			$MPEGaudioFrequencyLookup     = getid3_mp3::MPEGaudioFrequencyArray();
+			$MPEGaudioChannelModeLookup   = getid3_mp3::MPEGaudioChannelModeArray();
+			$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
+			$MPEGaudioEmphasisLookup      = getid3_mp3::MPEGaudioEmphasisArray();
+		}
+
+		if ($offset >= $ThisFileInfo['avdataend']) {
+			$ThisFileInfo['error'][] = 'end of file encounter looking for MPEG synch';
+			return false;
+		}
+		fseek($fd, $offset, SEEK_SET);
+		//$headerstring = fread($fd, 1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame
+		$headerstring = fread($fd, 226); // LAME header at offset 36 + 190 bytes of Xing/LAME data
+
+		// MP3 audio frame structure:
+		// $aa $aa $aa $aa [$bb $bb] $cc...
+		// where $aa..$aa is the four-byte mpeg-audio header (below)
+		// $bb $bb is the optional 2-byte CRC
+		// and $cc... is the audio data
+
+		$head4 = substr($headerstring, 0, 4);
+
+		static $MPEGaudioHeaderDecodeCache = array();
+		if (isset($MPEGaudioHeaderDecodeCache[$head4])) {
+			$MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4];
+		} else {
+			$MPEGheaderRawArray = getid3_mp3::MPEGaudioHeaderDecode($head4);
+			$MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray;
+		}
+
+		static $MPEGaudioHeaderValidCache = array();
+
+		// Not in cache
+		if (!isset($MPEGaudioHeaderValidCache[$head4])) {
+			//$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true);  // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1)
+			$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false);
+		}
+
+		// shortcut
+		if (!isset($ThisFileInfo['mpeg']['audio'])) {
+			$ThisFileInfo['mpeg']['audio'] = array();
+		}
+		$thisfile_mpeg_audio = &$ThisFileInfo['mpeg']['audio'];
+
+
+		if ($MPEGaudioHeaderValidCache[$head4]) {
+			$thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray;
+		} else {
+			$ThisFileInfo['error'][] = 'Invalid MPEG audio header at offset '.$offset;
+			return false;
+		}
+
+		if (!$FastMPEGheaderScan) {
+
+			$thisfile_mpeg_audio['version']       = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']];
+			$thisfile_mpeg_audio['layer']         = $MPEGaudioLayerLookup[$thisfile_mpeg_audio['raw']['layer']];
+
+			$thisfile_mpeg_audio['channelmode']   = $MPEGaudioChannelModeLookup[$thisfile_mpeg_audio['raw']['channelmode']];
+			$thisfile_mpeg_audio['channels']      = (($thisfile_mpeg_audio['channelmode'] == 'mono') ? 1 : 2);
+			$thisfile_mpeg_audio['sample_rate']   = $MPEGaudioFrequencyLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['raw']['sample_rate']];
+			$thisfile_mpeg_audio['protection']    = !$thisfile_mpeg_audio['raw']['protection'];
+			$thisfile_mpeg_audio['private']       = (bool) $thisfile_mpeg_audio['raw']['private'];
+			$thisfile_mpeg_audio['modeextension'] = $MPEGaudioModeExtensionLookup[$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['modeextension']];
+			$thisfile_mpeg_audio['copyright']     = (bool) $thisfile_mpeg_audio['raw']['copyright'];
+			$thisfile_mpeg_audio['original']      = (bool) $thisfile_mpeg_audio['raw']['original'];
+			$thisfile_mpeg_audio['emphasis']      = $MPEGaudioEmphasisLookup[$thisfile_mpeg_audio['raw']['emphasis']];
+
+			$ThisFileInfo['audio']['channels']    = $thisfile_mpeg_audio['channels'];
+			$ThisFileInfo['audio']['sample_rate'] = $thisfile_mpeg_audio['sample_rate'];
+
+			if ($thisfile_mpeg_audio['protection']) {
+				$thisfile_mpeg_audio['crc'] = getid3_lib::BigEndian2Int(substr($headerstring, 4, 2));
+			}
+
+		}
+
+		if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) {
+			// http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0
+			$ThisFileInfo['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1';
+			$thisfile_mpeg_audio['raw']['bitrate'] = 0;
+		}
+		$thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding'];
+		$thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']];
+
+		if (($thisfile_mpeg_audio['bitrate'] == 'free') && ($offset == $ThisFileInfo['avdataoffset'])) {
+			// only skip multiple frame check if free-format bitstream found at beginning of file
+			// otherwise is quite possibly simply corrupted data
+			$recursivesearch = false;
+		}
+
+		// For Layer 2 there are some combinations of bitrate and mode which are not allowed.
+		if (!$FastMPEGheaderScan && ($thisfile_mpeg_audio['layer'] == '2')) {
+
+			$ThisFileInfo['audio']['dataformat'] = 'mp2';
+			switch ($thisfile_mpeg_audio['channelmode']) {
+
+				case 'mono':
+					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) {
+						// these are ok
+					} else {
+						$ThisFileInfo['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
+						return false;
+					}
+					break;
+
+				case 'stereo':
+				case 'joint stereo':
+				case 'dual channel':
+					if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) {
+						// these are ok
+					} else {
+						$ThisFileInfo['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.';
+						return false;
+					}
+					break;
+
+			}
+
+		}
+
+
+		if ($ThisFileInfo['audio']['sample_rate'] > 0) {
+			$thisfile_mpeg_audio['framelength'] = getid3_mp3::MPEGaudioFrameLength($thisfile_mpeg_audio['bitrate'], $thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['layer'], (int) $thisfile_mpeg_audio['padding'], $ThisFileInfo['audio']['sample_rate']);
+		}
+
+		$nextframetestoffset = $offset + 1;
+		if ($thisfile_mpeg_audio['bitrate'] != 'free') {
+
+			$ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
+
+			if (isset($thisfile_mpeg_audio['framelength'])) {
+				$nextframetestoffset = $offset + $thisfile_mpeg_audio['framelength'];
+			} else {
+				$ThisFileInfo['error'][] = 'Frame at offset('.$offset.') is has an invalid frame length.';
+				return false;
+			}
+
+		}
+
+		$ExpectedNumberOfAudioBytes = 0;
+
+		////////////////////////////////////////////////////////////////////////////////////
+		// Variable-bitrate headers
+
+		if (substr($headerstring, 4 + 32, 4) == 'VBRI') {
+			// Fraunhofer VBR header is hardcoded 'VBRI' at offset 0x24 (36)
+			// specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
+
+			$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
+			$thisfile_mpeg_audio['VBR_method']   = 'Fraunhofer';
+			$ThisFileInfo['audio']['codec']                = 'Fraunhofer';
+
+			$SideInfoData = substr($headerstring, 4 + 2, 32);
+
+			$FraunhoferVBROffset = 36;
+
+			$thisfile_mpeg_audio['VBR_encoder_version']     = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  4, 2)); // VbriVersion
+			$thisfile_mpeg_audio['VBR_encoder_delay']       = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  6, 2)); // VbriDelay
+			$thisfile_mpeg_audio['VBR_quality']             = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset +  8, 2)); // VbriQuality
+			$thisfile_mpeg_audio['VBR_bytes']               = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 10, 4)); // VbriStreamBytes
+			$thisfile_mpeg_audio['VBR_frames']              = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 14, 4)); // VbriStreamFrames
+			$thisfile_mpeg_audio['VBR_seek_offsets']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 18, 2)); // VbriTableSize
+			$thisfile_mpeg_audio['VBR_seek_scale']          = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 20, 2)); // VbriTableScale
+			$thisfile_mpeg_audio['VBR_entry_bytes']         = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 22, 2)); // VbriEntryBytes
+			$thisfile_mpeg_audio['VBR_entry_frames']        = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset + 24, 2)); // VbriEntryFrames
+
+			$ExpectedNumberOfAudioBytes = $thisfile_mpeg_audio['VBR_bytes'];
+
+			$previousbyteoffset = $offset;
+			for ($i = 0; $i < $thisfile_mpeg_audio['VBR_seek_offsets']; $i++) {
+				$Fraunhofer_OffsetN = getid3_lib::BigEndian2Int(substr($headerstring, $FraunhoferVBROffset, $thisfile_mpeg_audio['VBR_entry_bytes']));
+				$FraunhoferVBROffset += $thisfile_mpeg_audio['VBR_entry_bytes'];
+				$thisfile_mpeg_audio['VBR_offsets_relative'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']);
+				$thisfile_mpeg_audio['VBR_offsets_absolute'][$i] = ($Fraunhofer_OffsetN * $thisfile_mpeg_audio['VBR_seek_scale']) + $previousbyteoffset;
+				$previousbyteoffset += $Fraunhofer_OffsetN;
+			}
+
+
+		} else {
+
+			// Xing VBR header is hardcoded 'Xing' at a offset 0x0D (13), 0x15 (21) or 0x24 (36)
+			// depending on MPEG layer and number of channels
+
+			$VBRidOffset = getid3_mp3::XingVBRidOffset($thisfile_mpeg_audio['version'], $thisfile_mpeg_audio['channelmode']);
+			$SideInfoData = substr($headerstring, 4 + 2, $VBRidOffset - 4);
+
+			if ((substr($headerstring, $VBRidOffset, strlen('Xing')) == 'Xing') || (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Info')) {
+				// 'Xing' is traditional Xing VBR frame
+				// 'Info' is LAME-encoded CBR (This was done to avoid CBR files to be recognized as traditional Xing VBR files by some decoders.)
+				// 'Info' *can* legally be used to specify a VBR file as well, however.
+
+				// http://www.multiweb.cz/twoinches/MP3inside.htm
+				//00..03 = "Xing" or "Info"
+				//04..07 = Flags:
+				//  0x01  Frames Flag     set if value for number of frames in file is stored
+				//  0x02  Bytes Flag      set if value for filesize in bytes is stored
+				//  0x04  TOC Flag        set if values for TOC are stored
+				//  0x08  VBR Scale Flag  set if values for VBR scale is stored
+				//08..11  Frames: Number of frames in file (including the first Xing/Info one)
+				//12..15  Bytes:  File length in Bytes
+				//16..115  TOC (Table of Contents):
+				//  Contains of 100 indexes (one Byte length) for easier lookup in file. Approximately solves problem with moving inside file.
+				//  Each Byte has a value according this formula:
+				//  (TOC[i] / 256) * fileLenInBytes
+				//  So if song lasts eg. 240 sec. and you want to jump to 60. sec. (and file is 5 000 000 Bytes length) you can use:
+				//  TOC[(60/240)*100] = TOC[25]
+				//  and corresponding Byte in file is then approximately at:
+				//  (TOC[25]/256) * 5000000
+				//116..119  VBR Scale
+
+
+				// should be safe to leave this at 'vbr' and let it be overriden to 'cbr' if a CBR preset/mode is used by LAME
+//				if (substr($headerstring, $VBRidOffset, strlen('Info')) == 'Xing') {
+					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
+					$thisfile_mpeg_audio['VBR_method']   = 'Xing';
+//				} else {
+//					$ScanAsCBR = true;
+//					$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
+//				}
+
+				$thisfile_mpeg_audio['xing_flags_raw'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 4, 4));
+
+				$thisfile_mpeg_audio['xing_flags']['frames']    = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000001);
+				$thisfile_mpeg_audio['xing_flags']['bytes']     = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000002);
+				$thisfile_mpeg_audio['xing_flags']['toc']       = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000004);
+				$thisfile_mpeg_audio['xing_flags']['vbr_scale'] = (bool) ($thisfile_mpeg_audio['xing_flags_raw'] & 0x00000008);
+
+				if ($thisfile_mpeg_audio['xing_flags']['frames']) {
+					$thisfile_mpeg_audio['VBR_frames'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset +  8, 4));
+					//$thisfile_mpeg_audio['VBR_frames']--; // don't count header Xing/Info frame
+				}
+				if ($thisfile_mpeg_audio['xing_flags']['bytes']) {
+					$thisfile_mpeg_audio['VBR_bytes']  = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4));
+				}
+
+				//if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
+				if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) {
+
+					$framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames'];
+
+					if ($thisfile_mpeg_audio['layer'] == '1') {
+						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
+						//$ThisFileInfo['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
+						$ThisFileInfo['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 12;
+					} else {
+						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
+						//$ThisFileInfo['audio']['bitrate'] = (($framelengthfloat - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
+						$ThisFileInfo['audio']['bitrate'] = $framelengthfloat * $thisfile_mpeg_audio['sample_rate'] * (2 / $ThisFileInfo['audio']['channels']) / 144;
+					}
+					$thisfile_mpeg_audio['framelength'] = floor($framelengthfloat);
+				}
+
+				if ($thisfile_mpeg_audio['xing_flags']['toc']) {
+					$LAMEtocData = substr($headerstring, $VBRidOffset + 16, 100);
+					for ($i = 0; $i < 100; $i++) {
+						$thisfile_mpeg_audio['toc'][$i] = ord($LAMEtocData{$i});
+					}
+				}
+				if ($thisfile_mpeg_audio['xing_flags']['vbr_scale']) {
+					$thisfile_mpeg_audio['VBR_scale'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 116, 4));
+				}
+
+
+				// http://gabriel.mp3-tech.org/mp3infotag.html
+				if (substr($headerstring, $VBRidOffset + 120, 4) == 'LAME') {
+
+					// shortcut
+					$thisfile_mpeg_audio['LAME'] = array();
+					$thisfile_mpeg_audio_lame    = &$thisfile_mpeg_audio['LAME'];
+
+
+					$thisfile_mpeg_audio_lame['long_version']  = substr($headerstring, $VBRidOffset + 120, 20);
+					$thisfile_mpeg_audio_lame['short_version'] = substr($thisfile_mpeg_audio_lame['long_version'], 0, 9);
+
+					if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.90') {
+
+						// extra 11 chars are not part of version string when LAMEtag present
+						unset($thisfile_mpeg_audio_lame['long_version']);
+
+						// It the LAME tag was only introduced in LAME v3.90
+						// http://www.hydrogenaudio.org/?act=ST&f=15&t=9933
+
+						// Offsets of various bytes in http://gabriel.mp3-tech.org/mp3infotag.html
+						// are assuming a 'Xing' identifier offset of 0x24, which is the case for
+						// MPEG-1 non-mono, but not for other combinations
+						$LAMEtagOffsetContant = $VBRidOffset - 0x24;
+
+						// shortcuts
+						$thisfile_mpeg_audio_lame['RGAD']    = array('track'=>array(), 'album'=>array());
+						$thisfile_mpeg_audio_lame_RGAD       = &$thisfile_mpeg_audio_lame['RGAD'];
+						$thisfile_mpeg_audio_lame_RGAD_track = &$thisfile_mpeg_audio_lame_RGAD['track'];
+						$thisfile_mpeg_audio_lame_RGAD_album = &$thisfile_mpeg_audio_lame_RGAD['album'];
+						$thisfile_mpeg_audio_lame['raw'] = array();
+						$thisfile_mpeg_audio_lame_raw    = &$thisfile_mpeg_audio_lame['raw'];
+
+						// byte $9B  VBR Quality
+						// This field is there to indicate a quality level, although the scale was not precised in the original Xing specifications.
+						// Actually overwrites original Xing bytes
+						unset($thisfile_mpeg_audio['VBR_scale']);
+						$thisfile_mpeg_audio_lame['vbr_quality'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0x9B, 1));
+
+						// bytes $9C-$A4  Encoder short VersionString
+						$thisfile_mpeg_audio_lame['short_version'] = substr($headerstring, $LAMEtagOffsetContant + 0x9C, 9);
+
+						// byte $A5  Info Tag revision + VBR method
+						$LAMEtagRevisionVBRmethod = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA5, 1));
+
+						$thisfile_mpeg_audio_lame['tag_revision']   = ($LAMEtagRevisionVBRmethod & 0xF0) >> 4;
+						$thisfile_mpeg_audio_lame_raw['vbr_method'] =  $LAMEtagRevisionVBRmethod & 0x0F;
+						$thisfile_mpeg_audio_lame['vbr_method']     = getid3_mp3::LAMEvbrMethodLookup($thisfile_mpeg_audio_lame_raw['vbr_method']);
+						$thisfile_mpeg_audio['bitrate_mode']        = substr($thisfile_mpeg_audio_lame['vbr_method'], 0, 3); // usually either 'cbr' or 'vbr', but truncates 'vbr-old / vbr-rh' to 'vbr'
+
+						// byte $A6  Lowpass filter value
+						$thisfile_mpeg_audio_lame['lowpass_frequency'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA6, 1)) * 100;
+
+						// bytes $A7-$AE  Replay Gain
+						// http://privatewww.essex.ac.uk/~djmrob/replaygain/rg_data_format.html
+						// bytes $A7-$AA : 32 bit floating point "Peak signal amplitude"
+						if ($thisfile_mpeg_audio_lame['short_version'] >= 'LAME3.94b') {
+							// LAME 3.94a16 and later - 9.23 fixed point
+							// ie 0x0059E2EE / (2^23) = 5890798 / 8388608 = 0.7022378444671630859375
+							$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = (float) ((getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4))) / 8388608);
+						} else {
+							// LAME 3.94a15 and earlier - 32-bit floating point
+							// Actually 3.94a16 will fall in here too and be WRONG, but is hard to detect 3.94a16 vs 3.94a15
+							$thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] = getid3_lib::LittleEndian2Float(substr($headerstring, $LAMEtagOffsetContant + 0xA7, 4));
+						}
+						if ($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'] == 0) {
+							unset($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
+						} else {
+							$thisfile_mpeg_audio_lame_RGAD['peak_db'] = getid3_lib::RGADamplitude2dB($thisfile_mpeg_audio_lame_RGAD['peak_amplitude']);
+						}
+
+						$thisfile_mpeg_audio_lame_raw['RGAD_track']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAB, 2));
+						$thisfile_mpeg_audio_lame_raw['RGAD_album']      =   getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAD, 2));
+
+
+						if ($thisfile_mpeg_audio_lame_raw['RGAD_track'] != 0) {
+
+							$thisfile_mpeg_audio_lame_RGAD_track['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0xE000) >> 13;
+							$thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x1C00) >> 10;
+							$thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x0200) >> 9;
+							$thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'] =  $thisfile_mpeg_audio_lame_raw['RGAD_track'] & 0x01FF;
+							$thisfile_mpeg_audio_lame_RGAD_track['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['name']);
+							$thisfile_mpeg_audio_lame_RGAD_track['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['originator']);
+							$thisfile_mpeg_audio_lame_RGAD_track['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_track['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_track['raw']['sign_bit']);
+
+							if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
+								$ThisFileInfo['replay_gain']['track']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
+							}
+							$ThisFileInfo['replay_gain']['track']['originator'] = $thisfile_mpeg_audio_lame_RGAD_track['originator'];
+							$ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_track['gain_db'];
+						} else {
+							unset($thisfile_mpeg_audio_lame_RGAD['track']);
+						}
+						if ($thisfile_mpeg_audio_lame_raw['RGAD_album'] != 0) {
+
+							$thisfile_mpeg_audio_lame_RGAD_album['raw']['name']        = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0xE000) >> 13;
+							$thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']  = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x1C00) >> 10;
+							$thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']    = ($thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x0200) >> 9;
+							$thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'] = $thisfile_mpeg_audio_lame_raw['RGAD_album'] & 0x01FF;
+							$thisfile_mpeg_audio_lame_RGAD_album['name']       = getid3_lib::RGADnameLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['name']);
+							$thisfile_mpeg_audio_lame_RGAD_album['originator'] = getid3_lib::RGADoriginatorLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['originator']);
+							$thisfile_mpeg_audio_lame_RGAD_album['gain_db']    = getid3_lib::RGADadjustmentLookup($thisfile_mpeg_audio_lame_RGAD_album['raw']['gain_adjust'], $thisfile_mpeg_audio_lame_RGAD_album['raw']['sign_bit']);
+
+							if (!empty($thisfile_mpeg_audio_lame_RGAD['peak_amplitude'])) {
+								$ThisFileInfo['replay_gain']['album']['peak']   = $thisfile_mpeg_audio_lame_RGAD['peak_amplitude'];
+							}
+							$ThisFileInfo['replay_gain']['album']['originator'] = $thisfile_mpeg_audio_lame_RGAD_album['originator'];
+							$ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpeg_audio_lame_RGAD_album['gain_db'];
+						} else {
+							unset($thisfile_mpeg_audio_lame_RGAD['album']);
+						}
+						if (empty($thisfile_mpeg_audio_lame_RGAD)) {
+							unset($thisfile_mpeg_audio_lame['RGAD']);
+						}
+
+
+						// byte $AF  Encoding flags + ATH Type
+						$EncodingFlagsATHtype = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xAF, 1));
+						$thisfile_mpeg_audio_lame['encoding_flags']['nspsytune']   = (bool) ($EncodingFlagsATHtype & 0x10);
+						$thisfile_mpeg_audio_lame['encoding_flags']['nssafejoint'] = (bool) ($EncodingFlagsATHtype & 0x20);
+						$thisfile_mpeg_audio_lame['encoding_flags']['nogap_next']  = (bool) ($EncodingFlagsATHtype & 0x40);
+						$thisfile_mpeg_audio_lame['encoding_flags']['nogap_prev']  = (bool) ($EncodingFlagsATHtype & 0x80);
+						$thisfile_mpeg_audio_lame['ath_type']                      =         $EncodingFlagsATHtype & 0x0F;
+
+						// byte $B0  if ABR {specified bitrate} else {minimal bitrate}
+						$thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB0, 1));
+						if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 2) { // Average BitRate (ABR)
+							$thisfile_mpeg_audio_lame['bitrate_abr'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
+						} elseif ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) { // Constant BitRate (CBR)
+							// ignore
+						} elseif ($thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'] > 0) { // Variable BitRate (VBR) - minimum bitrate
+							$thisfile_mpeg_audio_lame['bitrate_min'] = $thisfile_mpeg_audio_lame['raw']['abrbitrate_minbitrate'];
+						}
+
+						// bytes $B1-$B3  Encoder delays
+						$EncoderDelays = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB1, 3));
+						$thisfile_mpeg_audio_lame['encoder_delay'] = ($EncoderDelays & 0xFFF000) >> 12;
+						$thisfile_mpeg_audio_lame['end_padding']   =  $EncoderDelays & 0x000FFF;
+
+						// byte $B4  Misc
+						$MiscByte = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB4, 1));
+						$thisfile_mpeg_audio_lame_raw['noise_shaping']       = ($MiscByte & 0x03);
+						$thisfile_mpeg_audio_lame_raw['stereo_mode']         = ($MiscByte & 0x1C) >> 2;
+						$thisfile_mpeg_audio_lame_raw['not_optimal_quality'] = ($MiscByte & 0x20) >> 5;
+						$thisfile_mpeg_audio_lame_raw['source_sample_freq']  = ($MiscByte & 0xC0) >> 6;
+						$thisfile_mpeg_audio_lame['noise_shaping']       = $thisfile_mpeg_audio_lame_raw['noise_shaping'];
+						$thisfile_mpeg_audio_lame['stereo_mode']         = getid3_mp3::LAMEmiscStereoModeLookup($thisfile_mpeg_audio_lame_raw['stereo_mode']);
+						$thisfile_mpeg_audio_lame['not_optimal_quality'] = (bool) $thisfile_mpeg_audio_lame_raw['not_optimal_quality'];
+						$thisfile_mpeg_audio_lame['source_sample_freq']  = getid3_mp3::LAMEmiscSourceSampleFrequencyLookup($thisfile_mpeg_audio_lame_raw['source_sample_freq']);
+
+						// byte $B5  MP3 Gain
+						$thisfile_mpeg_audio_lame_raw['mp3_gain'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB5, 1), false, true);
+						$thisfile_mpeg_audio_lame['mp3_gain_db']     = (getid3_lib::RGADamplitude2dB(2) / 4) * $thisfile_mpeg_audio_lame_raw['mp3_gain'];
+						$thisfile_mpeg_audio_lame['mp3_gain_factor'] = pow(2, ($thisfile_mpeg_audio_lame['mp3_gain_db'] / 6));
+
+						// bytes $B6-$B7  Preset and surround info
+						$PresetSurroundBytes = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB6, 2));
+						// Reserved                                                    = ($PresetSurroundBytes & 0xC000);
+						$thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800);
+						$thisfile_mpeg_audio_lame['surround_info']     = getid3_mp3::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']);
+						$thisfile_mpeg_audio_lame['preset_used_id']    = ($PresetSurroundBytes & 0x07FF);
+						$thisfile_mpeg_audio_lame['preset_used']       = getid3_mp3::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame);
+						if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) {
+							$ThisFileInfo['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org';
+						}
+						if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) {
+							// this may change if 3.90.4 ever comes out
+							$thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3';
+						}
+
+						// bytes $B8-$BB  MusicLength
+						$thisfile_mpeg_audio_lame['audio_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xB8, 4));
+						$ExpectedNumberOfAudioBytes = (($thisfile_mpeg_audio_lame['audio_bytes'] > 0) ? $thisfile_mpeg_audio_lame['audio_bytes'] : $thisfile_mpeg_audio['VBR_bytes']);
+
+						// bytes $BC-$BD  MusicCRC
+						$thisfile_mpeg_audio_lame['music_crc']    = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBC, 2));
+
+						// bytes $BE-$BF  CRC-16 of Info Tag
+						$thisfile_mpeg_audio_lame['lame_tag_crc'] = getid3_lib::BigEndian2Int(substr($headerstring, $LAMEtagOffsetContant + 0xBE, 2));
+
+
+						// LAME CBR
+						if ($thisfile_mpeg_audio_lame_raw['vbr_method'] == 1) {
+
+							$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
+							$thisfile_mpeg_audio['bitrate'] = getid3_mp3::ClosestStandardMP3Bitrate($thisfile_mpeg_audio['bitrate']);
+							$ThisFileInfo['audio']['bitrate'] = $thisfile_mpeg_audio['bitrate'];
+							//if (empty($thisfile_mpeg_audio['bitrate']) || (!empty($thisfile_mpeg_audio_lame['bitrate_min']) && ($thisfile_mpeg_audio_lame['bitrate_min'] != 255))) {
+							//	$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio_lame['bitrate_min'];
+							//}
+
+						}
+
+					}
+				}
+
+			} else {
+
+				// not Fraunhofer or Xing VBR methods, most likely CBR (but could be VBR with no header)
+				$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
+				if ($recursivesearch) {
+					$thisfile_mpeg_audio['bitrate_mode'] = 'vbr';
+					if (getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, true)) {
+						$recursivesearch = false;
+						$thisfile_mpeg_audio['bitrate_mode'] = 'cbr';
+					}
+					if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') {
+						$ThisFileInfo['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.';
+					}
+				}
+
+			}
+
+		}
+
+		if (($ExpectedNumberOfAudioBytes > 0) && ($ExpectedNumberOfAudioBytes != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']))) {
+			if ($ExpectedNumberOfAudioBytes > ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) {
+				if (@$ThisFileInfo['fileformat'] == 'riff') {
+					// ignore, audio data is broken into chunks so will always be data "missing"
+				} elseif (($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])) == 1) {
+					$ThisFileInfo['warning'][] = 'Last byte of data truncated (this is a known bug in Meracl ID3 Tag Writer before v1.3.5)';
+				} else {
+					$ThisFileInfo['warning'][] = 'Probable truncated file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, only found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' (short by '.($ExpectedNumberOfAudioBytes - ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'])).' bytes)';
+				}
+			} else {
+				if ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes) == 1) {
+				//	$prenullbytefileoffset = ftell($fd);
+				//	fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
+				//	$PossibleNullByte = fread($fd, 1);
+				//	fseek($fd, $prenullbytefileoffset, SEEK_SET);
+				//	if ($PossibleNullByte === "\x00") {
+						$ThisFileInfo['avdataend']--;
+				//		$ThisFileInfo['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored';
+				//	} else {
+				//		$ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
+				//	}
+				} else {
+					$ThisFileInfo['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']).' ('.(($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)';
+				}
+			}
+		}
+
+		if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($ThisFileInfo['audio']['bitrate'])) {
+			if (($offset == $ThisFileInfo['avdataoffset']) && empty($thisfile_mpeg_audio['VBR_frames'])) {
+				$framebytelength = getid3_mp3::FreeFormatFrameLength($fd, $offset, $ThisFileInfo, true);
+				if ($framebytelength > 0) {
+					$thisfile_mpeg_audio['framelength'] = $framebytelength;
+					if ($thisfile_mpeg_audio['layer'] == '1') {
+						// BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12
+						$ThisFileInfo['audio']['bitrate'] = ((($framebytelength / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12;
+					} else {
+						// Bitrate = ((FrameLengthInBytes - Padding) * SampleRate) / 144
+						$ThisFileInfo['audio']['bitrate'] = (($framebytelength - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 144;
+					}
+				} else {
+					$ThisFileInfo['error'][] = 'Error calculating frame length of free-format MP3 without Xing/LAME header';
+				}
+			}
+		}
+
+		if (@$thisfile_mpeg_audio['VBR_frames']) {
+			switch ($thisfile_mpeg_audio['bitrate_mode']) {
+				case 'vbr':
+				case 'abr':
+					if (($thisfile_mpeg_audio['version'] == '1') && ($thisfile_mpeg_audio['layer'] == 1)) {
+						$thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 384);
+					} elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) {
+						$thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 576);
+					} else {
+						$thisfile_mpeg_audio['VBR_bitrate'] = ((@$thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($ThisFileInfo['audio']['sample_rate'] / 1152);
+					}
+					if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) {
+						$ThisFileInfo['audio']['bitrate']         = $thisfile_mpeg_audio['VBR_bitrate'];
+						$thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion
+					}
+					break;
+			}
+		}
+
+		// End variable-bitrate headers
+		////////////////////////////////////////////////////////////////////////////////////
+
+		if ($recursivesearch) {
+
+			if (!getid3_mp3::RecursiveFrameScanning($fd, $ThisFileInfo, $offset, $nextframetestoffset, $ScanAsCBR)) {
+				return false;
+			}
+
+		}
+
+
+		//if (false) {
+		//    // experimental side info parsing section - not returning anything useful yet
+		//
+		//    $SideInfoBitstream = getid3_lib::BigEndian2Bin($SideInfoData);
+		//    $SideInfoOffset = 0;
+		//
+		//    if ($thisfile_mpeg_audio['version'] == '1') {
+		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
+		//            // MPEG-1 (mono)
+		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+		//            $SideInfoOffset += 9;
+		//            $SideInfoOffset += 5;
+		//        } else {
+		//            // MPEG-1 (stereo, joint-stereo, dual-channel)
+		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+		//            $SideInfoOffset += 9;
+		//            $SideInfoOffset += 3;
+		//        }
+		//    } else { // 2 or 2.5
+		//        if ($thisfile_mpeg_audio['channelmode'] == 'mono') {
+		//            // MPEG-2, MPEG-2.5 (mono)
+		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
+		//            $SideInfoOffset += 8;
+		//            $SideInfoOffset += 1;
+		//        } else {
+		//            // MPEG-2, MPEG-2.5 (stereo, joint-stereo, dual-channel)
+		//            $thisfile_mpeg_audio['side_info']['main_data_begin'] = substr($SideInfoBitstream, $SideInfoOffset, 8);
+		//            $SideInfoOffset += 8;
+		//            $SideInfoOffset += 2;
+		//        }
+		//    }
+		//
+		//    if ($thisfile_mpeg_audio['version'] == '1') {
+		//        for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
+		//            for ($scfsi_band = 0; $scfsi_band < 4; $scfsi_band++) {
+		//                $thisfile_mpeg_audio['scfsi'][$channel][$scfsi_band] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+		//                $SideInfoOffset += 2;
+		//            }
+		//        }
+		//    }
+		//    for ($granule = 0; $granule < (($thisfile_mpeg_audio['version'] == '1') ? 2 : 1); $granule++) {
+		//        for ($channel = 0; $channel < $ThisFileInfo['audio']['channels']; $channel++) {
+		//            $thisfile_mpeg_audio['part2_3_length'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 12);
+		//            $SideInfoOffset += 12;
+		//            $thisfile_mpeg_audio['big_values'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+		//            $SideInfoOffset += 9;
+		//            $thisfile_mpeg_audio['global_gain'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 8);
+		//            $SideInfoOffset += 8;
+		//            if ($thisfile_mpeg_audio['version'] == '1') {
+		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
+		//                $SideInfoOffset += 4;
+		//            } else {
+		//                $thisfile_mpeg_audio['scalefac_compress'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 9);
+		//                $SideInfoOffset += 9;
+		//            }
+		//            $thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+		//            $SideInfoOffset += 1;
+		//
+		//            if ($thisfile_mpeg_audio['window_switching_flag'][$granule][$channel] == '1') {
+		//
+		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 2);
+		//                $SideInfoOffset += 2;
+		//                $thisfile_mpeg_audio['mixed_block_flag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+		//                $SideInfoOffset += 1;
+		//
+		//                for ($region = 0; $region < 2; $region++) {
+		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
+		//                    $SideInfoOffset += 5;
+		//                }
+		//                $thisfile_mpeg_audio['table_select'][$granule][$channel][2] = 0;
+		//
+		//                for ($window = 0; $window < 3; $window++) {
+		//                    $thisfile_mpeg_audio['subblock_gain'][$granule][$channel][$window] = substr($SideInfoBitstream, $SideInfoOffset, 3);
+		//                    $SideInfoOffset += 3;
+		//                }
+		//
+		//            } else {
+		//
+		//                for ($region = 0; $region < 3; $region++) {
+		//                    $thisfile_mpeg_audio['table_select'][$granule][$channel][$region] = substr($SideInfoBitstream, $SideInfoOffset, 5);
+		//                    $SideInfoOffset += 5;
+		//                }
+		//
+		//                $thisfile_mpeg_audio['region0_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 4);
+		//                $SideInfoOffset += 4;
+		//                $thisfile_mpeg_audio['region1_count'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 3);
+		//                $SideInfoOffset += 3;
+		//                $thisfile_mpeg_audio['block_type'][$granule][$channel] = 0;
+		//            }
+		//
+		//            if ($thisfile_mpeg_audio['version'] == '1') {
+		//                $thisfile_mpeg_audio['preflag'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+		//                $SideInfoOffset += 1;
+		//            }
+		//            $thisfile_mpeg_audio['scalefac_scale'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+		//            $SideInfoOffset += 1;
+		//            $thisfile_mpeg_audio['count1table_select'][$granule][$channel] = substr($SideInfoBitstream, $SideInfoOffset, 1);
+		//            $SideInfoOffset += 1;
+		//        }
+		//    }
+		//}
+
+		return true;
+	}
+
+	function RecursiveFrameScanning(&$fd, &$ThisFileInfo, &$offset, &$nextframetestoffset, $ScanAsCBR) {
+		for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) {
+			// check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch
+			if (($nextframetestoffset + 4) >= $ThisFileInfo['avdataend']) {
+				// end of file
+				return true;
+			}
+
+			$nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']);
+			if (getid3_mp3::decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, false)) {
+				if ($ScanAsCBR) {
+					// force CBR mode, used for trying to pick out invalid audio streams with
+					// valid(?) VBR headers, or VBR streams with no VBR header
+					if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($ThisFileInfo['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $ThisFileInfo['mpeg']['audio']['bitrate'])) {
+						return false;
+					}
+				}
+
+
+				// next frame is OK, get ready to check the one after that
+				if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) {
+					$nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
+				} else {
+					$ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.';
+					return false;
+				}
+
+			} else {
+
+				// next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
+				$ThisFileInfo['error'][] = 'Frame at offset ('.$offset.') is valid, but the next one at ('.$nextframetestoffset.') is not.';
+
+				return false;
+			}
+		}
+		return true;
+	}
+
+	function FreeFormatFrameLength($fd, $offset, &$ThisFileInfo, $deepscan=false) {
+		fseek($fd, $offset, SEEK_SET);
+		$MPEGaudioData = fread($fd, 32768);
+
+		$SyncPattern1 = substr($MPEGaudioData, 0, 4);
+		// may be different pattern due to padding
+		$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) | 0x02).$SyncPattern1{3};
+		if ($SyncPattern2 === $SyncPattern1) {
+			$SyncPattern2 = $SyncPattern1{0}.$SyncPattern1{1}.chr(ord($SyncPattern1{2}) & 0xFD).$SyncPattern1{3};
+		}
+
+		$framelength = false;
+		$framelength1 = strpos($MPEGaudioData, $SyncPattern1, 4);
+		$framelength2 = strpos($MPEGaudioData, $SyncPattern2, 4);
+		if ($framelength1 > 4) {
+			$framelength = $framelength1;
+		}
+		if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
+			$framelength = $framelength2;
+		}
+		if (!$framelength) {
+
+			// LAME 3.88 has a different value for modeextension on the first frame vs the rest
+			$framelength1 = strpos($MPEGaudioData, substr($SyncPattern1, 0, 3), 4);
+			$framelength2 = strpos($MPEGaudioData, substr($SyncPattern2, 0, 3), 4);
+
+			if ($framelength1 > 4) {
+				$framelength = $framelength1;
+			}
+			if (($framelength2 > 4) && ($framelength2 < $framelength1)) {
+				$framelength = $framelength2;
+			}
+			if (!$framelength) {
+				$ThisFileInfo['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset;
+				return false;
+			} else {
+				$ThisFileInfo['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)';
+				$ThisFileInfo['audio']['codec']   = 'LAME';
+				$ThisFileInfo['audio']['encoder'] = 'LAME3.88';
+				$SyncPattern1 = substr($SyncPattern1, 0, 3);
+				$SyncPattern2 = substr($SyncPattern2, 0, 3);
+			}
+		}
+
+		if ($deepscan) {
+
+			$ActualFrameLengthValues = array();
+			$nextoffset = $offset + $framelength;
+			while ($nextoffset < ($ThisFileInfo['avdataend'] - 6)) {
+				fseek($fd, $nextoffset - 1, SEEK_SET);
+				$NextSyncPattern = fread($fd, 6);
+				if ((substr($NextSyncPattern, 1, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 1, strlen($SyncPattern2)) == $SyncPattern2)) {
+					// good - found where expected
+					$ActualFrameLengthValues[] = $framelength;
+				} elseif ((substr($NextSyncPattern, 0, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 0, strlen($SyncPattern2)) == $SyncPattern2)) {
+					// ok - found one byte earlier than expected (last frame wasn't padded, first frame was)
+					$ActualFrameLengthValues[] = ($framelength - 1);
+					$nextoffset--;
+				} elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) {
+					// ok - found one byte later than expected (last frame was padded, first frame wasn't)
+					$ActualFrameLengthValues[] = ($framelength + 1);
+					$nextoffset++;
+				} else {
+					$ThisFileInfo['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset;
+					return false;
+				}
+				$nextoffset += $framelength;
+			}
+			if (count($ActualFrameLengthValues) > 0) {
+				$framelength = intval(round(array_sum($ActualFrameLengthValues) / count($ActualFrameLengthValues)));
+			}
+		}
+		return $framelength;
+	}
+
+	function getOnlyMPEGaudioInfoBruteForce($fd, &$ThisFileInfo) {
+
+		$MPEGaudioHeaderDecodeCache = array();
+		$MPEGaudioHeaderValidCache  = array();
+		$MPEGaudioHeaderLengthCache = array();
+		$MPEGaudioVersionLookup       = getid3_mp3::MPEGaudioVersionArray();
+		$MPEGaudioLayerLookup         = getid3_mp3::MPEGaudioLayerArray();
+		$MPEGaudioBitrateLookup       = getid3_mp3::MPEGaudioBitrateArray();
+		$MPEGaudioFrequencyLookup     = getid3_mp3::MPEGaudioFrequencyArray();
+		$MPEGaudioChannelModeLookup   = getid3_mp3::MPEGaudioChannelModeArray();
+		$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
+		$MPEGaudioEmphasisLookup      = getid3_mp3::MPEGaudioEmphasisArray();
+		$LongMPEGversionLookup   = array();
+		$LongMPEGlayerLookup     = array();
+		$LongMPEGbitrateLookup   = array();
+		$LongMPEGpaddingLookup   = array();
+		$LongMPEGfrequencyLookup = array();
+
+		$Distribution['bitrate']   = array();
+		$Distribution['frequency'] = array();
+		$Distribution['layer']     = array();
+		$Distribution['version']   = array();
+		$Distribution['padding']   = array();
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$max_frames_scan = 5000;
+		$frames_scanned  = 0;
+
+		$previousvalidframe = $ThisFileInfo['avdataoffset'];
+		while (ftell($fd) < $ThisFileInfo['avdataend']) {
+			set_time_limit(30);
+			$head4 = fread($fd, 4);
+			if (strlen($head4) < 4) {
+				break;
+			}
+			if ($head4{0} != "\xFF") {
+				for ($i = 1; $i < 4; $i++) {
+					if ($head4{$i} == "\xFF") {
+						fseek($fd, $i - 4, SEEK_CUR);
+						continue 2;
+					}
+				}
+				continue;
+			}
+			if (!isset($MPEGaudioHeaderDecodeCache[$head4])) {
+				$MPEGaudioHeaderDecodeCache[$head4] = getid3_mp3::MPEGaudioHeaderDecode($head4);
+			}
+			if (!isset($MPEGaudioHeaderValidCache[$head4])) {
+				$MPEGaudioHeaderValidCache[$head4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$head4], false, false);
+			}
+			if ($MPEGaudioHeaderValidCache[$head4]) {
+
+				if (!isset($MPEGaudioHeaderLengthCache[$head4])) {
+					$LongMPEGversionLookup[$head4]   = $MPEGaudioVersionLookup[$MPEGaudioHeaderDecodeCache[$head4]['version']];
+					$LongMPEGlayerLookup[$head4]     = $MPEGaudioLayerLookup[$MPEGaudioHeaderDecodeCache[$head4]['layer']];
+					$LongMPEGbitrateLookup[$head4]   = $MPEGaudioBitrateLookup[$LongMPEGversionLookup[$head4]][$LongMPEGlayerLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['bitrate']];
+					$LongMPEGpaddingLookup[$head4]   = (bool) $MPEGaudioHeaderDecodeCache[$head4]['padding'];
+					$LongMPEGfrequencyLookup[$head4] = $MPEGaudioFrequencyLookup[$LongMPEGversionLookup[$head4]][$MPEGaudioHeaderDecodeCache[$head4]['sample_rate']];
+					$MPEGaudioHeaderLengthCache[$head4] = getid3_mp3::MPEGaudioFrameLength(
+						$LongMPEGbitrateLookup[$head4],
+						$LongMPEGversionLookup[$head4],
+						$LongMPEGlayerLookup[$head4],
+						$LongMPEGpaddingLookup[$head4],
+						$LongMPEGfrequencyLookup[$head4]);
+				}
+				if ($MPEGaudioHeaderLengthCache[$head4] > 4) {
+					$WhereWeWere = ftell($fd);
+					fseek($fd, $MPEGaudioHeaderLengthCache[$head4] - 4, SEEK_CUR);
+					$next4 = fread($fd, 4);
+					if ($next4{0} == "\xFF") {
+						if (!isset($MPEGaudioHeaderDecodeCache[$next4])) {
+							$MPEGaudioHeaderDecodeCache[$next4] = getid3_mp3::MPEGaudioHeaderDecode($next4);
+						}
+						if (!isset($MPEGaudioHeaderValidCache[$next4])) {
+							$MPEGaudioHeaderValidCache[$next4] = getid3_mp3::MPEGaudioHeaderValid($MPEGaudioHeaderDecodeCache[$next4], false, false);
+						}
+						if ($MPEGaudioHeaderValidCache[$next4]) {
+							fseek($fd, -4, SEEK_CUR);
+
+							@$Distribution['bitrate'][$LongMPEGbitrateLookup[$head4]]++;
+							@$Distribution['layer'][$LongMPEGlayerLookup[$head4]]++;
+							@$Distribution['version'][$LongMPEGversionLookup[$head4]]++;
+							@$Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]++;
+							@$Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]++;
+							if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) {
+								$pct_data_scanned = (ftell($fd) - $ThisFileInfo['avdataoffset']) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
+								$ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
+								foreach ($Distribution as $key1 => $value1) {
+									foreach ($value1 as $key2 => $value2) {
+										$Distribution[$key1][$key2] = round($value2 / $pct_data_scanned);
+									}
+								}
+								break;
+							}
+							continue;
+						}
+					}
+					unset($next4);
+					fseek($fd, $WhereWeWere - 3, SEEK_SET);
+				}
+
+			}
+		}
+		foreach ($Distribution as $key => $value) {
+			ksort($Distribution[$key], SORT_NUMERIC);
+		}
+		ksort($Distribution['version'], SORT_STRING);
+		$ThisFileInfo['mpeg']['audio']['bitrate_distribution']   = $Distribution['bitrate'];
+		$ThisFileInfo['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency'];
+		$ThisFileInfo['mpeg']['audio']['layer_distribution']     = $Distribution['layer'];
+		$ThisFileInfo['mpeg']['audio']['version_distribution']   = $Distribution['version'];
+		$ThisFileInfo['mpeg']['audio']['padding_distribution']   = $Distribution['padding'];
+		if (count($Distribution['version']) > 1) {
+			$ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG version detected';
+		}
+		if (count($Distribution['layer']) > 1) {
+			$ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG layer detected';
+		}
+		if (count($Distribution['frequency']) > 1) {
+			$ThisFileInfo['error'][] = 'Corrupt file - more than one MPEG sample rate detected';
+		}
+
+
+		$bittotal = 0;
+		foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) {
+			if ($bitratevalue != 'free') {
+				$bittotal += ($bitratevalue * $bitratecount);
+			}
+		}
+		$ThisFileInfo['mpeg']['audio']['frame_count']  = array_sum($Distribution['bitrate']);
+		if ($ThisFileInfo['mpeg']['audio']['frame_count'] == 0) {
+			$ThisFileInfo['error'][] = 'no MPEG audio frames found';
+			return false;
+		}
+		$ThisFileInfo['mpeg']['audio']['bitrate']      = ($bittotal / $ThisFileInfo['mpeg']['audio']['frame_count']);
+		$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr');
+		$ThisFileInfo['mpeg']['audio']['sample_rate']  = getid3_lib::array_max($Distribution['frequency'], true);
+
+		$ThisFileInfo['audio']['bitrate']      = $ThisFileInfo['mpeg']['audio']['bitrate'];
+		$ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
+		$ThisFileInfo['audio']['sample_rate']  = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+		$ThisFileInfo['audio']['dataformat']   = 'mp'.getid3_lib::array_max($Distribution['layer'], true);
+		$ThisFileInfo['fileformat']            = $ThisFileInfo['audio']['dataformat'];
+
+		return true;
+	}
+
+
+	function getOnlyMPEGaudioInfo($fd, &$ThisFileInfo, $avdataoffset, $BitrateHistogram=false) {
+
+		// looks for synch, decodes MPEG audio header
+
+		static $MPEGaudioVersionLookup;
+		static $MPEGaudioLayerLookup;
+		static $MPEGaudioBitrateLookup;
+		if (empty($MPEGaudioVersionLookup)) {
+		   $MPEGaudioVersionLookup = getid3_mp3::MPEGaudioVersionArray();
+		   $MPEGaudioLayerLookup   = getid3_mp3::MPEGaudioLayerArray();
+		   $MPEGaudioBitrateLookup = getid3_mp3::MPEGaudioBitrateArray();
+
+		}
+
+		fseek($fd, $avdataoffset, SEEK_SET);
+		$sync_seek_buffer_size = min(128 * 1024, $ThisFileInfo['avdataend'] - $avdataoffset);
+		if ($sync_seek_buffer_size <= 0) {
+			$ThisFileInfo['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset;
+			return false;
+		}
+		$header = fread($fd, $sync_seek_buffer_size);
+		$sync_seek_buffer_size = strlen($header);
+		$SynchSeekOffset = 0;
+		while ($SynchSeekOffset < $sync_seek_buffer_size) {
+
+			if ((($avdataoffset + $SynchSeekOffset)  < $ThisFileInfo['avdataend']) && !feof($fd)) {
+
+				if ($SynchSeekOffset > $sync_seek_buffer_size) {
+					// if a synch's not found within the first 128k bytes, then give up
+					$ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB';
+					if (isset($ThisFileInfo['audio']['bitrate'])) {
+						unset($ThisFileInfo['audio']['bitrate']);
+					}
+					if (isset($ThisFileInfo['mpeg']['audio'])) {
+						unset($ThisFileInfo['mpeg']['audio']);
+					}
+					if (empty($ThisFileInfo['mpeg'])) {
+						unset($ThisFileInfo['mpeg']);
+					}
+					return false;
+
+				} elseif (feof($fd)) {
+
+					$ThisFileInfo['error'][] = 'Could not find valid MPEG audio synch before end of file';
+					if (isset($ThisFileInfo['audio']['bitrate'])) {
+						unset($ThisFileInfo['audio']['bitrate']);
+					}
+					if (isset($ThisFileInfo['mpeg']['audio'])) {
+						unset($ThisFileInfo['mpeg']['audio']);
+					}
+					if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || (count($ThisFileInfo['mpeg']) == 0))) {
+						unset($ThisFileInfo['mpeg']);
+					}
+					return false;
+				}
+			}
+
+			if (($SynchSeekOffset + 1) >= strlen($header)) {
+				$ThisFileInfo['error'][] = 'Could not find valid MPEG synch before end of file';
+				return false;
+			}
+
+			if (($header{$SynchSeekOffset} == "\xFF") && ($header{($SynchSeekOffset + 1)} > "\xE0")) { // synch detected
+
+				if (!isset($FirstFrameThisfileInfo) && !isset($ThisFileInfo['mpeg']['audio'])) {
+					$FirstFrameThisfileInfo = $ThisFileInfo;
+					$FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
+					if (!getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $FirstFrameThisfileInfo, false)) {
+						// if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
+						// garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
+						unset($FirstFrameThisfileInfo);
+					}
+				}
+
+				$dummy = $ThisFileInfo; // only overwrite real data if valid header found
+				if (getid3_mp3::decodeMPEGaudioHeader($fd, $avdataoffset + $SynchSeekOffset, $dummy, true)) {
+					$ThisFileInfo = $dummy;
+					$ThisFileInfo['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
+					switch (@$ThisFileInfo['fileformat']) {
+						case '':
+						case 'id3':
+						case 'ape':
+						case 'mp3':
+							$ThisFileInfo['fileformat']          = 'mp3';
+							$ThisFileInfo['audio']['dataformat'] = 'mp3';
+							break;
+					}
+					if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && ($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr')) {
+						if (!(abs($ThisFileInfo['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
+							// If there is garbage data between a valid VBR header frame and a sequence
+							// of valid MPEG-audio frames the VBR data is no longer discarded.
+							$ThisFileInfo = $FirstFrameThisfileInfo;
+							$ThisFileInfo['avdataoffset']        = $FirstFrameAVDataOffset;
+							$ThisFileInfo['fileformat']          = 'mp3';
+							$ThisFileInfo['audio']['dataformat'] = 'mp3';
+							$dummy                               = $ThisFileInfo;
+							unset($dummy['mpeg']['audio']);
+							$GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
+							$GarbageOffsetEnd   = $avdataoffset + $SynchSeekOffset;
+							if (getid3_mp3::decodeMPEGaudioHeader($fd, $GarbageOffsetEnd, $dummy, true, true)) {
+
+								$ThisFileInfo = $dummy;
+								$ThisFileInfo['avdataoffset'] = $GarbageOffsetEnd;
+								$ThisFileInfo['warning'][] = 'apparently-valid VBR header not used because could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.'), but did find valid CBR stream starting at '.$GarbageOffsetEnd;
+
+							} else {
+
+								$ThisFileInfo['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')';
+
+							}
+						}
+					}
+					if (isset($ThisFileInfo['mpeg']['audio']['bitrate_mode']) && ($ThisFileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($ThisFileInfo['mpeg']['audio']['VBR_method'])) {
+						// VBR file with no VBR header
+						$BitrateHistogram = true;
+					}
+
+					if ($BitrateHistogram) {
+
+						$ThisFileInfo['mpeg']['audio']['stereo_distribution']  = array('stereo'=>0, 'joint stereo'=>0, 'dual channel'=>0, 'mono'=>0);
+						$ThisFileInfo['mpeg']['audio']['version_distribution'] = array('1'=>0, '2'=>0, '2.5'=>0);
+
+						if ($ThisFileInfo['mpeg']['audio']['version'] == '1') {
+							if ($ThisFileInfo['mpeg']['audio']['layer'] == 3) {
+								$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0);
+							} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 2) {
+								$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 320000=>0, 384000=>0);
+							} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) {
+								$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 64000=>0, 96000=>0, 128000=>0, 160000=>0, 192000=>0, 224000=>0, 256000=>0, 288000=>0, 320000=>0, 352000=>0, 384000=>0, 416000=>0, 448000=>0);
+							}
+						} elseif ($ThisFileInfo['mpeg']['audio']['layer'] == 1) {
+							$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 32000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0, 176000=>0, 192000=>0, 224000=>0, 256000=>0);
+						} else {
+							$ThisFileInfo['mpeg']['audio']['bitrate_distribution'] = array('free'=>0, 8000=>0, 16000=>0, 24000=>0, 32000=>0, 40000=>0, 48000=>0, 56000=>0, 64000=>0, 80000=>0, 96000=>0, 112000=>0, 128000=>0, 144000=>0, 160000=>0);
+						}
+
+						$dummy = array('error'=>$ThisFileInfo['error'], 'warning'=>$ThisFileInfo['warning'], 'avdataend'=>$ThisFileInfo['avdataend'], 'avdataoffset'=>$ThisFileInfo['avdataoffset']);
+						$synchstartoffset = $ThisFileInfo['avdataoffset'];
+						fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+						// you can play with these numbers:
+						$max_frames_scan  = 50000;
+						$max_scan_segments = 10;
+
+						// don't play with these numbers:
+						$FastMode = false;
+						$SynchErrorsFound = 0;
+						$frames_scanned   = 0;
+						$this_scan_segment = 0;
+						$frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
+						$pct_data_scanned = 0;
+						for ($current_segment = 0; $current_segment < $max_scan_segments; $current_segment++) {
+//echo 'was at '.ftell($fd).'<br>';
+							$frames_scanned_this_segment = 0;
+							if (ftell($fd) >= $ThisFileInfo['avdataend']) {
+//echo 'breaking because current position ('.ftell($fd).') is >= $ThisFileInfo[avdataend] ('.$ThisFileInfo['avdataend'].')<br>';
+								break;
+							}
+							$scan_start_offset[$current_segment] = max(ftell($fd), $ThisFileInfo['avdataoffset'] + round($current_segment * (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $max_scan_segments)));
+//echo 'start at '.$scan_start_offset[$current_segment].'<br>';
+							if ($current_segment > 0) {
+								fseek($fd, $scan_start_offset[$current_segment], SEEK_SET);
+								$buffer_4k = fread($fd, 4096);
+								for ($j = 0; $j < (strlen($buffer_4k) - 4); $j++) {
+									if (($buffer_4k{$j} == "\xFF") && ($buffer_4k{($j + 1)} > "\xE0")) { // synch detected
+										if (getid3_mp3::decodeMPEGaudioHeader($fd, $scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
+											$calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
+											if (getid3_mp3::decodeMPEGaudioHeader($fd, $calculated_next_offset, $dummy, false, false, $FastMode)) {
+												$scan_start_offset[$current_segment] += $j;
+												break;
+											} else {
+//echo 'header['.__LINE__.'] at '.($calculated_next_offset).' invalid<br>';
+											}
+										} else {
+//echo 'header['.__LINE__.'] at '.($scan_start_offset[$current_segment] + $j).' invalid<br>';
+										}
+									}
+								}
+							}
+//echo 'actually start at '.$scan_start_offset[$current_segment].'<br>';
+							$synchstartoffset = $scan_start_offset[$current_segment];
+							while (getid3_mp3::decodeMPEGaudioHeader($fd, $synchstartoffset, $dummy, false, false, $FastMode)) {
+								$FastMode = true;
+								$thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
+
+								if (empty($dummy['mpeg']['audio']['framelength'])) {
+									$SynchErrorsFound++;
+									$synchstartoffset++;
+//echo ' [Ø] ';
+								} else {
+//echo ' . ';
+									@$ThisFileInfo['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]++;
+									@$ThisFileInfo['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]++;
+									@$ThisFileInfo['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]++;
+
+									$synchstartoffset += $dummy['mpeg']['audio']['framelength'];
+								}
+								$frames_scanned++;
+								if ($frames_scan_per_segment && (++$frames_scanned_this_segment >= $frames_scan_per_segment)) {
+									$this_pct_scanned = (ftell($fd) - $scan_start_offset[$current_segment]) / ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']);
+									if (($current_segment == 0) && (($this_pct_scanned * $max_scan_segments) >= 1)) {
+										// file likely contains < $max_frames_scan, just scan as one segment
+										$max_scan_segments = 1;
+										$frames_scan_per_segment = $max_frames_scan;
+									} else {
+										$pct_data_scanned += $this_pct_scanned;
+//var_dump($pct_data_scanned);
+//exit;
+										break;
+									}
+								}
+							}
+//echo '<hr>';
+						}
+						if ($pct_data_scanned > 0) {
+							$ThisFileInfo['warning'][] = 'too many MPEG audio frames to scan, only scanned '.$frames_scanned.' frames in '.$max_scan_segments.' segments ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
+							foreach ($ThisFileInfo['mpeg']['audio'] as $key1 => $value1) {
+								if (!eregi('_distribution$', $key1)) {
+									continue;
+								}
+								foreach ($value1 as $key2 => $value2) {
+									$ThisFileInfo['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
+								}
+							}
+						}
+
+						if ($SynchErrorsFound > 0) {
+							$ThisFileInfo['warning'][] = 'Found '.$SynchErrorsFound.' synch errors in histogram analysis';
+							//return false;
+						}
+
+						$bittotal     = 0;
+						$framecounter = 0;
+						foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
+							$framecounter += $bitratecount;
+							if ($bitratevalue != 'free') {
+								$bittotal += ($bitratevalue * $bitratecount);
+							}
+						}
+						if ($framecounter == 0) {
+							$ThisFileInfo['error'][] = 'Corrupt MP3 file: framecounter == zero';
+							return false;
+						}
+						$ThisFileInfo['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter);
+						$ThisFileInfo['mpeg']['audio']['bitrate']     = ($bittotal / $framecounter);
+
+						$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpeg']['audio']['bitrate'];
+
+
+						// Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
+						$distinct_bitrates = 0;
+						foreach ($ThisFileInfo['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
+							if ($bitrate_count > 0) {
+								$distinct_bitrates++;
+							}
+						}
+						if ($distinct_bitrates > 1) {
+							$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'vbr';
+						} else {
+							$ThisFileInfo['mpeg']['audio']['bitrate_mode'] = 'cbr';
+						}
+						$ThisFileInfo['audio']['bitrate_mode'] = $ThisFileInfo['mpeg']['audio']['bitrate_mode'];
+
+					}
+
+					break; // exit while()
+				}
+			}
+
+			$SynchSeekOffset++;
+			if (($avdataoffset + $SynchSeekOffset) >= $ThisFileInfo['avdataend']) {
+				// end of file/data
+
+				if (empty($ThisFileInfo['mpeg']['audio'])) {
+
+					$ThisFileInfo['error'][] = 'could not find valid MPEG synch before end of file';
+					if (isset($ThisFileInfo['audio']['bitrate'])) {
+						unset($ThisFileInfo['audio']['bitrate']);
+					}
+					if (isset($ThisFileInfo['mpeg']['audio'])) {
+						unset($ThisFileInfo['mpeg']['audio']);
+					}
+					if (isset($ThisFileInfo['mpeg']) && (!is_array($ThisFileInfo['mpeg']) || empty($ThisFileInfo['mpeg']))) {
+						unset($ThisFileInfo['mpeg']);
+					}
+					return false;
+
+				}
+				break;
+			}
+
+		}
+		$ThisFileInfo['audio']['channels']        = $ThisFileInfo['mpeg']['audio']['channels'];
+		$ThisFileInfo['audio']['channelmode']     = $ThisFileInfo['mpeg']['audio']['channelmode'];
+		$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['mpeg']['audio']['sample_rate'];
+		return true;
+	}
+
+
+	function MPEGaudioVersionArray() {
+		static $MPEGaudioVersion = array('2.5', false, '2', '1');
+		return $MPEGaudioVersion;
+	}
+
+	function MPEGaudioLayerArray() {
+		static $MPEGaudioLayer = array(false, 3, 2, 1);
+		return $MPEGaudioLayer;
+	}
+
+	function MPEGaudioBitrateArray() {
+		static $MPEGaudioBitrate;
+		if (empty($MPEGaudioBitrate)) {
+			$MPEGaudioBitrate = array (
+				'1'  =>  array (1 => array('free', 32000, 64000, 96000, 128000, 160000, 192000, 224000, 256000, 288000, 320000, 352000, 384000, 416000, 448000),
+								2 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 384000),
+								3 => array('free', 32000, 40000, 48000,  56000,  64000,  80000,  96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000)
+							   ),
+
+				'2'  =>  array (1 => array('free', 32000, 48000, 56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000, 176000, 192000, 224000, 256000),
+								2 => array('free',  8000, 16000, 24000,  32000,  40000,  48000,  56000,  64000,  80000,  96000, 112000, 128000, 144000, 160000),
+							   )
+			);
+			$MPEGaudioBitrate['2'][3] = $MPEGaudioBitrate['2'][2];
+			$MPEGaudioBitrate['2.5']  = $MPEGaudioBitrate['2'];
+		}
+		return $MPEGaudioBitrate;
+	}
+
+	function MPEGaudioFrequencyArray() {
+		static $MPEGaudioFrequency;
+		if (empty($MPEGaudioFrequency)) {
+			$MPEGaudioFrequency = array (
+				'1'   => array(44100, 48000, 32000),
+				'2'   => array(22050, 24000, 16000),
+				'2.5' => array(11025, 12000,  8000)
+			);
+		}
+		return $MPEGaudioFrequency;
+	}
+
+	function MPEGaudioChannelModeArray() {
+		static $MPEGaudioChannelMode = array('stereo', 'joint stereo', 'dual channel', 'mono');
+		return $MPEGaudioChannelMode;
+	}
+
+	function MPEGaudioModeExtensionArray() {
+		static $MPEGaudioModeExtension;
+		if (empty($MPEGaudioModeExtension)) {
+			$MPEGaudioModeExtension = array (
+				1 => array('4-31', '8-31', '12-31', '16-31'),
+				2 => array('4-31', '8-31', '12-31', '16-31'),
+				3 => array('', 'IS', 'MS', 'IS+MS')
+			);
+		}
+		return $MPEGaudioModeExtension;
+	}
+
+	function MPEGaudioEmphasisArray() {
+		static $MPEGaudioEmphasis = array('none', '50/15ms', false, 'CCIT J.17');
+		return $MPEGaudioEmphasis;
+	}
+
+	function MPEGaudioHeaderBytesValid($head4, $allowBitrate15=false) {
+		return getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode($head4), false, $allowBitrate15);
+	}
+
+	function MPEGaudioHeaderValid($rawarray, $echoerrors=false, $allowBitrate15=false) {
+		if (($rawarray['synch'] & 0x0FFE) != 0x0FFE) {
+			return false;
+		}
+
+		static $MPEGaudioVersionLookup;
+		static $MPEGaudioLayerLookup;
+		static $MPEGaudioBitrateLookup;
+		static $MPEGaudioFrequencyLookup;
+		static $MPEGaudioChannelModeLookup;
+		static $MPEGaudioModeExtensionLookup;
+		static $MPEGaudioEmphasisLookup;
+		if (empty($MPEGaudioVersionLookup)) {
+			$MPEGaudioVersionLookup       = getid3_mp3::MPEGaudioVersionArray();
+			$MPEGaudioLayerLookup         = getid3_mp3::MPEGaudioLayerArray();
+			$MPEGaudioBitrateLookup       = getid3_mp3::MPEGaudioBitrateArray();
+			$MPEGaudioFrequencyLookup     = getid3_mp3::MPEGaudioFrequencyArray();
+			$MPEGaudioChannelModeLookup   = getid3_mp3::MPEGaudioChannelModeArray();
+			$MPEGaudioModeExtensionLookup = getid3_mp3::MPEGaudioModeExtensionArray();
+			$MPEGaudioEmphasisLookup      = getid3_mp3::MPEGaudioEmphasisArray();
+		}
+
+		if (isset($MPEGaudioVersionLookup[$rawarray['version']])) {
+			$decodedVersion = $MPEGaudioVersionLookup[$rawarray['version']];
+		} else {
+			echo ($echoerrors ? "\n".'invalid Version ('.$rawarray['version'].')' : '');
+			return false;
+		}
+		if (isset($MPEGaudioLayerLookup[$rawarray['layer']])) {
+			$decodedLayer = $MPEGaudioLayerLookup[$rawarray['layer']];
+		} else {
+			echo ($echoerrors ? "\n".'invalid Layer ('.$rawarray['layer'].')' : '');
+			return false;
+		}
+		if (!isset($MPEGaudioBitrateLookup[$decodedVersion][$decodedLayer][$rawarray['bitrate']])) {
+			echo ($echoerrors ? "\n".'invalid Bitrate ('.$rawarray['bitrate'].')' : '');
+			if ($rawarray['bitrate'] == 15) {
+				// known issue in LAME 3.90 - 3.93.1 where free-format has bitrate ID of 15 instead of 0
+				// let it go through here otherwise file will not be identified
+				if (!$allowBitrate15) {
+					return false;
+				}
+			} else {
+				return false;
+			}
+		}
+		if (!isset($MPEGaudioFrequencyLookup[$decodedVersion][$rawarray['sample_rate']])) {
+			echo ($echoerrors ? "\n".'invalid Frequency ('.$rawarray['sample_rate'].')' : '');
+			return false;
+		}
+		if (!isset($MPEGaudioChannelModeLookup[$rawarray['channelmode']])) {
+			echo ($echoerrors ? "\n".'invalid ChannelMode ('.$rawarray['channelmode'].')' : '');
+			return false;
+		}
+		if (!isset($MPEGaudioModeExtensionLookup[$decodedLayer][$rawarray['modeextension']])) {
+			echo ($echoerrors ? "\n".'invalid Mode Extension ('.$rawarray['modeextension'].')' : '');
+			return false;
+		}
+		if (!isset($MPEGaudioEmphasisLookup[$rawarray['emphasis']])) {
+			echo ($echoerrors ? "\n".'invalid Emphasis ('.$rawarray['emphasis'].')' : '');
+			return false;
+		}
+		// These are just either set or not set, you can't mess that up :)
+		// $rawarray['protection'];
+		// $rawarray['padding'];
+		// $rawarray['private'];
+		// $rawarray['copyright'];
+		// $rawarray['original'];
+
+		return true;
+	}
+
+	function MPEGaudioHeaderDecode($Header4Bytes) {
+		// AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
+		// A - Frame sync (all bits set)
+		// B - MPEG Audio version ID
+		// C - Layer description
+		// D - Protection bit
+		// E - Bitrate index
+		// F - Sampling rate frequency index
+		// G - Padding bit
+		// H - Private bit
+		// I - Channel Mode
+		// J - Mode extension (Only if Joint stereo)
+		// K - Copyright
+		// L - Original
+		// M - Emphasis
+
+		if (strlen($Header4Bytes) != 4) {
+			return false;
+		}
+
+		$MPEGrawHeader['synch']         = (getid3_lib::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xFFE0) >> 4;
+		$MPEGrawHeader['version']       = (ord($Header4Bytes{1}) & 0x18) >> 3; //    BB
+		$MPEGrawHeader['layer']         = (ord($Header4Bytes{1}) & 0x06) >> 1; //      CC
+		$MPEGrawHeader['protection']    = (ord($Header4Bytes{1}) & 0x01);      //        D
+		$MPEGrawHeader['bitrate']       = (ord($Header4Bytes{2}) & 0xF0) >> 4; // EEEE
+		$MPEGrawHeader['sample_rate']   = (ord($Header4Bytes{2}) & 0x0C) >> 2; //     FF
+		$MPEGrawHeader['padding']       = (ord($Header4Bytes{2}) & 0x02) >> 1; //       G
+		$MPEGrawHeader['private']       = (ord($Header4Bytes{2}) & 0x01);      //        H
+		$MPEGrawHeader['channelmode']   = (ord($Header4Bytes{3}) & 0xC0) >> 6; // II
+		$MPEGrawHeader['modeextension'] = (ord($Header4Bytes{3}) & 0x30) >> 4; //   JJ
+		$MPEGrawHeader['copyright']     = (ord($Header4Bytes{3}) & 0x08) >> 3; //     K
+		$MPEGrawHeader['original']      = (ord($Header4Bytes{3}) & 0x04) >> 2; //      L
+		$MPEGrawHeader['emphasis']      = (ord($Header4Bytes{3}) & 0x03);      //       MM
+
+		return $MPEGrawHeader;
+	}
+
+	function MPEGaudioFrameLength(&$bitrate, &$version, &$layer, $padding, &$samplerate) {
+		static $AudioFrameLengthCache = array();
+
+		if (!isset($AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate])) {
+			$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = false;
+			if ($bitrate != 'free') {
+
+				if ($version == '1') {
+
+					if ($layer == '1') {
+
+						// For Layer I slot is 32 bits long
+						$FrameLengthCoefficient = 48;
+						$SlotLength = 4;
+
+					} else { // Layer 2 / 3
+
+						// for Layer 2 and Layer 3 slot is 8 bits long.
+						$FrameLengthCoefficient = 144;
+						$SlotLength = 1;
+
+					}
+
+				} else { // MPEG-2 / MPEG-2.5
+
+					if ($layer == '1') {
+
+						// For Layer I slot is 32 bits long
+						$FrameLengthCoefficient = 24;
+						$SlotLength = 4;
+
+					} elseif ($layer == '2') {
+
+						// for Layer 2 and Layer 3 slot is 8 bits long.
+						$FrameLengthCoefficient = 144;
+						$SlotLength = 1;
+
+					} else { // layer 3
+
+						// for Layer 2 and Layer 3 slot is 8 bits long.
+						$FrameLengthCoefficient = 72;
+						$SlotLength = 1;
+
+					}
+
+				}
+
+				// FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
+				if ($samplerate > 0) {
+					$NewFramelength  = ($FrameLengthCoefficient * $bitrate) / $samplerate;
+					$NewFramelength  = floor($NewFramelength / $SlotLength) * $SlotLength; // round to next-lower multiple of SlotLength (1 byte for Layer 2/3, 4 bytes for Layer I)
+					if ($padding) {
+						$NewFramelength += $SlotLength;
+					}
+					$AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate] = (int) $NewFramelength;
+				}
+			}
+		}
+		return $AudioFrameLengthCache[$bitrate][$version][$layer][$padding][$samplerate];
+	}
+
+	function ClosestStandardMP3Bitrate($bitrate) {
+		static $StandardBitrates = array(320000, 256000, 224000, 192000, 160000, 128000, 112000, 96000, 80000, 64000, 56000, 48000, 40000, 32000, 24000, 16000, 8000);
+		static $BitrateTable = array(0=>'-');
+		$roundbitrate = intval(round($bitrate, -3));
+		if (!isset($BitrateTable[$roundbitrate])) {
+			if ($roundbitrate > 320000) {
+				$BitrateTable[$roundbitrate] = round($bitrate, -4);
+			} else {
+				$LastBitrate = 320000;
+				foreach ($StandardBitrates as $StandardBitrate) {
+					$BitrateTable[$roundbitrate] = $StandardBitrate;
+					if ($roundbitrate >= $StandardBitrate - (($LastBitrate - $StandardBitrate) / 2)) {
+						break;
+					}
+					$LastBitrate = $StandardBitrate;
+				}
+			}
+		}
+		return $BitrateTable[$roundbitrate];
+	}
+
+	function XingVBRidOffset($version, $channelmode) {
+		static $XingVBRidOffsetCache = array();
+		if (empty($XingVBRidOffset)) {
+			$XingVBRidOffset = array (
+				'1'   => array ('mono'          => 0x15, // 4 + 17 = 21
+								'stereo'        => 0x24, // 4 + 32 = 36
+								'joint stereo'  => 0x24,
+								'dual channel'  => 0x24
+							   ),
+
+				'2'   => array ('mono'          => 0x0D, // 4 +  9 = 13
+								'stereo'        => 0x15, // 4 + 17 = 21
+								'joint stereo'  => 0x15,
+								'dual channel'  => 0x15
+							   ),
+
+				'2.5' => array ('mono'          => 0x15,
+								'stereo'        => 0x15,
+								'joint stereo'  => 0x15,
+								'dual channel'  => 0x15
+							   )
+			);
+		}
+		return $XingVBRidOffset[$version][$channelmode];
+	}
+
+	function LAMEvbrMethodLookup($VBRmethodID) {
+		static $LAMEvbrMethodLookup = array(
+			0x00 => 'unknown',
+			0x01 => 'cbr',
+			0x02 => 'abr',
+			0x03 => 'vbr-old / vbr-rh',
+			0x04 => 'vbr-new / vbr-mtrh',
+			0x05 => 'vbr-mt',
+			0x06 => 'vbr (full vbr method 4)',
+			0x08 => 'cbr (constant bitrate 2 pass)',
+			0x09 => 'abr (2 pass)',
+			0x0F => 'reserved'
+		);
+		return (isset($LAMEvbrMethodLookup[$VBRmethodID]) ? $LAMEvbrMethodLookup[$VBRmethodID] : '');
+	}
+
+	function LAMEmiscStereoModeLookup($StereoModeID) {
+		static $LAMEmiscStereoModeLookup = array(
+			0 => 'mono',
+			1 => 'stereo',
+			2 => 'dual mono',
+			3 => 'joint stereo',
+			4 => 'forced stereo',
+			5 => 'auto',
+			6 => 'intensity stereo',
+			7 => 'other'
+		);
+		return (isset($LAMEmiscStereoModeLookup[$StereoModeID]) ? $LAMEmiscStereoModeLookup[$StereoModeID] : '');
+	}
+
+	function LAMEmiscSourceSampleFrequencyLookup($SourceSampleFrequencyID) {
+		static $LAMEmiscSourceSampleFrequencyLookup = array(
+			0 => '<= 32 kHz',
+			1 => '44.1 kHz',
+			2 => '48 kHz',
+			3 => '> 48kHz'
+		);
+		return (isset($LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID]) ? $LAMEmiscSourceSampleFrequencyLookup[$SourceSampleFrequencyID] : '');
+	}
+
+	function LAMEsurroundInfoLookup($SurroundInfoID) {
+		static $LAMEsurroundInfoLookup = array(
+			0 => 'no surround info',
+			1 => 'DPL encoding',
+			2 => 'DPL2 encoding',
+			3 => 'Ambisonic encoding'
+		);
+		return (isset($LAMEsurroundInfoLookup[$SurroundInfoID]) ? $LAMEsurroundInfoLookup[$SurroundInfoID] : 'reserved');
+	}
+
+	function LAMEpresetUsedLookup($LAMEtag) {
+
+		if ($LAMEtag['preset_used_id'] == 0) {
+			// no preset used (LAME >=3.93)
+			// no preset recorded (LAME <3.93)
+			return '';
+		}
+		$LAMEpresetUsedLookup = array();
+
+		/////  THIS PART CANNOT BE STATIC .
+		for ($i = 8; $i <= 320; $i++) {
+			switch ($LAMEtag['vbr_method']) {
+				case 'cbr':
+					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$LAMEtag['vbr_method'].' '.$i;
+					break;
+				case 'abr':
+				default: // other VBR modes shouldn't be here(?)
+					$LAMEpresetUsedLookup[$i] = '--alt-preset '.$i;
+					break;
+			}
+		}
+
+		// named old-style presets (studio, phone, voice, etc) are handled in GuessEncoderOptions()
+
+		// named alt-presets
+		$LAMEpresetUsedLookup[1000] = '--r3mix';
+		$LAMEpresetUsedLookup[1001] = '--alt-preset standard';
+		$LAMEpresetUsedLookup[1002] = '--alt-preset extreme';
+		$LAMEpresetUsedLookup[1003] = '--alt-preset insane';
+		$LAMEpresetUsedLookup[1004] = '--alt-preset fast standard';
+		$LAMEpresetUsedLookup[1005] = '--alt-preset fast extreme';
+		$LAMEpresetUsedLookup[1006] = '--alt-preset medium';
+		$LAMEpresetUsedLookup[1007] = '--alt-preset fast medium';
+
+		// LAME 3.94 additions/changes
+		$LAMEpresetUsedLookup[1010] = '--preset portable';                                                           // 3.94a15 Oct 21 2003
+		$LAMEpresetUsedLookup[1015] = '--preset radio';                                                              // 3.94a15 Oct 21 2003
+
+		$LAMEpresetUsedLookup[320]  = '--preset insane';                                                             // 3.94a15 Nov 12 2003
+		$LAMEpresetUsedLookup[410]  = '-V9';
+		$LAMEpresetUsedLookup[420]  = '-V8';
+		$LAMEpresetUsedLookup[440]  = '-V6';
+		$LAMEpresetUsedLookup[430]  = '--preset radio';                                                              // 3.94a15 Nov 12 2003
+		$LAMEpresetUsedLookup[450]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'portable';  // 3.94a15 Nov 12 2003
+		$LAMEpresetUsedLookup[460]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'medium';    // 3.94a15 Nov 12 2003
+		$LAMEpresetUsedLookup[470]  = '--r3mix';                                                                     // 3.94b1  Dec 18 2003
+		$LAMEpresetUsedLookup[480]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'standard';  // 3.94a15 Nov 12 2003
+		$LAMEpresetUsedLookup[490]  = '-V1';
+		$LAMEpresetUsedLookup[500]  = '--preset '.(($LAMEtag['raw']['vbr_method'] == 4) ? 'fast ' : '').'extreme';   // 3.94a15 Nov 12 2003
+
+		return (isset($LAMEpresetUsedLookup[$LAMEtag['preset_used_id']]) ? $LAMEpresetUsedLookup[$LAMEtag['preset_used_id']] : 'new/unknown preset: '.$LAMEtag['preset_used_id'].' - report to info@getid3.org');
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.mpc.php b/apps/media/getID3/getid3/module.audio.mpc.php
new file mode 100644
index 0000000000000000000000000000000000000000..66200ad02a7e0a916e2e34b4d0ab9ed432dfd28d
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.mpc.php
@@ -0,0 +1,502 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.mpc.php                                        //
+// module for analyzing Musepack/MPEG+ Audio files             //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_mpc
+{
+
+	function getid3_mpc(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['mpc']['header'] = array();
+		$thisfile_mpc_header           = &$ThisFileInfo['mpc']['header'];
+
+		$ThisFileInfo['fileformat']               = 'mpc';
+		$ThisFileInfo['audio']['dataformat']      = 'mpc';
+		$ThisFileInfo['audio']['bitrate_mode']    = 'vbr';
+		$ThisFileInfo['audio']['channels']        = 2;  // up to SV7 the format appears to have been hardcoded for stereo only
+		$ThisFileInfo['audio']['lossless']        = false;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$MPCheaderData = fread($fd, 4);
+		$ThisFileInfo['mpc']['header']['preamble'] = substr($MPCheaderData, 0, 4); // should be 'MPCK' (SV8) or 'MP+' (SV7), otherwise possible stream data (SV4-SV6)
+		if (ereg('^MPCK', $ThisFileInfo['mpc']['header']['preamble'])) {
+
+			// this is SV8
+			return $this->ParseMPCsv8($fd, $ThisFileInfo);
+
+		} elseif (ereg('^MP\+', $ThisFileInfo['mpc']['header']['preamble'])) {
+
+			// this is SV7
+			return $this->ParseMPCsv7($fd, $ThisFileInfo);
+
+		} elseif (preg_match('/^[\x00\x01\x10\x11\x40\x41\x50\x51\x80\x81\x90\x91\xC0\xC1\xD0\xD1][\x20-37][\x00\x20\x40\x60\x80\xA0\xC0\xE0]/s', $MPCheaderData)) {
+
+			// this is SV4 - SV6, handle seperately
+			return $this->ParseMPCsv6($fd, $ThisFileInfo);
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Expecting "MP+" or "MPCK" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($MPCheaderData, 0, 4).'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['mpc']);
+			return false;
+
+		}
+		return false;
+	}
+
+
+	function ParseMPCsv8(&$fd, &$ThisFileInfo) {
+		// this is SV8
+		// http://trac.musepack.net/trac/wiki/SV8Specification
+
+		$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
+
+		$keyNameSize            = 2;
+		$maxHandledPacketLength = 9; // specs say: "n*8; 0 < n < 10"
+
+		$offset = ftell($fd);
+		while ($offset < $ThisFileInfo['avdataend']) {
+			$thisPacket = array();
+			$thisPacket['offset'] = $offset;
+			$packet_offset = 0;
+
+			// Size is a variable-size field, could be 1-4 bytes (possibly more?)
+			// read enough data in and figure out the exact size later
+			$MPCheaderData = fread($fd, $keyNameSize + $maxHandledPacketLength);
+			$packet_offset += $keyNameSize;
+			$thisPacket['key']      = substr($MPCheaderData, 0, $keyNameSize);
+			$thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
+			if ($thisPacket['key'] == $thisPacket['key_name']) {
+				$ThisFileInfo['error'][] = 'Found unexpected key value "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
+				return false;
+			}
+			$packetLength = 0;
+			$thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength); // includes keyname and packet_size field
+			if ($thisPacket['packet_size'] === false) {
+				$ThisFileInfo['error'][] = 'Did not find expected packet length within '.$maxHandledPacketLength.' bytes at offset '.($thisPacket['offset'] + $keyNameSize);
+				return false;
+			}
+			$packet_offset += $packetLength;
+			$offset += $thisPacket['packet_size'];
+
+			switch ($thisPacket['key']) {
+				case 'SH': // Stream Header
+					$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
+					if ($moreBytesToRead > 0) {
+						$MPCheaderData .= fread($fd, $moreBytesToRead);
+					}
+					$thisPacket['crc']               =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
+					$packet_offset += 4;
+					$thisPacket['stream_version']    =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
+					$packet_offset += 1;
+
+					$packetLength = 0;
+					$thisPacket['sample_count']      = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
+					$packet_offset += $packetLength;
+
+					$packetLength = 0;
+					$thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
+					$packet_offset += $packetLength;
+
+					$otherUsefulData                 =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
+					$packet_offset += 2;
+					$thisPacket['sample_frequency_raw'] =        (($otherUsefulData & 0xE000) >> 13);
+					$thisPacket['max_bands_used']       =        (($otherUsefulData & 0x1F00) >>  8);
+					$thisPacket['channels']             =        (($otherUsefulData & 0x00F0) >>  4) + 1;
+					$thisPacket['ms_used']              = (bool) (($otherUsefulData & 0x0008) >>  3);
+					$thisPacket['audio_block_frames']   =        (($otherUsefulData & 0x0007) >>  0);
+					$thisPacket['sample_frequency']     = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
+
+					$thisfile_mpc_header['mid_side_stereo']      = $thisPacket['ms_used'];
+					$thisfile_mpc_header['sample_rate']          = $thisPacket['sample_frequency'];
+					$thisfile_mpc_header['samples']              = $thisPacket['sample_count'];
+					$thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
+
+					$ThisFileInfo['audio']['channels']    = $thisPacket['channels'];
+					$ThisFileInfo['audio']['sample_rate'] = $thisPacket['sample_frequency'];
+					$ThisFileInfo['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
+					$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+					break;
+
+				case 'RG': // Replay Gain
+					$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
+					if ($moreBytesToRead > 0) {
+						$MPCheaderData .= fread($fd, $moreBytesToRead);
+					}
+					$thisPacket['replaygain_version']     =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
+					$packet_offset += 1;
+					$thisPacket['replaygain_title_gain']  =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
+					$packet_offset += 2;
+					$thisPacket['replaygain_title_peak']  =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
+					$packet_offset += 2;
+					$thisPacket['replaygain_album_gain']  =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
+					$packet_offset += 2;
+					$thisPacket['replaygain_album_peak']  =       getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
+					$packet_offset += 2;
+
+					if ($thisPacket['replaygain_title_gain']) { $ThisFileInfo['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain']; }
+					if ($thisPacket['replaygain_title_peak']) { $ThisFileInfo['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak']; }
+					if ($thisPacket['replaygain_album_gain']) { $ThisFileInfo['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain']; }
+					if ($thisPacket['replaygain_album_peak']) { $ThisFileInfo['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak']; }
+					break;
+
+				case 'EI': // Encoder Info
+					$moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
+					if ($moreBytesToRead > 0) {
+						$MPCheaderData .= fread($fd, $moreBytesToRead);
+					}
+					$profile_pns                 = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
+					$packet_offset += 1;
+					$quality_int =                   (($profile_pns & 0xF0) >> 4);
+					$quality_dec =                   (($profile_pns & 0x0E) >> 3);
+					$thisPacket['quality'] = (float) $quality_int + ($quality_dec / 8);
+					$thisPacket['pns_tool'] = (bool) (($profile_pns & 0x01) >> 0);
+					$thisPacket['version_major'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
+					$packet_offset += 1;
+					$thisPacket['version_minor'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
+					$packet_offset += 1;
+					$thisPacket['version_build'] = getid3_lib::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
+					$packet_offset += 1;
+					$thisPacket['version'] = $thisPacket['version_major'].'.'.$thisPacket['version_minor'].'.'.$thisPacket['version_build'];
+
+					$ThisFileInfo['audio']['encoder'] = 'MPC v'.$thisPacket['version'].' ('.(($thisPacket['version_minor'] % 2) ? 'unstable' : 'stable').')';
+					$thisfile_mpc_header['encoder_version'] = $ThisFileInfo['audio']['encoder'];
+					//$thisfile_mpc_header['quality']         = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
+					$thisfile_mpc_header['quality']         = (float) ($thisPacket['quality'] - 5); // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
+					break;
+
+				case 'SO': // Seek Table Offset
+					$packetLength = 0;
+					$thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
+					$packet_offset += $packetLength;
+					break;
+
+				case 'ST': // Seek Table
+				case 'SE': // Stream End
+				case 'AP': // Audio Data
+					// nothing useful here, just skip this packet
+					$thisPacket = array();
+					break;
+
+				default:
+					$ThisFileInfo['error'][] = 'Found unhandled key type "'.$thisPacket['key'].'" at offset '.$thisPacket['offset'];
+					return false;
+					break;
+			}
+			if (!empty($thisPacket)) {
+				$ThisFileInfo['mpc']['packets'][] = $thisPacket;
+			}
+			fseek($fd, $offset);
+		}
+		$thisfile_mpc_header['size'] = $offset;
+		return true;
+	}
+
+	function ParseMPCsv7(&$fd, &$ThisFileInfo) {
+		// this is SV7
+		// http://www.uni-jena.de/~pfk/mpp/sv8/header.html
+		$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
+		$offset = 0;
+
+		$thisfile_mpc_header['size'] = 28;
+		$MPCheaderData  = $ThisFileInfo['mpc']['header']['preamble'];
+		$MPCheaderData .= fread($fd, $thisfile_mpc_header['size'] - strlen($ThisFileInfo['mpc']['header']['preamble']));
+		$offset = strlen('MP+');
+
+		$StreamVersionByte                           = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
+		$offset += 1;
+		$thisfile_mpc_header['stream_version_major'] = ($StreamVersionByte & 0x0F) >> 0;
+		$thisfile_mpc_header['stream_version_minor'] = ($StreamVersionByte & 0xF0) >> 4; // should always be 0, subversions no longer exist in SV8
+		$thisfile_mpc_header['frame_count']          = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
+		$offset += 4;
+
+		if ($thisfile_mpc_header['stream_version_major'] != 7) {
+			$ThisFileInfo['error'][] = 'Only Musepack SV7 supported (this file claims to be v'.$thisfile_mpc_header['stream_version_major'].')';
+			return false;
+		}
+
+		$FlagsDWORD1                                   = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
+		$offset += 4;
+		$thisfile_mpc_header['intensity_stereo']       = (bool) (($FlagsDWORD1 & 0x80000000) >> 31);
+		$thisfile_mpc_header['mid_side_stereo']        = (bool) (($FlagsDWORD1 & 0x40000000) >> 30);
+		$thisfile_mpc_header['max_subband']            =         ($FlagsDWORD1 & 0x3F000000) >> 24;
+		$thisfile_mpc_header['raw']['profile']         =         ($FlagsDWORD1 & 0x00F00000) >> 20;
+		$thisfile_mpc_header['begin_loud']             = (bool) (($FlagsDWORD1 & 0x00080000) >> 19);
+		$thisfile_mpc_header['end_loud']               = (bool) (($FlagsDWORD1 & 0x00040000) >> 18);
+		$thisfile_mpc_header['raw']['sample_rate']     =         ($FlagsDWORD1 & 0x00030000) >> 16;
+		$thisfile_mpc_header['max_level']              =         ($FlagsDWORD1 & 0x0000FFFF);
+
+		$thisfile_mpc_header['raw']['title_peak']      = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
+		$offset += 2;
+		$thisfile_mpc_header['raw']['title_gain']      = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
+		$offset += 2;
+
+		$thisfile_mpc_header['raw']['album_peak']      = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2));
+		$offset += 2;
+		$thisfile_mpc_header['raw']['album_gain']      = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 2), true);
+		$offset += 2;
+
+		$FlagsDWORD2                                   = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 4));
+		$offset += 4;
+		$thisfile_mpc_header['true_gapless']           = (bool) (($FlagsDWORD2 & 0x80000000) >> 31);
+		$thisfile_mpc_header['last_frame_length']      =         ($FlagsDWORD2 & 0x7FF00000) >> 20;
+
+
+		$thisfile_mpc_header['raw']['not_sure_what']   = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 3));
+		$offset += 3;
+		$thisfile_mpc_header['raw']['encoder_version'] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, $offset, 1));
+		$offset += 1;
+
+		$thisfile_mpc_header['profile']     = $this->MPCprofileNameLookup($thisfile_mpc_header['raw']['profile']);
+		$thisfile_mpc_header['sample_rate'] = $this->MPCfrequencyLookup($thisfile_mpc_header['raw']['sample_rate']);
+		if ($thisfile_mpc_header['sample_rate'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt MPC file: frequency == zero';
+			return false;
+		}
+		$ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
+		$thisfile_mpc_header['samples']       = ((($thisfile_mpc_header['frame_count'] - 1) * 1152) + $thisfile_mpc_header['last_frame_length']) * $ThisFileInfo['audio']['channels'];
+
+		$ThisFileInfo['playtime_seconds']     = ($thisfile_mpc_header['samples'] / $ThisFileInfo['audio']['channels']) / $ThisFileInfo['audio']['sample_rate'];
+		if ($ThisFileInfo['playtime_seconds'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt MPC file: playtime_seconds == zero';
+			return false;
+		}
+
+		// add size of file header to avdataoffset - calc bitrate correctly + MD5 data
+		$ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
+
+		$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		$thisfile_mpc_header['title_peak']        = $thisfile_mpc_header['raw']['title_peak'];
+		$thisfile_mpc_header['title_peak_db']     = $this->MPCpeakDBLookup($thisfile_mpc_header['title_peak']);
+		if ($thisfile_mpc_header['raw']['title_gain'] < 0) {
+			$thisfile_mpc_header['title_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['title_gain']) / -100;
+		} else {
+			$thisfile_mpc_header['title_gain_db'] = (float) $thisfile_mpc_header['raw']['title_gain'] / 100;
+		}
+
+		$thisfile_mpc_header['album_peak']        = $thisfile_mpc_header['raw']['album_peak'];
+		$thisfile_mpc_header['album_peak_db']     = $this->MPCpeakDBLookup($thisfile_mpc_header['album_peak']);
+		if ($thisfile_mpc_header['raw']['album_gain'] < 0) {
+			$thisfile_mpc_header['album_gain_db'] = (float) (32768 + $thisfile_mpc_header['raw']['album_gain']) / -100;
+		} else {
+			$thisfile_mpc_header['album_gain_db'] = (float) $thisfile_mpc_header['raw']['album_gain'] / 100;;
+		}
+		$thisfile_mpc_header['encoder_version']   = $this->MPCencoderVersionLookup($thisfile_mpc_header['raw']['encoder_version']);
+
+		$ThisFileInfo['replay_gain']['track']['adjustment'] = $thisfile_mpc_header['title_gain_db'];
+		$ThisFileInfo['replay_gain']['album']['adjustment'] = $thisfile_mpc_header['album_gain_db'];
+
+		if ($thisfile_mpc_header['title_peak'] > 0) {
+			$ThisFileInfo['replay_gain']['track']['peak'] = $thisfile_mpc_header['title_peak'];
+		} elseif (round($thisfile_mpc_header['max_level'] * 1.18) > 0) {
+			$ThisFileInfo['replay_gain']['track']['peak'] = getid3_lib::CastAsInt(round($thisfile_mpc_header['max_level'] * 1.18)); // why? I don't know - see mppdec.c
+		}
+		if ($thisfile_mpc_header['album_peak'] > 0) {
+			$ThisFileInfo['replay_gain']['album']['peak'] = $thisfile_mpc_header['album_peak'];
+		}
+
+		//$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'].'.'.$thisfile_mpc_header['stream_version_minor'].', '.$thisfile_mpc_header['encoder_version'];
+		$ThisFileInfo['audio']['encoder'] = $thisfile_mpc_header['encoder_version'];
+		$ThisFileInfo['audio']['encoder_options'] = $thisfile_mpc_header['profile'];
+		$thisfile_mpc_header['quality'] = (float) ($thisfile_mpc_header['raw']['profile'] - 5); // values can range from 0 to 15, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
+
+		return true;
+	}
+
+	function ParseMPCsv6(&$fd, &$ThisFileInfo) {
+		// this is SV4 - SV6
+		$thisfile_mpc_header = &$ThisFileInfo['mpc']['header'];
+		$offset = 0;
+
+        $thisfile_mpc_header['size'] = 8;
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$MPCheaderData = fread($fd, $thisfile_mpc_header['size']);
+
+        // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
+	    $ThisFileInfo['avdataoffset'] += $thisfile_mpc_header['size'];
+
+		// Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
+		$HeaderDWORD[0] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 0, 4));
+		$HeaderDWORD[1] = getid3_lib::LittleEndian2Int(substr($MPCheaderData, 4, 4));
+
+
+		// DDDD DDDD  CCCC CCCC  BBBB BBBB  AAAA AAAA
+		// aaaa aaaa  abcd dddd  dddd deee  eeff ffff
+		//
+		// a = bitrate       = anything
+		// b = IS            = anything
+		// c = MS            = anything
+		// d = streamversion = 0000000004 or 0000000005 or 0000000006
+		// e = maxband       = anything
+		// f = blocksize     = 000001 for SV5+, anything(?) for SV4
+
+		$thisfile_mpc_header['target_bitrate']       =        (($HeaderDWORD[0] & 0xFF800000) >> 23);
+		$thisfile_mpc_header['intensity_stereo']     = (bool) (($HeaderDWORD[0] & 0x00400000) >> 22);
+		$thisfile_mpc_header['mid_side_stereo']      = (bool) (($HeaderDWORD[0] & 0x00200000) >> 21);
+		$thisfile_mpc_header['stream_version_major'] =         ($HeaderDWORD[0] & 0x001FF800) >> 11;
+		$thisfile_mpc_header['stream_version_minor'] = 0; // no sub-version numbers before SV7
+		$thisfile_mpc_header['max_band']             =         ($HeaderDWORD[0] & 0x000007C0) >>  6;  // related to lowpass frequency, not sure how it translates exactly
+		$thisfile_mpc_header['block_size']           =         ($HeaderDWORD[0] & 0x0000003F);
+
+		switch ($thisfile_mpc_header['stream_version_major']) {
+			case 4:
+				$thisfile_mpc_header['frame_count'] = ($HeaderDWORD[1] >> 16);
+				break;
+
+			case 5:
+			case 6:
+				$thisfile_mpc_header['frame_count'] =  $HeaderDWORD[1];
+				break;
+
+			default:
+				$ThisFileInfo['error'] = 'Expecting 4, 5 or 6 in version field, found '.$thisfile_mpc_header['stream_version_major'].' instead';
+				unset($ThisFileInfo['mpc']);
+				return false;
+				break;
+		}
+
+		if (($thisfile_mpc_header['stream_version_major'] > 4) && ($thisfile_mpc_header['block_size'] != 1)) {
+			$ThisFileInfo['warning'][] = 'Block size expected to be 1, actual value found: '.$thisfile_mpc_header['block_size'];
+		}
+
+		$thisfile_mpc_header['sample_rate']   = 44100; // AB: used by all files up to SV7
+		$ThisFileInfo['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
+		$thisfile_mpc_header['samples']       = $thisfile_mpc_header['frame_count'] * 1152 * $ThisFileInfo['audio']['channels'];
+
+		if ($thisfile_mpc_header['target_bitrate'] == 0) {
+			$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+		} else {
+			$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
+		}
+
+		$ThisFileInfo['mpc']['bitrate']   = ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
+		$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['mpc']['bitrate'];
+		$ThisFileInfo['audio']['encoder'] = 'SV'.$thisfile_mpc_header['stream_version_major'];
+
+		return true;
+	}
+
+
+	function MPCprofileNameLookup($profileid) {
+		static $MPCprofileNameLookup = array(
+			0  => 'no profile',
+			1  => 'Experimental',
+			2  => 'unused',
+			3  => 'unused',
+			4  => 'unused',
+			5  => 'below Telephone (q = 0.0)',
+			6  => 'below Telephone (q = 1.0)',
+			7  => 'Telephone (q = 2.0)',
+			8  => 'Thumb (q = 3.0)',
+			9  => 'Radio (q = 4.0)',
+			10 => 'Standard (q = 5.0)',
+			11 => 'Extreme (q = 6.0)',
+			12 => 'Insane (q = 7.0)',
+			13 => 'BrainDead (q = 8.0)',
+			14 => 'above BrainDead (q = 9.0)',
+			15 => 'above BrainDead (q = 10.0)'
+		);
+		return (isset($MPCprofileNameLookup[$profileid]) ? $MPCprofileNameLookup[$profileid] : 'invalid');
+	}
+
+	function MPCfrequencyLookup($frequencyid) {
+		static $MPCfrequencyLookup = array(
+			0 => 44100,
+			1 => 48000,
+			2 => 37800,
+			3 => 32000
+		);
+		return (isset($MPCfrequencyLookup[$frequencyid]) ? $MPCfrequencyLookup[$frequencyid] : 'invalid');
+	}
+
+	function MPCpeakDBLookup($intvalue) {
+		if ($intvalue > 0) {
+			return ((log10($intvalue) / log10(2)) - 15) * 6;
+		}
+		return false;
+	}
+
+	function MPCencoderVersionLookup($encoderversion) {
+		//Encoder version * 100  (106 = 1.06)
+		//EncoderVersion % 10 == 0        Release (1.0)
+		//EncoderVersion %  2 == 0        Beta (1.06)
+		//EncoderVersion %  2 == 1        Alpha (1.05a...z)
+
+		if ($encoderversion == 0) {
+			// very old version, not known exactly which
+			return 'Buschmann v1.7.0-v1.7.9 or Klemm v0.90-v1.05';
+		}
+
+		if (($encoderversion % 10) == 0) {
+
+			// release version
+			return number_format($encoderversion / 100, 2);
+
+		} elseif (($encoderversion % 2) == 0) {
+
+			// beta version
+			return number_format($encoderversion / 100, 2).' beta';
+
+		}
+
+		// alpha version
+		return number_format($encoderversion / 100, 2).' alpha';
+	}
+
+	function SV8variableLengthInteger($data, &$packetLength, $maxHandledPacketLength=9) {
+		$packet_size = 0;
+		for ($packetLength = 1; $packetLength <= $maxHandledPacketLength; $packetLength++) {
+			// variable-length size field:
+			//  bits, big-endian
+			//  0xxx xxxx                                           - value 0 to  2^7-1
+			//  1xxx xxxx  0xxx xxxx                                - value 0 to 2^14-1
+			//  1xxx xxxx  1xxx xxxx  0xxx xxxx                     - value 0 to 2^21-1
+			//  1xxx xxxx  1xxx xxxx  1xxx xxxx  0xxx xxxx          - value 0 to 2^28-1
+			//  ...
+			$thisbyte = ord(substr($data, ($packetLength - 1), 1));
+			// look through bytes until find a byte with MSB==0
+			$packet_size = ($packet_size << 7);
+			$packet_size = ($packet_size | ($thisbyte & 0x7F));
+			if (($thisbyte & 0x80) === 0) {
+				break;
+			}
+			if ($packetLength >= $maxHandledPacketLength) {
+				return false;
+			}
+		}
+		return $packet_size;
+	}
+
+	function MPCsv8PacketName($packetKey) {
+		static $MPCsv8PacketName = array();
+		if (empty($MPCsv8PacketName)) {
+			$MPCsv8PacketName = array(
+				'AP' => 'Audio Packet',
+				'CT' => 'Chapter Tag',
+				'EI' => 'Encoder Info',
+				'RG' => 'Replay Gain',
+				'SE' => 'Stream End',
+				'SH' => 'Stream Header',
+				'SO' => 'Seek Table Offset',
+				'ST' => 'Seek Table',
+			);
+		}
+		return (isset($MPCsv8PacketName[$packetKey]) ? $MPCsv8PacketName[$packetKey] : $packetKey);
+	}
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.ogg.php b/apps/media/getID3/getid3/module.audio.ogg.php
new file mode 100644
index 0000000000000000000000000000000000000000..1cc6366ac5ce6c31d3f8de7b8857c5a590e945cf
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.ogg.php
@@ -0,0 +1,556 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.ogg.php                                        //
+// module for analyzing Ogg Vorbis, OggFLAC and Speex files    //
+// dependencies: module.audio.flac.php                         //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.flac.php', __FILE__, true);
+
+class getid3_ogg
+{
+
+	function getid3_ogg(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat'] = 'ogg';
+
+		// Warn about illegal tags - only vorbiscomments are allowed
+		if (isset($ThisFileInfo['id3v2'])) {
+			$ThisFileInfo['warning'][] = 'Illegal ID3v2 tag present.';
+		}
+		if (isset($ThisFileInfo['id3v1'])) {
+			$ThisFileInfo['warning'][] = 'Illegal ID3v1 tag present.';
+		}
+		if (isset($ThisFileInfo['ape'])) {
+			$ThisFileInfo['warning'][] = 'Illegal APE tag present.';
+		}
+
+
+		// Page 1 - Stream Header
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
+		$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
+
+		if (ftell($fd) >= GETID3_FREAD_BUFFER_SIZE) {
+			$ThisFileInfo['error'][] = 'Could not find start of Ogg page in the first '.GETID3_FREAD_BUFFER_SIZE.' bytes (this might not be an Ogg-Vorbis file?)';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['ogg']);
+			return false;
+		}
+
+		$filedata = fread($fd, $oggpageinfo['page_length']);
+		$filedataoffset = 0;
+
+		if (substr($filedata, 0, 4) == 'fLaC') {
+
+			$ThisFileInfo['audio']['dataformat']   = 'flac';
+			$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+			$ThisFileInfo['audio']['lossless']     = true;
+
+		} elseif (substr($filedata, 1, 6) == 'vorbis') {
+
+			$this->ParseVorbisPageHeader($filedata, $filedataoffset, $ThisFileInfo, $oggpageinfo);
+
+		} elseif (substr($filedata, 0, 8) == 'Speex   ') {
+
+			// http://www.speex.org/manual/node10.html
+
+			$ThisFileInfo['audio']['dataformat']   = 'speex';
+			$ThisFileInfo['mime_type']             = 'audio/speex';
+			$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
+			$ThisFileInfo['audio']['lossless']     = false;
+
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_string']           =                              substr($filedata, $filedataoffset, 8); // hard-coded to 'Speex   '
+			$filedataoffset += 8;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']          =                              substr($filedata, $filedataoffset, 20);
+			$filedataoffset += 20;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version_id']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['header_size']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']                   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode_bitstream_version'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels']            = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['bitrate']                = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['framesize']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr']                    = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['frames_per_packet']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['extra_headers']          = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved1']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+			$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['reserved2']              = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+			$filedataoffset += 4;
+
+			$ThisFileInfo['speex']['speex_version'] = trim($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['speex_version']);
+			$ThisFileInfo['speex']['sample_rate']   = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['rate'];
+			$ThisFileInfo['speex']['channels']      = $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['nb_channels'];
+			$ThisFileInfo['speex']['vbr']           = (bool) $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['vbr'];
+			$ThisFileInfo['speex']['band_type']     = getid3_ogg::SpeexBandModeLookup($ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['mode']);
+
+			$ThisFileInfo['audio']['sample_rate']   = $ThisFileInfo['speex']['sample_rate'];
+			$ThisFileInfo['audio']['channels']      = $ThisFileInfo['speex']['channels'];
+			if ($ThisFileInfo['speex']['vbr']) {
+				$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+			}
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Expecting either "Speex   " or "vorbis" identifier strings, found neither';
+			unset($ThisFileInfo['ogg']);
+			unset($ThisFileInfo['mime_type']);
+			return false;
+
+		}
+
+
+		// Page 2 - Comment Header
+
+		$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
+		$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
+
+		switch ($ThisFileInfo['audio']['dataformat']) {
+
+			case 'vorbis':
+				$filedata = fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
+				$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, 0, 1));
+				$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] =                              substr($filedata, 1, 6); // hard-coded to 'vorbis'
+
+				getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
+				break;
+
+			case 'flac':
+				if (!getid3_flac::FLACparseMETAdata($fd, $ThisFileInfo)) {
+					$ThisFileInfo['error'][] = 'Failed to parse FLAC headers';
+					return false;
+				}
+				break;
+
+			case 'speex':
+				fseek($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length'], SEEK_CUR);
+				getid3_ogg::ParseVorbisCommentsFilepointer($fd, $ThisFileInfo);
+				break;
+
+		}
+
+
+
+		// Last Page - Number of Samples
+
+		if ($ThisFileInfo['avdataend'] >= pow(2, 31)) {
+
+			$ThisFileInfo['warning'][] = 'Unable to parse Ogg end chunk file (PHP does not support file operations beyond 2GB)';
+
+		} else {
+
+			fseek($fd, max($ThisFileInfo['avdataend'] - GETID3_FREAD_BUFFER_SIZE, 0), SEEK_SET);
+			$LastChunkOfOgg = strrev(fread($fd, GETID3_FREAD_BUFFER_SIZE));
+			if ($LastOggSpostion = strpos($LastChunkOfOgg, 'SggO')) {
+				fseek($fd, $ThisFileInfo['avdataend'] - ($LastOggSpostion + strlen('SggO')), SEEK_SET);
+				$ThisFileInfo['avdataend'] = ftell($fd);
+				$ThisFileInfo['ogg']['pageheader']['eos'] = getid3_ogg::ParseOggPageHeader($fd);
+				$ThisFileInfo['ogg']['samples']   = $ThisFileInfo['ogg']['pageheader']['eos']['pcm_abs_position'];
+				if ($ThisFileInfo['ogg']['samples'] == 0) {
+					$ThisFileInfo['error'][] = 'Corrupt Ogg file: eos.number of samples == zero';
+					return false;
+				}
+				$ThisFileInfo['ogg']['bitrate_average'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($ThisFileInfo['ogg']['samples'] / $ThisFileInfo['audio']['sample_rate']);
+			}
+
+		}
+
+		if (!empty($ThisFileInfo['ogg']['bitrate_average'])) {
+			$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_average'];
+		} elseif (!empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
+			$ThisFileInfo['audio']['bitrate'] = $ThisFileInfo['ogg']['bitrate_nominal'];
+		} elseif (!empty($ThisFileInfo['ogg']['bitrate_min']) && !empty($ThisFileInfo['ogg']['bitrate_max'])) {
+			$ThisFileInfo['audio']['bitrate'] = ($ThisFileInfo['ogg']['bitrate_min'] + $ThisFileInfo['ogg']['bitrate_max']) / 2;
+		}
+		if (isset($ThisFileInfo['audio']['bitrate']) && !isset($ThisFileInfo['playtime_seconds'])) {
+			if ($ThisFileInfo['audio']['bitrate'] == 0) {
+				$ThisFileInfo['error'][] = 'Corrupt Ogg file: bitrate_audio == zero';
+				return false;
+			}
+			$ThisFileInfo['playtime_seconds'] = (float) ((($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate']);
+		}
+
+		if (isset($ThisFileInfo['ogg']['vendor'])) {
+			$ThisFileInfo['audio']['encoder'] = preg_replace('/^Encoded with /', '', $ThisFileInfo['ogg']['vendor']);
+
+			// Vorbis only
+			if ($ThisFileInfo['audio']['dataformat'] == 'vorbis') {
+
+				// Vorbis 1.0 starts with Xiph.Org
+				if  (preg_match('/^Xiph.Org/', $ThisFileInfo['audio']['encoder'])) {
+
+					if ($ThisFileInfo['audio']['bitrate_mode'] == 'abr') {
+
+						// Set -b 128 on abr files
+						$ThisFileInfo['audio']['encoder_options'] = '-b '.round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000);
+
+					} elseif (($ThisFileInfo['audio']['bitrate_mode'] == 'vbr') && ($ThisFileInfo['audio']['channels'] == 2) && ($ThisFileInfo['audio']['sample_rate'] >= 44100) && ($ThisFileInfo['audio']['sample_rate'] <= 48000)) {
+						// Set -q N on vbr files
+						$ThisFileInfo['audio']['encoder_options'] = '-q '.$this->get_quality_from_nominal_bitrate($ThisFileInfo['ogg']['bitrate_nominal']);
+
+					}
+				}
+
+				if (empty($ThisFileInfo['audio']['encoder_options']) && !empty($ThisFileInfo['ogg']['bitrate_nominal'])) {
+					$ThisFileInfo['audio']['encoder_options'] = 'Nominal bitrate: '.intval(round($ThisFileInfo['ogg']['bitrate_nominal'] / 1000)).'kbps';
+				}
+			}
+		}
+
+		return true;
+	}
+
+	function ParseVorbisPageHeader(&$filedata, &$filedataoffset, &$ThisFileInfo, &$oggpageinfo) {
+		$ThisFileInfo['audio']['dataformat'] = 'vorbis';
+		$ThisFileInfo['audio']['lossless']   = false;
+
+		$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['packet_type'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
+		$filedataoffset += 1;
+		$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['stream_type'] = substr($filedata, $filedataoffset, 6); // hard-coded to 'vorbis'
+		$filedataoffset += 6;
+		$ThisFileInfo['ogg']['bitstreamversion'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$ThisFileInfo['ogg']['numberofchannels'] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
+		$filedataoffset += 1;
+		$ThisFileInfo['audio']['channels']       = $ThisFileInfo['ogg']['numberofchannels'];
+		$ThisFileInfo['ogg']['samplerate']       = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		if ($ThisFileInfo['ogg']['samplerate'] == 0) {
+			$ThisFileInfo['error'][] = 'Corrupt Ogg file: sample rate == zero';
+			return false;
+		}
+		$ThisFileInfo['audio']['sample_rate']    = $ThisFileInfo['ogg']['samplerate'];
+		$ThisFileInfo['ogg']['samples']          = 0; // filled in later
+		$ThisFileInfo['ogg']['bitrate_average']  = 0; // filled in later
+		$ThisFileInfo['ogg']['bitrate_max']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$ThisFileInfo['ogg']['bitrate_nominal']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$ThisFileInfo['ogg']['bitrate_min']      = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$ThisFileInfo['ogg']['blocksize_small']  = pow(2,  getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0x0F);
+		$ThisFileInfo['ogg']['blocksize_large']  = pow(2, (getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)) & 0xF0) >> 4);
+		$ThisFileInfo['ogg']['stop_bit']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1)); // must be 1, marks end of packet
+
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr'; // overridden if actually abr
+		if ($ThisFileInfo['ogg']['bitrate_max'] == 0xFFFFFFFF) {
+			unset($ThisFileInfo['ogg']['bitrate_max']);
+			$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
+		}
+		if ($ThisFileInfo['ogg']['bitrate_nominal'] == 0xFFFFFFFF) {
+			unset($ThisFileInfo['ogg']['bitrate_nominal']);
+		}
+		if ($ThisFileInfo['ogg']['bitrate_min'] == 0xFFFFFFFF) {
+			unset($ThisFileInfo['ogg']['bitrate_min']);
+			$ThisFileInfo['audio']['bitrate_mode'] = 'abr';
+		}
+		return true;
+	}
+
+	function ParseOggPageHeader(&$fd) {
+		// http://xiph.org/ogg/vorbis/doc/framing.html
+		$oggheader['page_start_offset'] = ftell($fd); // where we started from in the file
+
+		$filedata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
+		$filedataoffset = 0;
+		while ((substr($filedata, $filedataoffset++, 4) != 'OggS')) {
+			if ((ftell($fd) - $oggheader['page_start_offset']) >= GETID3_FREAD_BUFFER_SIZE) {
+				// should be found before here
+				return false;
+			}
+			if ((($filedataoffset + 28) > strlen($filedata)) || (strlen($filedata) < 28)) {
+				if (feof($fd) || (($filedata .= fread($fd, GETID3_FREAD_BUFFER_SIZE)) === false)) {
+					// get some more data, unless eof, in which case fail
+					return false;
+				}
+			}
+		}
+		$filedataoffset += strlen('OggS') - 1; // page, delimited by 'OggS'
+
+		$oggheader['stream_structver']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
+		$filedataoffset += 1;
+		$oggheader['flags_raw']         = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
+		$filedataoffset += 1;
+		$oggheader['flags']['fresh']    = (bool) ($oggheader['flags_raw'] & 0x01); // fresh packet
+		$oggheader['flags']['bos']      = (bool) ($oggheader['flags_raw'] & 0x02); // first page of logical bitstream (bos)
+		$oggheader['flags']['eos']      = (bool) ($oggheader['flags_raw'] & 0x04); // last page of logical bitstream (eos)
+
+		$oggheader['pcm_abs_position']  = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 8));
+		$filedataoffset += 8;
+		$oggheader['stream_serialno']   = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$oggheader['page_seqno']        = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$oggheader['page_checksum']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 4));
+		$filedataoffset += 4;
+		$oggheader['page_segments']     = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
+		$filedataoffset += 1;
+		$oggheader['page_length'] = 0;
+		for ($i = 0; $i < $oggheader['page_segments']; $i++) {
+			$oggheader['segment_table'][$i] = getid3_lib::LittleEndian2Int(substr($filedata, $filedataoffset, 1));
+			$filedataoffset += 1;
+			$oggheader['page_length'] += $oggheader['segment_table'][$i];
+		}
+		$oggheader['header_end_offset'] = $oggheader['page_start_offset'] + $filedataoffset;
+		$oggheader['page_end_offset']   = $oggheader['header_end_offset'] + $oggheader['page_length'];
+		fseek($fd, $oggheader['header_end_offset'], SEEK_SET);
+
+		return $oggheader;
+	}
+
+
+	function ParseVorbisCommentsFilepointer(&$fd, &$ThisFileInfo) {
+
+		$OriginalOffset = ftell($fd);
+		$CommentStartOffset = $OriginalOffset;
+		$commentdataoffset = 0;
+		$VorbisCommentPage = 1;
+
+		switch ($ThisFileInfo['audio']['dataformat']) {
+			case 'vorbis':
+				$CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
+				fseek($fd, $CommentStartOffset, SEEK_SET);
+				$commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
+				$commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
+
+				$commentdataoffset += (strlen('vorbis') + 1);
+				break;
+
+			case 'flac':
+				fseek($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['offset'] + 4, SEEK_SET);
+				$commentdata = fread($fd, $ThisFileInfo['flac']['VORBIS_COMMENT']['raw']['block_length']);
+				break;
+
+			case 'speex':
+				$CommentStartOffset = $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_start_offset'];  // Second Ogg page, after header block
+				fseek($fd, $CommentStartOffset, SEEK_SET);
+				$commentdataoffset = 27 + $ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage]['page_segments'];
+				$commentdata = fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1) + $commentdataoffset);
+				break;
+
+			default:
+				return false;
+				break;
+		}
+
+		$VendorSize = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
+		$commentdataoffset += 4;
+
+		$ThisFileInfo['ogg']['vendor'] = substr($commentdata, $commentdataoffset, $VendorSize);
+		$commentdataoffset += $VendorSize;
+
+		$CommentsCount = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
+		$commentdataoffset += 4;
+		$ThisFileInfo['avdataoffset'] = $CommentStartOffset + $commentdataoffset;
+
+		$basicfields = array('TITLE', 'ARTIST', 'ALBUM', 'TRACKNUMBER', 'GENRE', 'DATE', 'DESCRIPTION', 'COMMENT');
+		for ($i = 0; $i < $CommentsCount; $i++) {
+
+			$ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] = $CommentStartOffset + $commentdataoffset;
+
+			if (ftell($fd) < ($ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + 4)) {
+				$VorbisCommentPage++;
+
+				$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
+				$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
+
+				// First, save what we haven't read yet
+				$AsYetUnusedData = substr($commentdata, $commentdataoffset);
+
+				// Then take that data off the end
+				$commentdata     = substr($commentdata, 0, $commentdataoffset);
+
+				// Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
+				$commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
+				$commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
+
+				// Finally, stick the unused data back on the end
+				$commentdata .= $AsYetUnusedData;
+
+				//$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
+				$commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
+
+			}
+			$ThisFileInfo['ogg']['comments_raw'][$i]['size']       = getid3_lib::LittleEndian2Int(substr($commentdata, $commentdataoffset, 4));
+
+			// replace avdataoffset with position just after the last vorbiscomment
+			$ThisFileInfo['avdataoffset'] = $ThisFileInfo['ogg']['comments_raw'][$i]['dataoffset'] + $ThisFileInfo['ogg']['comments_raw'][$i]['size'] + 4;
+
+			$commentdataoffset += 4;
+			while ((strlen($commentdata) - $commentdataoffset) < $ThisFileInfo['ogg']['comments_raw'][$i]['size']) {
+				if (($ThisFileInfo['ogg']['comments_raw'][$i]['size'] > $ThisFileInfo['avdataend']) || ($ThisFileInfo['ogg']['comments_raw'][$i]['size'] < 0)) {
+					$ThisFileInfo['error'][] = 'Invalid Ogg comment size (comment #'.$i.', claims to be '.number_format($ThisFileInfo['ogg']['comments_raw'][$i]['size']).' bytes) - aborting reading comments';
+					break 2;
+				}
+
+				$VorbisCommentPage++;
+
+				$oggpageinfo = getid3_ogg::ParseOggPageHeader($fd);
+				$ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']] = $oggpageinfo;
+
+				// First, save what we haven't read yet
+				$AsYetUnusedData = substr($commentdata, $commentdataoffset);
+
+				// Then take that data off the end
+				$commentdata     = substr($commentdata, 0, $commentdataoffset);
+
+				// Add [headerlength] bytes of dummy data for the Ogg Page Header, just to keep absolute offsets correct
+				$commentdata .= str_repeat("\x00", 27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
+				$commentdataoffset += (27 + $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_segments']);
+
+				// Finally, stick the unused data back on the end
+				$commentdata .= $AsYetUnusedData;
+
+				//$commentdata .= fread($fd, $ThisFileInfo['ogg']['pageheader'][$oggpageinfo['page_seqno']]['page_length']);
+				$commentdata .= fread($fd, getid3_ogg::OggPageSegmentLength($ThisFileInfo['ogg']['pageheader'][$VorbisCommentPage], 1));
+
+				//$filebaseoffset += $oggpageinfo['header_end_offset'] - $oggpageinfo['page_start_offset'];
+			}
+			$commentstring = substr($commentdata, $commentdataoffset, $ThisFileInfo['ogg']['comments_raw'][$i]['size']);
+			$commentdataoffset += $ThisFileInfo['ogg']['comments_raw'][$i]['size'];
+
+			if (!$commentstring) {
+
+				// no comment?
+				$ThisFileInfo['warning'][] = 'Blank Ogg comment ['.$i.']';
+
+			} elseif (strstr($commentstring, '=')) {
+
+				$commentexploded = explode('=', $commentstring, 2);
+				$ThisFileInfo['ogg']['comments_raw'][$i]['key']   = strtoupper($commentexploded[0]);
+				$ThisFileInfo['ogg']['comments_raw'][$i]['value'] = @$commentexploded[1];
+				$ThisFileInfo['ogg']['comments_raw'][$i]['data']  = base64_decode($ThisFileInfo['ogg']['comments_raw'][$i]['value']);
+
+				$ThisFileInfo['ogg']['comments'][strtolower($ThisFileInfo['ogg']['comments_raw'][$i]['key'])][] = $ThisFileInfo['ogg']['comments_raw'][$i]['value'];
+
+				$imageinfo = array();
+				$imagechunkcheck = getid3_lib::GetDataImageSize($ThisFileInfo['ogg']['comments_raw'][$i]['data'], $imageinfo);
+				$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] = getid3_lib::image_type_to_mime_type($imagechunkcheck[2]);
+				if (!$ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] || ($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime'] == 'application/octet-stream')) {
+					unset($ThisFileInfo['ogg']['comments_raw'][$i]['image_mime']);
+					unset($ThisFileInfo['ogg']['comments_raw'][$i]['data']);
+				}
+
+			} else {
+
+				$ThisFileInfo['warning'][] = '[known problem with CDex >= v1.40, < v1.50b7] Invalid Ogg comment name/value pair ['.$i.']: '.$commentstring;
+
+			}
+		}
+
+
+		// Replay Gain Adjustment
+		// http://privatewww.essex.ac.uk/~djmrob/replaygain/
+		if (isset($ThisFileInfo['ogg']['comments']) && is_array($ThisFileInfo['ogg']['comments'])) {
+			foreach ($ThisFileInfo['ogg']['comments'] as $index => $commentvalue) {
+				switch ($index) {
+					case 'rg_audiophile':
+					case 'replaygain_album_gain':
+						$ThisFileInfo['replay_gain']['album']['adjustment'] = (double) $commentvalue[0];
+						unset($ThisFileInfo['ogg']['comments'][$index]);
+						break;
+
+					case 'rg_radio':
+					case 'replaygain_track_gain':
+						$ThisFileInfo['replay_gain']['track']['adjustment'] = (double) $commentvalue[0];
+						unset($ThisFileInfo['ogg']['comments'][$index]);
+						break;
+
+					case 'replaygain_album_peak':
+						$ThisFileInfo['replay_gain']['album']['peak'] = (double) $commentvalue[0];
+						unset($ThisFileInfo['ogg']['comments'][$index]);
+						break;
+
+					case 'rg_peak':
+					case 'replaygain_track_peak':
+						$ThisFileInfo['replay_gain']['track']['peak'] = (double) $commentvalue[0];
+						unset($ThisFileInfo['ogg']['comments'][$index]);
+						break;
+
+
+					default:
+						// do nothing
+						break;
+				}
+			}
+		}
+
+		fseek($fd, $OriginalOffset, SEEK_SET);
+
+		return true;
+	}
+
+	function SpeexBandModeLookup($mode) {
+		static $SpeexBandModeLookup = array();
+		if (empty($SpeexBandModeLookup)) {
+			$SpeexBandModeLookup[0] = 'narrow';
+			$SpeexBandModeLookup[1] = 'wide';
+			$SpeexBandModeLookup[2] = 'ultra-wide';
+		}
+		return (isset($SpeexBandModeLookup[$mode]) ? $SpeexBandModeLookup[$mode] : null);
+	}
+
+
+	function OggPageSegmentLength($OggInfoArray, $SegmentNumber=1) {
+		for ($i = 0; $i < $SegmentNumber; $i++) {
+			$segmentlength = 0;
+			foreach ($OggInfoArray['segment_table'] as $key => $value) {
+				$segmentlength += $value;
+				if ($value < 255) {
+					break;
+				}
+			}
+		}
+		return $segmentlength;
+	}
+
+
+	function get_quality_from_nominal_bitrate($nominal_bitrate) {
+
+		// decrease precision
+		$nominal_bitrate = $nominal_bitrate / 1000;
+
+		if ($nominal_bitrate < 128) {
+			// q-1 to q4
+			$qval = ($nominal_bitrate - 64) / 16;
+		} elseif ($nominal_bitrate < 256) {
+			// q4 to q8
+			$qval = $nominal_bitrate / 32;
+		} elseif ($nominal_bitrate < 320) {
+			// q8 to q9
+			$qval = ($nominal_bitrate + 256) / 64;
+		} else {
+			// q9 to q10
+			$qval = ($nominal_bitrate + 1300) / 180;
+		}
+		//return $qval; // 5.031324
+		//return intval($qval); // 5
+		return round($qval, 1); // 5 or 4.9
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.optimfrog.php b/apps/media/getID3/getid3/module.audio.optimfrog.php
new file mode 100644
index 0000000000000000000000000000000000000000..3c2dfb0bd2de1aa042ba9f8895a1c2248b273ab6
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.optimfrog.php
@@ -0,0 +1,408 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.optimfrog.php                                  //
+// module for analyzing OptimFROG audio files                  //
+// dependencies: module.audio.riff.php                         //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+class getid3_optimfrog
+{
+
+	function getid3_optimfrog(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']            = 'ofr';
+		$ThisFileInfo['audio']['dataformat']   = 'ofr';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+		$ThisFileInfo['audio']['lossless']     = true;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$OFRheader  = fread($fd, 8);
+		if (substr($OFRheader, 0, 5) == '*RIFF') {
+
+			return $this->ParseOptimFROGheader42($fd, $ThisFileInfo);
+
+		} elseif (substr($OFRheader, 0, 3) == 'OFR') {
+
+			return $this->ParseOptimFROGheader45($fd, $ThisFileInfo);
+
+		}
+
+		$ThisFileInfo['error'][] = 'Expecting "*RIFF" or "OFR " at offset '.$ThisFileInfo['avdataoffset'].', found "'.$OFRheader.'"';
+		unset($ThisFileInfo['fileformat']);
+		return false;
+	}
+
+
+	function ParseOptimFROGheader42(&$fd, &$ThisFileInfo) {
+		// for fileformat of v4.21 and older
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$OptimFROGheaderData = fread($fd, 45);
+		$ThisFileInfo['avdataoffset'] = 45;
+
+		$OptimFROGencoderVersion_raw   = getid3_lib::LittleEndian2Int(substr($OptimFROGheaderData, 0, 1));
+		$OptimFROGencoderVersion_major = floor($OptimFROGencoderVersion_raw / 10);
+		$OptimFROGencoderVersion_minor = $OptimFROGencoderVersion_raw - ($OptimFROGencoderVersion_major * 10);
+		$RIFFdata                = substr($OptimFROGheaderData, 1, 44);
+		$OrignalRIFFheaderSize   = getid3_lib::LittleEndian2Int(substr($RIFFdata,  4, 4)) +  8;
+		$OrignalRIFFdataSize     = getid3_lib::LittleEndian2Int(substr($RIFFdata, 40, 4)) + 44;
+
+		if ($OrignalRIFFheaderSize > $OrignalRIFFdataSize) {
+			$ThisFileInfo['avdataend'] -= ($OrignalRIFFheaderSize - $OrignalRIFFdataSize);
+			fseek($fd, $ThisFileInfo['avdataend'], SEEK_SET);
+			$RIFFdata .= fread($fd, $OrignalRIFFheaderSize - $OrignalRIFFdataSize);
+		}
+
+		// move the data chunk after all other chunks (if any)
+		// so that the RIFF parser doesn't see EOF when trying
+		// to skip over the data chunk
+		$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
+		getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
+
+		$ThisFileInfo['audio']['encoder']         = 'OptimFROG '.$OptimFROGencoderVersion_major.'.'.$OptimFROGencoderVersion_minor;
+		$ThisFileInfo['audio']['channels']        = $ThisFileInfo['riff']['audio'][0]['channels'];
+		$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['riff']['audio'][0]['sample_rate'];
+		$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['riff']['audio'][0]['bits_per_sample'];
+		$ThisFileInfo['playtime_seconds']         = $OrignalRIFFdataSize / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate'] * ($ThisFileInfo['audio']['bits_per_sample'] / 8));
+		$ThisFileInfo['audio']['bitrate']         = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+	}
+
+
+	function ParseOptimFROGheader45(&$fd, &$ThisFileInfo) {
+		// for fileformat of v4.50a and higher
+
+		$RIFFdata = '';
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		while (!feof($fd) && (ftell($fd) < $ThisFileInfo['avdataend'])) {
+			$BlockOffset = ftell($fd);
+			$BlockData   = fread($fd, 8);
+			$offset      = 8;
+			$BlockName   =                  substr($BlockData, 0, 4);
+			$BlockSize   = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 4));
+
+			if ($BlockName == 'OFRX') {
+				$BlockName = 'OFR ';
+			}
+			if (!isset($ThisFileInfo['ofr'][$BlockName])) {
+				$ThisFileInfo['ofr'][$BlockName] = array();
+			}
+			$thisfile_ofr_thisblock = &$ThisFileInfo['ofr'][$BlockName];
+
+			switch ($BlockName) {
+				case 'OFR ':
+
+					// shortcut
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+
+					$ThisFileInfo['audio']['encoder'] = 'OptimFROG 4.50 alpha';
+					switch ($BlockSize) {
+						case 12:
+						case 15:
+							// good
+							break;
+
+						default:
+							$ThisFileInfo['warning'][] = '"'.$BlockName.'" contains more data than expected (expected 12 or 15 bytes, found '.$BlockSize.' bytes)';
+							break;
+					}
+					$BlockData .= fread($fd, $BlockSize);
+
+					$thisfile_ofr_thisblock['total_samples']      = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 6));
+					$offset += 6;
+					$thisfile_ofr_thisblock['raw']['sample_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
+					$thisfile_ofr_thisblock['sample_type']        = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
+					$offset += 1;
+					$thisfile_ofr_thisblock['channel_config']     = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
+					$thisfile_ofr_thisblock['channels']           = $thisfile_ofr_thisblock['channel_config'];
+					$offset += 1;
+					$thisfile_ofr_thisblock['sample_rate']        = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
+					$offset += 4;
+
+					if ($BlockSize > 12) {
+
+						// OFR 4.504b or higher
+						$thisfile_ofr_thisblock['channels']           = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
+						$thisfile_ofr_thisblock['raw']['encoder_id']  = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
+						$thisfile_ofr_thisblock['encoder']            = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
+						$offset += 2;
+						$thisfile_ofr_thisblock['raw']['compression'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
+						$thisfile_ofr_thisblock['compression']        = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
+						$thisfile_ofr_thisblock['speedup']            = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
+						$offset += 1;
+
+						$ThisFileInfo['audio']['encoder']         = 'OptimFROG '.$thisfile_ofr_thisblock['encoder'];
+						$ThisFileInfo['audio']['encoder_options'] = '--mode '.$thisfile_ofr_thisblock['compression'];
+
+						if ((($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xF0) >> 4) == 7) { // v4.507
+							if (strtolower(getid3_lib::fileextension($ThisFileInfo['filename'])) == 'ofs') {
+								// OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
+								// between lossless and lossy other than the file extension.
+								$ThisFileInfo['audio']['dataformat']   = 'ofs';
+								$ThisFileInfo['audio']['lossless']     = true;
+							}
+						}
+
+					}
+
+					$ThisFileInfo['audio']['channels']        = $thisfile_ofr_thisblock['channels'];
+					$ThisFileInfo['audio']['sample_rate']     = $thisfile_ofr_thisblock['sample_rate'];
+					$ThisFileInfo['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
+					break;
+
+
+				case 'COMP':
+					// unlike other block types, there CAN be multiple COMP blocks
+
+					$COMPdata['offset'] = $BlockOffset;
+					$COMPdata['size']   = $BlockSize;
+
+					if ($ThisFileInfo['avdataoffset'] == 0) {
+						$ThisFileInfo['avdataoffset'] = $BlockOffset;
+					}
+
+					// Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
+					$BlockData .= fread($fd, 14);
+					fseek($fd, $BlockSize - 14, SEEK_CUR);
+
+					$COMPdata['crc_32']                       = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
+					$offset += 4;
+					$COMPdata['sample_count']                 = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 4));
+					$offset += 4;
+					$COMPdata['raw']['sample_type']           = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
+					$COMPdata['sample_type']                  = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
+					$offset += 1;
+					$COMPdata['raw']['channel_configuration'] = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 1));
+					$COMPdata['channel_configuration']        = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
+					$offset += 1;
+					$COMPdata['raw']['algorithm_id']          = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
+					//$COMPdata['algorithm']                    = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
+					$offset += 2;
+
+					if ($ThisFileInfo['ofr']['OFR ']['size'] > 12) {
+
+						// OFR 4.504b or higher
+						$COMPdata['raw']['encoder_id']        = getid3_lib::LittleEndian2Int(substr($BlockData, $offset, 2));
+						$COMPdata['encoder']                  = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
+						$offset += 2;
+
+					}
+
+					if ($COMPdata['crc_32'] == 0x454E4F4E) {
+						// ASCII value of 'NONE' - placeholder value in v4.50a
+						$COMPdata['crc_32'] = false;
+					}
+
+					$thisfile_ofr_thisblock[] = $COMPdata;
+					break;
+
+				case 'HEAD':
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+
+					$RIFFdata .= fread($fd, $BlockSize);
+					break;
+
+				case 'TAIL':
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+
+					if ($BlockSize > 0) {
+						$RIFFdata .= fread($fd, $BlockSize);
+					}
+					break;
+
+				case 'RECV':
+					// block contains no useful meta data - simply note and skip
+
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+
+					fseek($fd, $BlockSize, SEEK_CUR);
+					break;
+
+
+				case 'APET':
+					// APEtag v2
+
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+					$ThisFileInfo['warning'][] = 'APEtag processing inside OptimFROG not supported in this version ('.GETID3_VERSION.') of getID3()';
+
+					fseek($fd, $BlockSize, SEEK_CUR);
+					break;
+
+
+				case 'MD5 ':
+					// APEtag v2
+
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+
+					if ($BlockSize == 16) {
+
+						$thisfile_ofr_thisblock['md5_binary'] = fread($fd, $BlockSize);
+						$thisfile_ofr_thisblock['md5_string'] = getid3_lib::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
+						$ThisFileInfo['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
+
+					} else {
+
+						$ThisFileInfo['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found '.$BlockSize.' instead';
+						fseek($fd, $BlockSize, SEEK_CUR);
+
+					}
+					break;
+
+
+				default:
+					$thisfile_ofr_thisblock['offset'] = $BlockOffset;
+					$thisfile_ofr_thisblock['size']   = $BlockSize;
+
+					$ThisFileInfo['warning'][] = 'Unhandled OptimFROG block type "'.$BlockName.'" at offset '.$thisfile_ofr_thisblock['offset'];
+					fseek($fd, $BlockSize, SEEK_CUR);
+					break;
+			}
+		}
+		if (isset($ThisFileInfo['ofr']['TAIL']['offset'])) {
+			$ThisFileInfo['avdataend'] = $ThisFileInfo['ofr']['TAIL']['offset'];
+		}
+
+		$ThisFileInfo['playtime_seconds'] = (float) $ThisFileInfo['ofr']['OFR ']['total_samples'] / ($ThisFileInfo['audio']['channels'] * $ThisFileInfo['audio']['sample_rate']);
+		$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		// move the data chunk after all other chunks (if any)
+		// so that the RIFF parser doesn't see EOF when trying
+		// to skip over the data chunk
+		$RIFFdata = substr($RIFFdata, 0, 36).substr($RIFFdata, 44).substr($RIFFdata, 36, 8);
+		getid3_riff::ParseRIFFdata($RIFFdata, $ThisFileInfo);
+
+		return true;
+	}
+
+
+	function OptimFROGsampleTypeLookup($SampleType) {
+		static $OptimFROGsampleTypeLookup = array(
+			0  => 'unsigned int (8-bit)',
+			1  => 'signed int (8-bit)',
+			2  => 'unsigned int (16-bit)',
+			3  => 'signed int (16-bit)',
+			4  => 'unsigned int (24-bit)',
+			5  => 'signed int (24-bit)',
+			6  => 'unsigned int (32-bit)',
+			7  => 'signed int (32-bit)',
+			8  => 'float 0.24 (32-bit)',
+			9  => 'float 16.8 (32-bit)',
+			10 => 'float 24.0 (32-bit)'
+		);
+		return (isset($OptimFROGsampleTypeLookup[$SampleType]) ? $OptimFROGsampleTypeLookup[$SampleType] : false);
+	}
+
+	function OptimFROGbitsPerSampleTypeLookup($SampleType) {
+		static $OptimFROGbitsPerSampleTypeLookup = array(
+			0  => 8,
+			1  => 8,
+			2  => 16,
+			3  => 16,
+			4  => 24,
+			5  => 24,
+			6  => 32,
+			7  => 32,
+			8  => 32,
+			9  => 32,
+			10 => 32
+		);
+		return (isset($OptimFROGbitsPerSampleTypeLookup[$SampleType]) ? $OptimFROGbitsPerSampleTypeLookup[$SampleType] : false);
+	}
+
+	function OptimFROGchannelConfigurationLookup($ChannelConfiguration) {
+		static $OptimFROGchannelConfigurationLookup = array(
+			0 => 'mono',
+			1 => 'stereo'
+		);
+		return (isset($OptimFROGchannelConfigurationLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigurationLookup[$ChannelConfiguration] : false);
+	}
+
+	function OptimFROGchannelConfigNumChannelsLookup($ChannelConfiguration) {
+		static $OptimFROGchannelConfigNumChannelsLookup = array(
+			0 => 1,
+			1 => 2
+		);
+		return (isset($OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration]) ? $OptimFROGchannelConfigNumChannelsLookup[$ChannelConfiguration] : false);
+	}
+
+
+
+	// function OptimFROGalgorithmNameLookup($AlgorithID) {
+	//     static $OptimFROGalgorithmNameLookup = array();
+	//     return (isset($OptimFROGalgorithmNameLookup[$AlgorithID]) ? $OptimFROGalgorithmNameLookup[$AlgorithID] : false);
+	// }
+
+
+	function OptimFROGencoderNameLookup($EncoderID) {
+		// version = (encoderID >> 4) + 4500
+		// system  =  encoderID & 0xF
+
+		$EncoderVersion  = number_format(((($EncoderID & 0xF0) >> 4) + 4500) / 1000, 3);
+		$EncoderSystemID = ($EncoderID & 0x0F);
+
+		static $OptimFROGencoderSystemLookup = array(
+			0x00 => 'Windows console',
+			0x01 => 'Linux console',
+			0x0F => 'unknown'
+		);
+		return $EncoderVersion.' ('.(isset($OptimFROGencoderSystemLookup[$EncoderSystemID]) ? $OptimFROGencoderSystemLookup[$EncoderSystemID] : 'undefined encoder type (0x'.dechex($EncoderSystemID).')').')';
+	}
+
+	function OptimFROGcompressionLookup($CompressionID) {
+		// mode    = compression >> 3
+		// speedup = compression & 0x07
+
+		$CompressionModeID    = ($CompressionID & 0xF8) >> 3;
+		//$CompressionSpeedupID = ($CompressionID & 0x07);
+
+		static $OptimFROGencoderModeLookup = array(
+			0x00 => 'fast',
+			0x01 => 'normal',
+			0x02 => 'high',
+			0x03 => 'extra', // extranew (some versions)
+			0x04 => 'best',  // bestnew (some versions)
+			0x05 => 'ultra',
+			0x06 => 'insane',
+			0x07 => 'highnew',
+			0x08 => 'extranew',
+			0x09 => 'bestnew'
+		);
+		return (isset($OptimFROGencoderModeLookup[$CompressionModeID]) ? $OptimFROGencoderModeLookup[$CompressionModeID] : 'undefined mode (0x'.str_pad(dechex($CompressionModeID), 2, '0', STR_PAD_LEFT).')');
+	}
+
+	function OptimFROGspeedupLookup($CompressionID) {
+		// mode    = compression >> 3
+		// speedup = compression & 0x07
+
+		//$CompressionModeID    = ($CompressionID & 0xF8) >> 3;
+		$CompressionSpeedupID = ($CompressionID & 0x07);
+
+		static $OptimFROGencoderSpeedupLookup = array(
+			0x00 => '1x',
+			0x01 => '2x',
+			0x02 => '4x'
+		);
+
+		return (isset($OptimFROGencoderSpeedupLookup[$CompressionSpeedupID]) ? $OptimFROGencoderSpeedupLookup[$CompressionSpeedupID] : 'undefined mode (0x'.dechex($CompressionSpeedupID));
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.rkau.php b/apps/media/getID3/getid3/module.audio.rkau.php
new file mode 100644
index 0000000000000000000000000000000000000000..0e344d43e65e880df202931c3384393fd0bc0172
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.rkau.php
@@ -0,0 +1,92 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.shorten.php                                    //
+// module for analyzing Shorten Audio files                    //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_rkau
+{
+
+	function getid3_rkau(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$RKAUHeader = fread($fd, 20);
+		if (substr($RKAUHeader, 0, 3) != 'RKA') {
+			$ThisFileInfo['error'][] = 'Expecting "RKA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($RKAUHeader, 0, 3).'"';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat']            = 'rkau';
+		$ThisFileInfo['audio']['dataformat']   = 'rkau';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+
+		$ThisFileInfo['rkau']['raw']['version']   = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 3, 1));
+		$ThisFileInfo['rkau']['version']          = '1.'.str_pad($ThisFileInfo['rkau']['raw']['version'] & 0x0F, 2, '0', STR_PAD_LEFT);
+		if (($ThisFileInfo['rkau']['version'] > 1.07) || ($ThisFileInfo['rkau']['version'] < 1.06)) {
+			$ThisFileInfo['error'][] = 'This version of getID3() can only parse RKAU files v1.06 and 1.07 (this file is v'.$ThisFileInfo['rkau']['version'].')';
+			unset($ThisFileInfo['rkau']);
+			return false;
+		}
+
+		$ThisFileInfo['rkau']['source_bytes']     = getid3_lib::LittleEndian2Int(substr($RKAUHeader,  4, 4));
+		$ThisFileInfo['rkau']['sample_rate']      = getid3_lib::LittleEndian2Int(substr($RKAUHeader,  8, 4));
+		$ThisFileInfo['rkau']['channels']         = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 12, 1));
+		$ThisFileInfo['rkau']['bits_per_sample']  = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 13, 1));
+
+		$ThisFileInfo['rkau']['raw']['quality']   = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 14, 1));
+		$this->RKAUqualityLookup($ThisFileInfo['rkau']);
+
+		$ThisFileInfo['rkau']['raw']['flags']            = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 15, 1));
+		$ThisFileInfo['rkau']['flags']['joint_stereo']   = (bool) (!($ThisFileInfo['rkau']['raw']['flags'] & 0x01));
+		$ThisFileInfo['rkau']['flags']['streaming']      =  (bool)  ($ThisFileInfo['rkau']['raw']['flags'] & 0x02);
+		$ThisFileInfo['rkau']['flags']['vrq_lossy_mode'] =  (bool)  ($ThisFileInfo['rkau']['raw']['flags'] & 0x04);
+
+		if ($ThisFileInfo['rkau']['flags']['streaming']) {
+			$ThisFileInfo['avdataoffset'] += 20;
+			$ThisFileInfo['rkau']['compressed_bytes']  = getid3_lib::LittleEndian2Int(substr($RKAUHeader, 16, 4));
+		} else {
+			$ThisFileInfo['avdataoffset'] += 16;
+			$ThisFileInfo['rkau']['compressed_bytes'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - 1;
+		}
+		// Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
+		// sometimes it's more, sometimes less. No idea why(?)
+
+		$ThisFileInfo['audio']['lossless']        = $ThisFileInfo['rkau']['lossless'];
+		$ThisFileInfo['audio']['channels']        = $ThisFileInfo['rkau']['channels'];
+		$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['rkau']['bits_per_sample'];
+		$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['rkau']['sample_rate'];
+
+		$ThisFileInfo['playtime_seconds']         = $ThisFileInfo['rkau']['source_bytes'] / ($ThisFileInfo['rkau']['sample_rate'] * $ThisFileInfo['rkau']['channels'] * ($ThisFileInfo['rkau']['bits_per_sample'] / 8));
+		$ThisFileInfo['audio']['bitrate']         = ($ThisFileInfo['rkau']['compressed_bytes'] * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+
+	}
+
+
+	function RKAUqualityLookup(&$RKAUdata) {
+		$level   = ($RKAUdata['raw']['quality'] & 0xF0) >> 4;
+		$quality =  $RKAUdata['raw']['quality'] & 0x0F;
+
+		$RKAUdata['lossless']          = (($quality == 0) ? true : false);
+		$RKAUdata['compression_level'] = $level + 1;
+		if (!$RKAUdata['lossless']) {
+			$RKAUdata['quality_setting'] = $quality;
+		}
+
+		return true;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.shorten.php b/apps/media/getID3/getid3/module.audio.shorten.php
new file mode 100644
index 0000000000000000000000000000000000000000..a9eb1ab1ccd0455054b00b4c36bd0c806561a4e7
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.shorten.php
@@ -0,0 +1,180 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.shorten.php                                    //
+// module for analyzing Shorten Audio files                    //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_shorten
+{
+
+	function getid3_shorten(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$ShortenHeader = fread($fd, 8);
+		if (substr($ShortenHeader, 0, 4) != 'ajkg') {
+			$ThisFileInfo['error'][] = 'Expecting "ajkg" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($ShortenHeader, 0, 4).'"';
+			return false;
+		}
+		$ThisFileInfo['fileformat']            = 'shn';
+		$ThisFileInfo['audio']['dataformat']   = 'shn';
+		$ThisFileInfo['audio']['lossless']     = true;
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+
+		$ThisFileInfo['shn']['version'] = getid3_lib::LittleEndian2Int(substr($ShortenHeader, 4, 1));
+
+		fseek($fd, $ThisFileInfo['avdataend'] - 12, SEEK_SET);
+		$SeekTableSignatureTest = fread($fd, 12);
+		$ThisFileInfo['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
+		if ($ThisFileInfo['shn']['seektable']['present']) {
+			$ThisFileInfo['shn']['seektable']['length'] = getid3_lib::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
+			$ThisFileInfo['shn']['seektable']['offset'] = $ThisFileInfo['avdataend'] - $ThisFileInfo['shn']['seektable']['length'];
+			fseek($fd, $ThisFileInfo['shn']['seektable']['offset'], SEEK_SET);
+			$SeekTableMagic = fread($fd, 4);
+			if ($SeekTableMagic != 'SEEK') {
+
+				$ThisFileInfo['error'][] = 'Expecting "SEEK" at offset '.$ThisFileInfo['shn']['seektable']['offset'].', found "'.$SeekTableMagic.'"';
+				return false;
+
+			} else {
+
+				// typedef struct tag_TSeekEntry
+				// {
+				//   unsigned long SampleNumber;
+				//   unsigned long SHNFileByteOffset;
+				//   unsigned long SHNLastBufferReadPosition;
+				//   unsigned short SHNByteGet;
+				//   unsigned short SHNBufferOffset;
+				//   unsigned short SHNFileBitOffset;
+				//   unsigned long SHNGBuffer;
+				//   unsigned short SHNBitShift;
+				//   long CBuf0[3];
+				//   long CBuf1[3];
+				//   long Offset0[4];
+				//   long Offset1[4];
+				// }TSeekEntry;
+
+				$SeekTableData = fread($fd, $ThisFileInfo['shn']['seektable']['length'] - 16);
+				$ThisFileInfo['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
+				//$ThisFileInfo['shn']['seektable']['entries'] = array();
+				//$SeekTableOffset = 0;
+				//for ($i = 0; $i < $ThisFileInfo['shn']['seektable']['entry_count']; $i++) {
+				//	$SeekTableEntry['sample_number'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//	$SeekTableOffset += 4;
+				//	$SeekTableEntry['shn_file_byte_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//	$SeekTableOffset += 4;
+				//	$SeekTableEntry['shn_last_buffer_read_position'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//	$SeekTableOffset += 4;
+				//	$SeekTableEntry['shn_byte_get'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
+				//	$SeekTableOffset += 2;
+				//	$SeekTableEntry['shn_buffer_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
+				//	$SeekTableOffset += 2;
+				//	$SeekTableEntry['shn_file_bit_offset'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
+				//	$SeekTableOffset += 2;
+				//	$SeekTableEntry['shn_gbuffer'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//	$SeekTableOffset += 4;
+				//	$SeekTableEntry['shn_bit_shift'] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
+				//	$SeekTableOffset += 2;
+				//	for ($j = 0; $j < 3; $j++) {
+				//		$SeekTableEntry['cbuf0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//		$SeekTableOffset += 4;
+				//	}
+				//	for ($j = 0; $j < 3; $j++) {
+				//		$SeekTableEntry['cbuf1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//		$SeekTableOffset += 4;
+				//	}
+				//	for ($j = 0; $j < 4; $j++) {
+				//		$SeekTableEntry['offset0'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//		$SeekTableOffset += 4;
+				//	}
+				//	for ($j = 0; $j < 4; $j++) {
+				//		$SeekTableEntry['offset1'][$j] = getid3_lib::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
+				//		$SeekTableOffset += 4;
+				//	}
+                //
+				//	$ThisFileInfo['shn']['seektable']['entries'][] = $SeekTableEntry;
+				//}
+
+			}
+
+		}
+
+		if ((bool) ini_get('safe_mode')) {
+			$ThisFileInfo['error'][] = 'PHP running in Safe Mode - backtick operator not available, cannot run shntool to analyze Shorten files';
+			return false;
+		}
+
+		if (GETID3_OS_ISWINDOWS) {
+
+			$RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe');
+			foreach ($RequiredFiles as $required_file) {
+				if (!is_readable(GETID3_HELPERAPPSDIR.$required_file)) {
+					$ThisFileInfo['error'][] = GETID3_HELPERAPPSDIR.$required_file.' does not exist';
+					return false;
+				}
+			}
+			$commandline = GETID3_HELPERAPPSDIR.'shorten.exe -x "'.$ThisFileInfo['filenamepath'].'" - | '.GETID3_HELPERAPPSDIR.'head.exe -c 64';
+			$commandline = str_replace('/', '\\', $commandline);
+
+		} else {
+
+	        static $shorten_present;
+	        if (!isset($shorten_present)) {
+                $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
+            }
+            if (!$shorten_present) {
+                $ThisFileInfo['error'][] = 'shorten binary was not found in path or /usr/local/bin';
+                return false;
+            }
+            $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '' ) . 'shorten -x '.escapeshellarg($ThisFileInfo['filenamepath']).' - | head -c 64';
+
+		}
+
+		$output = `$commandline`;
+
+		if (!empty($output) && (substr($output, 12, 4) == 'fmt ')) {
+
+			getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+			$fmt_size = getid3_lib::LittleEndian2Int(substr($output, 16, 4));
+			$DecodedWAVFORMATEX = getid3_riff::RIFFparseWAVEFORMATex(substr($output, 20, $fmt_size));
+			$ThisFileInfo['audio']['channels']        = $DecodedWAVFORMATEX['channels'];
+			$ThisFileInfo['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
+			$ThisFileInfo['audio']['sample_rate']     = $DecodedWAVFORMATEX['sample_rate'];
+
+			if (substr($output, 20 + $fmt_size, 4) == 'data') {
+
+				$ThisFileInfo['playtime_seconds'] = getid3_lib::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
+
+			} else {
+
+				$ThisFileInfo['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime';
+				return false;
+
+			}
+
+			$ThisFileInfo['audio']['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) / $ThisFileInfo['playtime_seconds']) * 8;
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'shorten failed to decode file to WAV for parsing';
+			return false;
+
+		}
+
+		return true;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.tta.php b/apps/media/getID3/getid3/module.audio.tta.php
new file mode 100644
index 0000000000000000000000000000000000000000..903de6bf45d85e7d824215f49f91727da44864f9
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.tta.php
@@ -0,0 +1,107 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.tta.php                                        //
+// module for analyzing TTA Audio files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_tta
+{
+
+	function getid3_tta(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat']            = 'tta';
+		$ThisFileInfo['audio']['dataformat']   = 'tta';
+		$ThisFileInfo['audio']['lossless']     = true;
+		$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$ttaheader = fread($fd, 26);
+
+		$ThisFileInfo['tta']['magic'] = substr($ttaheader,  0,  3);
+		if ($ThisFileInfo['tta']['magic'] != 'TTA') {
+			$ThisFileInfo['error'][] = 'Expecting "TTA" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['tta']['magic'].'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['audio']);
+			unset($ThisFileInfo['tta']);
+			return false;
+		}
+
+		switch ($ttaheader{3}) {
+			case "\x01": // TTA v1.x
+			case "\x02": // TTA v1.x
+			case "\x03": // TTA v1.x
+				// "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year."
+				$ThisFileInfo['tta']['major_version'] = 1;
+				$ThisFileInfo['avdataoffset'] += 16;
+
+				$ThisFileInfo['tta']['compression_level']   = ord($ttaheader{3});
+				$ThisFileInfo['tta']['channels']            = getid3_lib::LittleEndian2Int(substr($ttaheader,  4,  2));
+				$ThisFileInfo['tta']['bits_per_sample']     = getid3_lib::LittleEndian2Int(substr($ttaheader,  6,  2));
+				$ThisFileInfo['tta']['sample_rate']         = getid3_lib::LittleEndian2Int(substr($ttaheader,  8,  4));
+				$ThisFileInfo['tta']['samples_per_channel'] = getid3_lib::LittleEndian2Int(substr($ttaheader, 12,  4));
+
+				$ThisFileInfo['audio']['encoder_options']   = '-e'.$ThisFileInfo['tta']['compression_level'];
+				$ThisFileInfo['playtime_seconds']           = $ThisFileInfo['tta']['samples_per_channel'] / $ThisFileInfo['tta']['sample_rate'];
+				break;
+
+			case '2': // TTA v2.x
+				// "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
+				$ThisFileInfo['tta']['major_version'] = 2;
+				$ThisFileInfo['avdataoffset'] += 20;
+
+				$ThisFileInfo['tta']['compression_level']   = getid3_lib::LittleEndian2Int(substr($ttaheader,  4,  2));
+				$ThisFileInfo['tta']['audio_format']        = getid3_lib::LittleEndian2Int(substr($ttaheader,  6,  2));
+				$ThisFileInfo['tta']['channels']            = getid3_lib::LittleEndian2Int(substr($ttaheader,  8,  2));
+				$ThisFileInfo['tta']['bits_per_sample']     = getid3_lib::LittleEndian2Int(substr($ttaheader, 10,  2));
+				$ThisFileInfo['tta']['sample_rate']         = getid3_lib::LittleEndian2Int(substr($ttaheader, 12,  4));
+				$ThisFileInfo['tta']['data_length']         = getid3_lib::LittleEndian2Int(substr($ttaheader, 16,  4));
+
+				$ThisFileInfo['audio']['encoder_options']   = '-e'.$ThisFileInfo['tta']['compression_level'];
+				$ThisFileInfo['playtime_seconds']           = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
+				break;
+
+			case '1': // TTA v3.x
+				// "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
+				$ThisFileInfo['tta']['major_version'] = 3;
+				$ThisFileInfo['avdataoffset'] += 26;
+
+				$ThisFileInfo['tta']['audio_format']        = getid3_lib::LittleEndian2Int(substr($ttaheader,  4,  2)); // getid3_riff::RIFFwFormatTagLookup()
+				$ThisFileInfo['tta']['channels']            = getid3_lib::LittleEndian2Int(substr($ttaheader,  6,  2));
+				$ThisFileInfo['tta']['bits_per_sample']     = getid3_lib::LittleEndian2Int(substr($ttaheader,  8,  2));
+				$ThisFileInfo['tta']['sample_rate']         = getid3_lib::LittleEndian2Int(substr($ttaheader, 10,  4));
+				$ThisFileInfo['tta']['data_length']         = getid3_lib::LittleEndian2Int(substr($ttaheader, 14,  4));
+				$ThisFileInfo['tta']['crc32_footer']        =                              substr($ttaheader, 18,  4);
+				$ThisFileInfo['tta']['seek_point']          = getid3_lib::LittleEndian2Int(substr($ttaheader, 22,  4));
+
+				$ThisFileInfo['playtime_seconds']           = $ThisFileInfo['tta']['data_length'] / $ThisFileInfo['tta']['sample_rate'];
+				break;
+
+			default:
+				$ThisFileInfo['error'][] = 'This version of getID3() only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v'.$ttaheader{3};
+				return false;
+				break;
+		}
+
+		$ThisFileInfo['audio']['encoder']         = 'TTA v'.$ThisFileInfo['tta']['major_version'];
+		$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['tta']['bits_per_sample'];
+		$ThisFileInfo['audio']['sample_rate']     = $ThisFileInfo['tta']['sample_rate'];
+		$ThisFileInfo['audio']['channels']        = $ThisFileInfo['tta']['channels'];
+		$ThisFileInfo['audio']['bitrate']         = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		return true;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.voc.php b/apps/media/getID3/getid3/module.audio.voc.php
new file mode 100644
index 0000000000000000000000000000000000000000..e93b44fa61db3d02188512c6d2bdbf048c53f747
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.voc.php
@@ -0,0 +1,205 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.voc.php                                        //
+// module for analyzing Creative VOC Audio files               //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_voc
+{
+
+	function getid3_voc(&$fd, &$ThisFileInfo) {
+
+		$OriginalAVdataOffset = $ThisFileInfo['avdataoffset'];
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$VOCheader  = fread($fd, 26);
+
+		if (substr($VOCheader, 0, 19) != 'Creative Voice File') {
+			$ThisFileInfo['error'][] = 'Expecting "Creative Voice File" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($VOCheader, 0, 19).'"';
+			return false;
+		}
+
+		// shortcuts
+		$thisfile_audio = &$ThisFileInfo['audio'];
+		$ThisFileInfo['voc'] = array();
+		$thisfile_voc        = &$ThisFileInfo['voc'];
+
+		$ThisFileInfo['fileformat']               = 'voc';
+		$thisfile_audio['dataformat']      = 'voc';
+		$thisfile_audio['bitrate_mode']    = 'cbr';
+		$thisfile_audio['lossless']        = true;
+		$thisfile_audio['channels']        = 1; // might be overriden below
+		$thisfile_audio['bits_per_sample'] = 8; // might be overriden below
+
+		// byte #     Description
+		// ------     ------------------------------------------
+		// 00-12      'Creative Voice File'
+		// 13         1A (eof to abort printing of file)
+		// 14-15      Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
+		// 16-17      Version number (minor,major) (VOC-HDR puts 0A 01)
+		// 18-19      2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
+
+		$thisfile_voc['header']['datablock_offset'] = getid3_lib::LittleEndian2Int(substr($VOCheader, 20, 2));
+		$thisfile_voc['header']['minor_version']    = getid3_lib::LittleEndian2Int(substr($VOCheader, 22, 1));
+		$thisfile_voc['header']['major_version']    = getid3_lib::LittleEndian2Int(substr($VOCheader, 23, 1));
+
+		do {
+
+			$BlockOffset    = ftell($fd);
+			$BlockData      = fread($fd, 4);
+			$BlockType      = ord($BlockData{0});
+			$BlockSize      = getid3_lib::LittleEndian2Int(substr($BlockData, 1, 3));
+			$ThisBlock      = array();
+
+			@$thisfile_voc['blocktypes'][$BlockType]++;
+			switch ($BlockType) {
+				case 0:  // Terminator
+					// do nothing, we'll break out of the loop down below
+					break;
+
+				case 1:  // Sound data
+					$BlockData .= fread($fd, 2);
+					if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
+						$ThisFileInfo['avdataoffset'] = ftell($fd);
+					}
+					fseek($fd, $BlockSize - 2, SEEK_CUR);
+
+					$ThisBlock['sample_rate_id']   = getid3_lib::LittleEndian2Int(substr($BlockData, 4, 1));
+					$ThisBlock['compression_type'] = getid3_lib::LittleEndian2Int(substr($BlockData, 5, 1));
+
+					$ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
+					if ($ThisBlock['compression_type'] <= 3) {
+						$thisfile_voc['compressed_bits_per_sample'] = getid3_lib::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
+					}
+
+					// Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
+					if (empty($thisfile_audio['sample_rate'])) {
+						// SR byte = 256 - (1000000 / sample_rate)
+						$thisfile_audio['sample_rate'] = getid3_lib::trunc((1000000 / (256 - $ThisBlock['sample_rate_id'])) / $thisfile_audio['channels']);
+					}
+					break;
+
+				case 2:  // Sound continue
+				case 3:  // Silence
+				case 4:  // Marker
+				case 6:  // Repeat
+				case 7:  // End repeat
+					// nothing useful, just skip
+					fseek($fd, $BlockSize, SEEK_CUR);
+					break;
+
+				case 8:  // Extended
+					$BlockData .= fread($fd, 4);
+
+					//00-01  Time Constant:
+					//   Mono: 65536 - (256000000 / sample_rate)
+					// Stereo: 65536 - (256000000 / (sample_rate * 2))
+					$ThisBlock['time_constant'] =        getid3_lib::LittleEndian2Int(substr($BlockData, 4, 2));
+					$ThisBlock['pack_method']   =        getid3_lib::LittleEndian2Int(substr($BlockData, 6, 1));
+					$ThisBlock['stereo']        = (bool) getid3_lib::LittleEndian2Int(substr($BlockData, 7, 1));
+
+					$thisfile_audio['channels']    = ($ThisBlock['stereo'] ? 2 : 1);
+					$thisfile_audio['sample_rate'] = getid3_lib::trunc((256000000 / (65536 - $ThisBlock['time_constant'])) / $thisfile_audio['channels']);
+					break;
+
+				case 9:  // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
+					$BlockData .= fread($fd, 12);
+					if ($ThisFileInfo['avdataoffset'] <= $OriginalAVdataOffset) {
+						$ThisFileInfo['avdataoffset'] = ftell($fd);
+					}
+					fseek($fd, $BlockSize - 12, SEEK_CUR);
+
+					$ThisBlock['sample_rate']      = getid3_lib::LittleEndian2Int(substr($BlockData,  4, 4));
+					$ThisBlock['bits_per_sample']  = getid3_lib::LittleEndian2Int(substr($BlockData,  8, 1));
+					$ThisBlock['channels']         = getid3_lib::LittleEndian2Int(substr($BlockData,  9, 1));
+					$ThisBlock['wFormat']          = getid3_lib::LittleEndian2Int(substr($BlockData, 10, 2));
+
+					$ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
+					if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
+						$thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
+					}
+
+					$thisfile_audio['sample_rate']     = $ThisBlock['sample_rate'];
+					$thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
+					$thisfile_audio['channels']        = $ThisBlock['channels'];
+					break;
+
+				default:
+					$ThisFileInfo['warning'][] = 'Unhandled block type "'.$BlockType.'" at offset '.$BlockOffset;
+					fseek($fd, $BlockSize, SEEK_CUR);
+					break;
+			}
+
+			if (!empty($ThisBlock)) {
+				$ThisBlock['block_offset']  = $BlockOffset;
+				$ThisBlock['block_size']    = $BlockSize;
+				$ThisBlock['block_type_id'] = $BlockType;
+				$thisfile_voc['blocks'][] = $ThisBlock;
+			}
+
+		} while (!feof($fd) && ($BlockType != 0));
+
+		// Terminator block doesn't have size field, so seek back 3 spaces
+		fseek($fd, -3, SEEK_CUR);
+
+		ksort($thisfile_voc['blocktypes']);
+
+		if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
+			$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
+			$thisfile_audio['bitrate'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+		}
+
+		return true;
+	}
+
+	function VOCcompressionTypeLookup($index) {
+		static $VOCcompressionTypeLookup = array(
+			0 => '8-bit',
+			1 => '4-bit',
+			2 => '2.6-bit',
+			3 => '2-bit'
+		);
+		return (isset($VOCcompressionTypeLookup[$index]) ? $VOCcompressionTypeLookup[$index] : 'Multi DAC ('.($index - 3).') channels');
+	}
+
+	function VOCwFormatLookup($index) {
+		static $VOCwFormatLookup = array(
+			0x0000 => '8-bit unsigned PCM',
+			0x0001 => 'Creative 8-bit to 4-bit ADPCM',
+			0x0002 => 'Creative 8-bit to 3-bit ADPCM',
+			0x0003 => 'Creative 8-bit to 2-bit ADPCM',
+			0x0004 => '16-bit signed PCM',
+			0x0006 => 'CCITT a-Law',
+			0x0007 => 'CCITT u-Law',
+			0x2000 => 'Creative 16-bit to 4-bit ADPCM'
+		);
+		return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
+	}
+
+	function VOCwFormatActualBitsPerSampleLookup($index) {
+		static $VOCwFormatLookup = array(
+			0x0000 => 8,
+			0x0001 => 4,
+			0x0002 => 3,
+			0x0003 => 2,
+			0x0004 => 16,
+			0x0006 => 8,
+			0x0007 => 8,
+			0x2000 => 4
+		);
+		return (isset($VOCwFormatLookup[$index]) ? $VOCwFormatLookup[$index] : false);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.vqf.php b/apps/media/getID3/getid3/module.audio.vqf.php
new file mode 100644
index 0000000000000000000000000000000000000000..49d4e8510e652bfd980e05bb3398cbb4a334fe68
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.vqf.php
@@ -0,0 +1,159 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.vqf.php                                        //
+// module for analyzing VQF audio files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_vqf
+{
+	function getid3_vqf(&$fd, &$ThisFileInfo) {
+		// based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
+		// http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
+
+		$ThisFileInfo['fileformat']            = 'vqf';
+		$ThisFileInfo['audio']['dataformat']   = 'vqf';
+		$ThisFileInfo['audio']['bitrate_mode'] = 'cbr';
+		$ThisFileInfo['audio']['lossless']     = false;
+
+		// shortcut
+		$ThisFileInfo['vqf']['raw'] = array();
+		$thisfile_vqf               = &$ThisFileInfo['vqf'];
+		$thisfile_vqf_raw           = &$thisfile_vqf['raw'];
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$VQFheaderData = fread($fd, 16);
+
+		$offset = 0;
+		$thisfile_vqf_raw['header_tag']     =               substr($VQFheaderData, $offset, 4);
+		if ($thisfile_vqf_raw['header_tag'] != 'TWIN') {
+			$ThisFileInfo['error'][] = 'Expecting "TWIN" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_vqf_raw['header_tag'].'"';
+			unset($ThisFileInfo['vqf']);
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+		$offset += 4;
+		$thisfile_vqf_raw['version']        =               substr($VQFheaderData, $offset, 8);
+		$offset += 8;
+		$thisfile_vqf_raw['size']           = getid3_lib::BigEndian2Int(substr($VQFheaderData, $offset, 4));
+		$offset += 4;
+
+		while (ftell($fd) < $ThisFileInfo['avdataend']) {
+
+			$ChunkBaseOffset = ftell($fd);
+			$chunkoffset = 0;
+			$ChunkData = fread($fd, 8);
+			$ChunkName = substr($ChunkData, $chunkoffset, 4);
+			if ($ChunkName == 'DATA') {
+				$ThisFileInfo['avdataoffset'] = $ChunkBaseOffset;
+				break;
+			}
+			$chunkoffset += 4;
+			$ChunkSize = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
+			$chunkoffset += 4;
+			if ($ChunkSize > ($ThisFileInfo['avdataend'] - ftell($fd))) {
+				$ThisFileInfo['error'][] = 'Invalid chunk size ('.$ChunkSize.') for chunk "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
+				break;
+			}
+			if ($ChunkSize > 0) {
+				$ChunkData .= fread($fd, $ChunkSize);
+			}
+
+			switch ($ChunkName) {
+				case 'COMM':
+					// shortcut
+					$thisfile_vqf['COMM'] = array();
+					$thisfile_vqf_COMM    = &$thisfile_vqf['COMM'];
+
+					$thisfile_vqf_COMM['channel_mode']   = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
+					$chunkoffset += 4;
+					$thisfile_vqf_COMM['bitrate']        = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
+					$chunkoffset += 4;
+					$thisfile_vqf_COMM['sample_rate']    = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
+					$chunkoffset += 4;
+					$thisfile_vqf_COMM['security_level'] = getid3_lib::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
+					$chunkoffset += 4;
+
+					$ThisFileInfo['audio']['channels']        = $thisfile_vqf_COMM['channel_mode'] + 1;
+					$ThisFileInfo['audio']['sample_rate']     = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
+					$ThisFileInfo['audio']['bitrate']         = $thisfile_vqf_COMM['bitrate'] * 1000;
+					$ThisFileInfo['audio']['encoder_options'] = 'CBR' . ceil($ThisFileInfo['audio']['bitrate']/1000);
+
+					if ($ThisFileInfo['audio']['bitrate'] == 0) {
+						$ThisFileInfo['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
+						return false;
+					}
+					break;
+
+				case 'NAME':
+				case 'AUTH':
+				case '(c) ':
+				case 'FILE':
+				case 'COMT':
+				case 'ALBM':
+					$thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
+					break;
+
+				case 'DSIZ':
+					$thisfile_vqf['DSIZ'] = getid3_lib::BigEndian2Int(substr($ChunkData, 8, 4));
+					break;
+
+				default:
+					$ThisFileInfo['warning'][] = 'Unhandled chunk type "'.$ChunkName.'" at offset '.$ChunkBaseOffset;
+					break;
+			}
+		}
+
+		$ThisFileInfo['playtime_seconds'] = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['audio']['bitrate'];
+
+		if (isset($thisfile_vqf['DSIZ']) && (($thisfile_vqf['DSIZ'] != ($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'))))) {
+			switch ($thisfile_vqf['DSIZ']) {
+				case 0:
+				case 1:
+					$ThisFileInfo['warning'][] = 'Invalid DSIZ value "'.$thisfile_vqf['DSIZ'].'". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v'.($thisfile_vqf['DSIZ'] + 1).'.0';
+					$ThisFileInfo['audio']['encoder'] = 'Ahead Nero';
+					break;
+
+				default:
+					$ThisFileInfo['warning'][] = 'Probable corrupted file - should be '.$thisfile_vqf['DSIZ'].' bytes, actually '.($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset'] - strlen('DATA'));
+					break;
+			}
+		}
+
+		return true;
+	}
+
+	function VQFchannelFrequencyLookup($frequencyid) {
+		static $VQFchannelFrequencyLookup = array(
+			11 => 11025,
+			22 => 22050,
+			44 => 44100
+		);
+		return (isset($VQFchannelFrequencyLookup[$frequencyid]) ? $VQFchannelFrequencyLookup[$frequencyid] : $frequencyid * 1000);
+	}
+
+	function VQFcommentNiceNameLookup($shortname) {
+		static $VQFcommentNiceNameLookup = array(
+			'NAME' => 'title',
+			'AUTH' => 'artist',
+			'(c) ' => 'copyright',
+			'FILE' => 'filename',
+			'COMT' => 'comment',
+			'ALBM' => 'album'
+		);
+		return (isset($VQFcommentNiceNameLookup[$shortname]) ? $VQFcommentNiceNameLookup[$shortname] : $shortname);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.audio.wavpack.php b/apps/media/getID3/getid3/module.audio.wavpack.php
new file mode 100644
index 0000000000000000000000000000000000000000..589ebe28a4da7d3a61edfd7b597394e205a0b391
--- /dev/null
+++ b/apps/media/getID3/getid3/module.audio.wavpack.php
@@ -0,0 +1,372 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.audio.wavpack.php                                    //
+// module for analyzing WavPack v4.0+ Audio files              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_wavpack
+{
+
+	function getid3_wavpack(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		while (true) {
+
+			$wavpackheader = fread($fd, 32);
+
+			if (ftell($fd) >= $ThisFileInfo['avdataend']) {
+				break;
+			} elseif (feof($fd)) {
+				break;
+			} elseif (
+				(@$ThisFileInfo['wavpack']['blockheader']['total_samples'] > 0) &&
+				(@$ThisFileInfo['wavpack']['blockheader']['block_samples'] > 0) &&
+				(!isset($ThisFileInfo['wavpack']['riff_trailer_size']) || ($ThisFileInfo['wavpack']['riff_trailer_size'] <= 0)) &&
+				((@$ThisFileInfo['wavpack']['config_flags']['md5_checksum'] === false) || !empty($ThisFileInfo['md5_data_source']))) {
+					break;
+			}
+
+			$blockheader_offset = ftell($fd) - 32;
+			$blockheader_magic  =                              substr($wavpackheader,  0,  4);
+			$blockheader_size   = getid3_lib::LittleEndian2Int(substr($wavpackheader,  4,  4));
+
+			if ($blockheader_magic != 'wvpk') {
+				$ThisFileInfo['error'][] = 'Expecting "wvpk" at offset '.$blockheader_offset.', found "'.$blockheader_magic.'"';
+				if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
+					unset($ThisFileInfo['fileformat']);
+					unset($ThisFileInfo['audio']);
+					unset($ThisFileInfo['wavpack']);
+				}
+				return false;
+			}
+
+
+			if ((@$ThisFileInfo['wavpack']['blockheader']['block_samples'] <= 0) ||
+				(@$ThisFileInfo['wavpack']['blockheader']['total_samples'] <= 0)) {
+				// Also, it is possible that the first block might not have
+				// any samples (block_samples == 0) and in this case you should skip blocks
+				// until you find one with samples because the other information (like
+				// total_samples) are not guaranteed to be correct until (block_samples > 0)
+
+				// Finally, I have defined a format for files in which the length is not known
+				// (for example when raw files are created using pipes). In these cases
+				// total_samples will be -1 and you must seek to the final block to determine
+				// the total number of samples.
+
+
+				$ThisFileInfo['audio']['dataformat']   = 'wavpack';
+				$ThisFileInfo['fileformat']            = 'wavpack';
+				$ThisFileInfo['audio']['lossless']     = true;
+				$ThisFileInfo['audio']['bitrate_mode'] = 'vbr';
+
+				$ThisFileInfo['wavpack']['blockheader']['offset'] = $blockheader_offset;
+				$ThisFileInfo['wavpack']['blockheader']['magic']  = $blockheader_magic;
+				$ThisFileInfo['wavpack']['blockheader']['size']   = $blockheader_size;
+
+				if ($ThisFileInfo['wavpack']['blockheader']['size'] >= 0x100000) {
+					$ThisFileInfo['error'][] = 'Expecting WavPack block size less than "0x100000", found "'.$ThisFileInfo['wavpack']['blockheader']['size'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
+					if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
+						unset($ThisFileInfo['fileformat']);
+						unset($ThisFileInfo['audio']);
+						unset($ThisFileInfo['wavpack']);
+					}
+					return false;
+				}
+
+				$ThisFileInfo['wavpack']['blockheader']['minor_version'] = ord($wavpackheader{8});
+				$ThisFileInfo['wavpack']['blockheader']['major_version'] = ord($wavpackheader{9});
+
+				if (($ThisFileInfo['wavpack']['blockheader']['major_version'] != 4) ||
+					(($ThisFileInfo['wavpack']['blockheader']['minor_version'] < 4) &&
+					($ThisFileInfo['wavpack']['blockheader']['minor_version'] > 16))) {
+						$ThisFileInfo['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.$ThisFileInfo['wavpack']['blockheader']['minor_version'].'" at offset '.$ThisFileInfo['wavpack']['blockheader']['offset'];
+						if ((@$ThisFileInfo['audio']['dataformat'] != 'wavpack') && (@$ThisFileInfo['audio']['dataformat'] != 'wvc')) {
+							unset($ThisFileInfo['fileformat']);
+							unset($ThisFileInfo['audio']);
+							unset($ThisFileInfo['wavpack']);
+						}
+						return false;
+				}
+
+				$ThisFileInfo['wavpack']['blockheader']['track_number']  = ord($wavpackheader{10}); // unused
+				$ThisFileInfo['wavpack']['blockheader']['index_number']  = ord($wavpackheader{11}); // unused
+				$ThisFileInfo['wavpack']['blockheader']['total_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 12,  4));
+				$ThisFileInfo['wavpack']['blockheader']['block_index']   = getid3_lib::LittleEndian2Int(substr($wavpackheader, 16,  4));
+				$ThisFileInfo['wavpack']['blockheader']['block_samples'] = getid3_lib::LittleEndian2Int(substr($wavpackheader, 20,  4));
+				$ThisFileInfo['wavpack']['blockheader']['flags_raw']     = getid3_lib::LittleEndian2Int(substr($wavpackheader, 24,  4));
+				$ThisFileInfo['wavpack']['blockheader']['crc']           = getid3_lib::LittleEndian2Int(substr($wavpackheader, 28,  4));
+
+				$ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample']     =    1 + ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000003);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['mono']                 = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000004);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']               = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000008);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['joint_stereo']         = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000010);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['cross_decorrelation']  = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000020);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_noiseshape']    = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000040);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['ieee_32bit_float']     = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000080);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['int_32bit']            = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000100);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000200);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000400);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00000800);
+				$ThisFileInfo['wavpack']['blockheader']['flags']['multichannel_final']   = (bool) ($ThisFileInfo['wavpack']['blockheader']['flags_raw'] & 0x00001000);
+
+				$ThisFileInfo['audio']['lossless'] = !$ThisFileInfo['wavpack']['blockheader']['flags']['hybrid'];
+			}
+
+			while (!feof($fd) && (ftell($fd) < ($blockheader_offset + $blockheader_size + 8))) {
+
+				$metablock = array('offset'=>ftell($fd));
+				$metablockheader = fread($fd, 2);
+				if (feof($fd)) {
+					break;
+				}
+				$metablock['id'] = ord($metablockheader{0});
+				$metablock['function_id'] = ($metablock['id'] & 0x3F);
+				$metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
+
+				// The 0x20 bit in the id of the meta subblocks (which is defined as
+				// ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
+				// if a decoder encounters an id that it does not know about, it uses
+				// that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set
+				// then the decoder simply ignores the metadata, but if it is zero
+				// then the decoder should quit because it means that an understanding
+				// of the metadata is required to correctly decode the audio.
+				$metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
+
+				$metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
+				$metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
+				if ($metablock['large_block']) {
+					$metablockheader .= fread($fd, 2);
+				}
+				$metablock['size'] = getid3_lib::LittleEndian2Int(substr($metablockheader, 1)) * 2; // size is stored in words
+				$metablock['data'] = null;
+
+				if ($metablock['size'] > 0) {
+
+					switch ($metablock['function_id']) {
+						case 0x21: // ID_RIFF_HEADER
+						case 0x22: // ID_RIFF_TRAILER
+						case 0x23: // ID_REPLAY_GAIN
+						case 0x24: // ID_CUESHEET
+						case 0x25: // ID_CONFIG_BLOCK
+						case 0x26: // ID_MD5_CHECKSUM
+							$metablock['data'] = fread($fd, $metablock['size']);
+
+							if ($metablock['padded_data']) {
+								// padded to the nearest even byte
+								$metablock['size']--;
+								$metablock['data'] = substr($metablock['data'], 0, -1);
+							}
+							break;
+
+						case 0x00: // ID_DUMMY
+						case 0x01: // ID_ENCODER_INFO
+						case 0x02: // ID_DECORR_TERMS
+						case 0x03: // ID_DECORR_WEIGHTS
+						case 0x04: // ID_DECORR_SAMPLES
+						case 0x05: // ID_ENTROPY_VARS
+						case 0x06: // ID_HYBRID_PROFILE
+						case 0x07: // ID_SHAPING_WEIGHTS
+						case 0x08: // ID_FLOAT_INFO
+						case 0x09: // ID_INT32_INFO
+						case 0x0A: // ID_WV_BITSTREAM
+						case 0x0B: // ID_WVC_BITSTREAM
+						case 0x0C: // ID_WVX_BITSTREAM
+						case 0x0D: // ID_CHANNEL_INFO
+							fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
+							break;
+
+						default:
+							$ThisFileInfo['warning'][] = 'Unexpected metablock type "0x'.str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT).'" at offset '.$metablock['offset'];
+							fseek($fd, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
+							break;
+					}
+
+					switch ($metablock['function_id']) {
+						case 0x21: // ID_RIFF_HEADER
+							getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+							$original_wav_filesize = getid3_lib::LittleEndian2Int(substr($metablock['data'], 4, 4));
+							getid3_riff::ParseRIFFdata($metablock['data'], $ParsedRIFFheader);
+							$metablock['riff'] = $ParsedRIFFheader['riff'];
+							$metablock['riff']['original_filesize'] = $original_wav_filesize;
+							$ThisFileInfo['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
+
+							$ThisFileInfo['audio']['sample_rate'] = $ParsedRIFFheader['riff']['raw']['fmt ']['nSamplesPerSec'];
+							$ThisFileInfo['playtime_seconds']     = $ThisFileInfo['wavpack']['blockheader']['total_samples'] / $ThisFileInfo['audio']['sample_rate'];
+
+							// Safe RIFF header in case there's a RIFF footer later
+							$metablockRIFFheader = $metablock['data'];
+							break;
+
+
+						case 0x22: // ID_RIFF_TRAILER
+							$metablockRIFFfooter = $metablockRIFFheader.$metablock['data'];
+							getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio-video.riff.php', __FILE__, true);
+
+							$ftell_old = ftell($fd);
+							$startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
+							$ParsedRIFFfooter = array('avdataend'=>$ThisFileInfo['avdataend'], 'fileformat'=>'riff', 'error'=>array(), 'warning'=>array());
+							$metablock['riff'] = getid3_riff::ParseRIFF($fd, $startoffset, $startoffset + $metablock['size'], $ParsedRIFFfooter);
+							fseek($fd, $ftell_old, SEEK_SET);
+
+							if (!empty($metablock['riff']['INFO'])) {
+								getid3_riff::RIFFcommentsParse($metablock['riff']['INFO'], $metablock['comments']);
+								$ThisFileInfo['tags']['riff'] = $metablock['comments'];
+							}
+							break;
+
+
+						case 0x23: // ID_REPLAY_GAIN
+							$ThisFileInfo['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
+							break;
+
+
+						case 0x24: // ID_CUESHEET
+							$ThisFileInfo['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset '.$metablock['offset'];
+							break;
+
+
+						case 0x25: // ID_CONFIG_BLOCK
+							$metablock['flags_raw'] = getid3_lib::LittleEndian2Int(substr($metablock['data'], 0, 3));
+
+							$metablock['flags']['adobe_mode']     = (bool) ($metablock['flags_raw'] & 0x000001); // "adobe" mode for 32-bit floats
+							$metablock['flags']['fast_flag']      = (bool) ($metablock['flags_raw'] & 0x000002); // fast mode
+							$metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x000004); // double fast
+							$metablock['flags']['high_flag']      = (bool) ($metablock['flags_raw'] & 0x000008); // high quality mode
+							$metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x000010); // double high (not used yet)
+							$metablock['flags']['bitrate_kbps']   = (bool) ($metablock['flags_raw'] & 0x000020); // bitrate is kbps, not bits / sample
+							$metablock['flags']['auto_shaping']   = (bool) ($metablock['flags_raw'] & 0x000040); // automatic noise shaping
+							$metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x000080); // shaping mode specified
+							$metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x000100); // joint-stereo mode specified
+							$metablock['flags']['copy_time']      = (bool) ($metablock['flags_raw'] & 0x000200); // copy file-time from source
+							$metablock['flags']['create_exe']     = (bool) ($metablock['flags_raw'] & 0x000400); // create executable
+							$metablock['flags']['create_wvc']     = (bool) ($metablock['flags_raw'] & 0x000800); // create correction file
+							$metablock['flags']['optimize_wvc']   = (bool) ($metablock['flags_raw'] & 0x001000); // maximize bybrid compression
+							$metablock['flags']['quality_mode']   = (bool) ($metablock['flags_raw'] & 0x002000); // psychoacoustic quality mode
+							$metablock['flags']['raw_flag']       = (bool) ($metablock['flags_raw'] & 0x004000); // raw mode (not implemented yet)
+							$metablock['flags']['calc_noise']     = (bool) ($metablock['flags_raw'] & 0x008000); // calc noise in hybrid mode
+							$metablock['flags']['lossy_mode']     = (bool) ($metablock['flags_raw'] & 0x010000); // obsolete (for information)
+							$metablock['flags']['extra_mode']     = (bool) ($metablock['flags_raw'] & 0x020000); // extra processing mode
+							$metablock['flags']['skip_wvx']       = (bool) ($metablock['flags_raw'] & 0x040000); // no wvx stream w/ floats & big ints
+							$metablock['flags']['md5_checksum']   = (bool) ($metablock['flags_raw'] & 0x080000); // compute & store MD5 signature
+							$metablock['flags']['quiet_mode']     = (bool) ($metablock['flags_raw'] & 0x100000); // don't report progress %
+
+							$ThisFileInfo['wavpack']['config_flags'] = $metablock['flags'];
+
+
+							if ($ThisFileInfo['wavpack']['blockheader']['flags']['hybrid']) {
+								@$ThisFileInfo['audio']['encoder_options'] .= ' -b???';
+							}
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['adobe_mode']     ? ' -a' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['optimize_wvc']   ? ' -cc' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['create_exe']     ? ' -e' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['fast_flag']      ? ' -f' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['joint_override'] ? ' -j?' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['high_flag']      ? ' -h' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['md5_checksum']   ? ' -m' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['calc_noise']     ? ' -n' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['shape_override'] ? ' -s?' : '');
+							@$ThisFileInfo['audio']['encoder_options'] .= ($metablock['flags']['extra_mode']     ? ' -x?' : '');
+							if (@$ThisFileInfo['audio']['encoder_options']) {
+							    $ThisFileInfo['audio']['encoder_options'] = trim(@$ThisFileInfo['audio']['encoder_options']);
+							}
+							elseif (isset($ThisFileInfo['audio']['encoder_options'])) {
+							    unset($ThisFileInfo['audio']['encoder_options']);
+							}
+							break;
+
+
+						case 0x26: // ID_MD5_CHECKSUM
+							if (strlen($metablock['data']) == 16) {
+								$ThisFileInfo['md5_data_source'] = strtolower(getid3_lib::PrintHexBytes($metablock['data'], true, false, false));
+							} else {
+								$ThisFileInfo['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset '.$metablock['offset'].', but found '.strlen($metablock['data']).' bytes';
+							}
+							break;
+
+
+						case 0x00: // ID_DUMMY
+						case 0x01: // ID_ENCODER_INFO
+						case 0x02: // ID_DECORR_TERMS
+						case 0x03: // ID_DECORR_WEIGHTS
+						case 0x04: // ID_DECORR_SAMPLES
+						case 0x05: // ID_ENTROPY_VARS
+						case 0x06: // ID_HYBRID_PROFILE
+						case 0x07: // ID_SHAPING_WEIGHTS
+						case 0x08: // ID_FLOAT_INFO
+						case 0x09: // ID_INT32_INFO
+						case 0x0A: // ID_WV_BITSTREAM
+						case 0x0B: // ID_WVC_BITSTREAM
+						case 0x0C: // ID_WVX_BITSTREAM
+						case 0x0D: // ID_CHANNEL_INFO
+							unset($metablock);
+							break;
+					}
+
+				}
+				if (!empty($metablock)) {
+					$ThisFileInfo['wavpack']['metablocks'][] = $metablock;
+				}
+
+			}
+
+		}
+
+		$ThisFileInfo['audio']['encoder']         = 'WavPack v'.$ThisFileInfo['wavpack']['blockheader']['major_version'].'.'.str_pad($ThisFileInfo['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
+		$ThisFileInfo['audio']['bits_per_sample'] = $ThisFileInfo['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
+		$ThisFileInfo['audio']['channels']        = ($ThisFileInfo['wavpack']['blockheader']['flags']['mono'] ? 1 : 2);
+
+		if (@$ThisFileInfo['playtime_seconds']) {
+
+			$ThisFileInfo['audio']['bitrate']     = (($ThisFileInfo['avdataend'] - $ThisFileInfo['avdataoffset']) * 8) / $ThisFileInfo['playtime_seconds'];
+
+		} else {
+
+			$ThisFileInfo['audio']['dataformat']  = 'wvc';
+
+		}
+
+		return true;
+	}
+
+
+	function WavPackMetablockNameLookup(&$id) {
+		static $WavPackMetablockNameLookup = array(
+			0x00 => 'Dummy',
+			0x01 => 'Encoder Info',
+			0x02 => 'Decorrelation Terms',
+			0x03 => 'Decorrelation Weights',
+			0x04 => 'Decorrelation Samples',
+			0x05 => 'Entropy Variables',
+			0x06 => 'Hybrid Profile',
+			0x07 => 'Shaping Weights',
+			0x08 => 'Float Info',
+			0x09 => 'Int32 Info',
+			0x0A => 'WV Bitstream',
+			0x0B => 'WVC Bitstream',
+			0x0C => 'WVX Bitstream',
+			0x0D => 'Channel Info',
+			0x21 => 'RIFF header',
+			0x22 => 'RIFF trailer',
+			0x23 => 'Replay Gain',
+			0x24 => 'Cuesheet',
+			0x25 => 'Config Block',
+			0x26 => 'MD5 Checksum',
+		);
+		return (@$WavPackMetablockNameLookup[$id]);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.bmp.php b/apps/media/getID3/getid3/module.graphic.bmp.php
new file mode 100644
index 0000000000000000000000000000000000000000..ea8a119b5a6383cc892f86fce61264ff08133dda
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.bmp.php
@@ -0,0 +1,685 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.bmp.php                                      //
+// module for analyzing BMP Image files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_bmp
+{
+
+	function getid3_bmp(&$fd, &$ThisFileInfo, $ExtractPalette=false, $ExtractData=false) {
+
+	    // shortcuts
+	    $ThisFileInfo['bmp']['header']['raw'] = array();
+	    $thisfile_bmp                         = &$ThisFileInfo['bmp'];
+	    $thisfile_bmp_header                  = &$thisfile_bmp['header'];
+	    $thisfile_bmp_header_raw              = &$thisfile_bmp_header['raw'];
+
+		// BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
+		// all versions
+		// WORD    bfType;
+		// DWORD   bfSize;
+		// WORD    bfReserved1;
+		// WORD    bfReserved2;
+		// DWORD   bfOffBits;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$offset = 0;
+		$BMPheader = fread($fd, 14 + 40);
+
+		$thisfile_bmp_header_raw['identifier']  = substr($BMPheader, $offset, 2);
+		$offset += 2;
+
+		if ($thisfile_bmp_header_raw['identifier'] != 'BM') {
+			$ThisFileInfo['error'][] = 'Expecting "BM" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$thisfile_bmp_header_raw['identifier'].'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['bmp']);
+			return false;
+		}
+
+		$thisfile_bmp_header_raw['filesize']    = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+		$thisfile_bmp_header_raw['reserved1']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+		$offset += 2;
+		$thisfile_bmp_header_raw['reserved2']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+		$offset += 2;
+		$thisfile_bmp_header_raw['data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+		$thisfile_bmp_header_raw['header_size'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+		$offset += 4;
+
+
+		// check if the hardcoded-to-1 "planes" is at offset 22 or 26
+		$planes22 = getid3_lib::LittleEndian2Int(substr($BMPheader, 22, 2));
+		$planes26 = getid3_lib::LittleEndian2Int(substr($BMPheader, 26, 2));
+		if (($planes22 == 1) && ($planes26 != 1)) {
+			$thisfile_bmp['type_os']      = 'OS/2';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif (($planes26 == 1) && ($planes22 != 1)) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
+			$thisfile_bmp['type_os']      = 'OS/2';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 1;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 4;
+		} elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
+			$thisfile_bmp['type_os']      = 'Windows';
+			$thisfile_bmp['type_version'] = 5;
+		} else {
+			$ThisFileInfo['error'][] = 'Unknown BMP subtype (or not a BMP file)';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['bmp']);
+			return false;
+		}
+
+		$ThisFileInfo['fileformat']                  = 'bmp';
+		$ThisFileInfo['video']['dataformat']         = 'bmp';
+		$ThisFileInfo['video']['lossless']           = true;
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		if ($thisfile_bmp['type_os'] == 'OS/2') {
+
+			// OS/2-format BMP
+			// http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
+
+			// DWORD  Size;             /* Size of this structure in bytes */
+			// DWORD  Width;            /* Bitmap width in pixels */
+			// DWORD  Height;           /* Bitmap height in pixel */
+			// WORD   NumPlanes;        /* Number of bit planes (color depth) */
+			// WORD   BitsPerPixel;     /* Number of bits per pixel per plane */
+
+			$thisfile_bmp_header_raw['width']          = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['height']         = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['planes']         = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['bits_per_pixel'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+
+			$ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
+			$ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
+			$ThisFileInfo['video']['codec']           = 'BI_RGB '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
+
+			if ($thisfile_bmp['type_version'] >= 2) {
+				// DWORD  Compression;      /* Bitmap compression scheme */
+				// DWORD  ImageDataSize;    /* Size of bitmap data in bytes */
+				// DWORD  XResolution;      /* X resolution of display device */
+				// DWORD  YResolution;      /* Y resolution of display device */
+				// DWORD  ColorsUsed;       /* Number of color table indices used */
+				// DWORD  ColorsImportant;  /* Number of important color indices */
+				// WORD   Units;            /* Type of units used to measure resolution */
+				// WORD   Reserved;         /* Pad structure to 4-byte boundary */
+				// WORD   Recording;        /* Recording algorithm */
+				// WORD   Rendering;        /* Halftoning algorithm used */
+				// DWORD  Size1;            /* Reserved for halftoning algorithm use */
+				// DWORD  Size2;            /* Reserved for halftoning algorithm use */
+				// DWORD  ColorEncoding;    /* Color model used in bitmap */
+				// DWORD  Identifier;       /* Reserved for application use */
+
+				$thisfile_bmp_header_raw['compression']      = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['bmp_data_size']    = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_h']     = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_v']     = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['colors_used']      = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['resolution_units'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['reserved1']        = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['recording']        = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['rendering']        = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+				$offset += 2;
+				$thisfile_bmp_header_raw['size1']            = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['size2']            = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['color_encoding']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['identifier']       = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+
+				$thisfile_bmp_header['compression']             = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
+
+				$ThisFileInfo['video']['codec'] = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			}
+
+		} elseif ($thisfile_bmp['type_os'] == 'Windows') {
+
+			// Windows-format BMP
+
+			// BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
+			// all versions
+			// DWORD  biSize;
+			// LONG   biWidth;
+			// LONG   biHeight;
+			// WORD   biPlanes;
+			// WORD   biBitCount;
+			// DWORD  biCompression;
+			// DWORD  biSizeImage;
+			// LONG   biXPelsPerMeter;
+			// LONG   biYPelsPerMeter;
+			// DWORD  biClrUsed;
+			// DWORD  biClrImportant;
+
+			// possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ?
+
+			$thisfile_bmp_header_raw['width']            = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['height']           = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['planes']           = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['bits_per_pixel']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 2));
+			$offset += 2;
+			$thisfile_bmp_header_raw['compression']      = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['bmp_data_size']    = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['resolution_h']     = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['resolution_v']     = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
+			$offset += 4;
+			$thisfile_bmp_header_raw['colors_used']      = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+			$thisfile_bmp_header_raw['colors_important'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+			$offset += 4;
+
+			$thisfile_bmp_header['compression']  = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
+			$ThisFileInfo['video']['resolution_x']    = $thisfile_bmp_header_raw['width'];
+			$ThisFileInfo['video']['resolution_y']    = $thisfile_bmp_header_raw['height'];
+			$ThisFileInfo['video']['codec']           = $thisfile_bmp_header['compression'].' '.$thisfile_bmp_header_raw['bits_per_pixel'].'-bit';
+			$ThisFileInfo['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
+
+			if (($thisfile_bmp['type_version'] >= 4) || ($thisfile_bmp_header_raw['compression'] == 3)) {
+				// should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
+				$BMPheader .= fread($fd, 44);
+
+				// BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
+				// Win95+, WinNT4.0+
+				// DWORD        bV4RedMask;
+				// DWORD        bV4GreenMask;
+				// DWORD        bV4BlueMask;
+				// DWORD        bV4AlphaMask;
+				// DWORD        bV4CSType;
+				// CIEXYZTRIPLE bV4Endpoints;
+				// DWORD        bV4GammaRed;
+				// DWORD        bV4GammaGreen;
+				// DWORD        bV4GammaBlue;
+				$thisfile_bmp_header_raw['red_mask']     = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['green_mask']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['blue_mask']    = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['alpha_mask']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['cs_type']      = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_red']   =                  substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_green'] =                  substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['ciexyz_blue']  =                  substr($BMPheader, $offset, 4);
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_red']    = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_green']  = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['gamma_blue']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+
+				$thisfile_bmp_header['ciexyz_red']   = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
+				$thisfile_bmp_header['ciexyz_green'] = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
+				$thisfile_bmp_header['ciexyz_blue']  = getid3_lib::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
+			}
+
+			if ($thisfile_bmp['type_version'] >= 5) {
+				$BMPheader .= fread($fd, 16);
+
+				// BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
+				// Win98+, Win2000+
+				// DWORD        bV5Intent;
+				// DWORD        bV5ProfileData;
+				// DWORD        bV5ProfileSize;
+				// DWORD        bV5Reserved;
+				$thisfile_bmp_header_raw['intent']              = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['profile_data_offset'] = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['profile_data_size']   = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+				$thisfile_bmp_header_raw['reserved3']           = getid3_lib::LittleEndian2Int(substr($BMPheader, $offset, 4));
+				$offset += 4;
+			}
+
+		} else {
+
+			$ThisFileInfo['error'][] = 'Unknown BMP format in header.';
+			return false;
+
+		}
+
+
+		if ($ExtractPalette || $ExtractData) {
+			$PaletteEntries = 0;
+			if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
+				$PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
+			} elseif (isset($thisfile_bmp_header_raw['colors_used']) && ($thisfile_bmp_header_raw['colors_used'] > 0) && ($thisfile_bmp_header_raw['colors_used'] <= 256)) {
+				$PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
+			}
+			if ($PaletteEntries > 0) {
+				$BMPpalette = fread($fd, 4 * $PaletteEntries);
+				$paletteoffset = 0;
+				for ($i = 0; $i < $PaletteEntries; $i++) {
+					// RGBQUAD          - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
+					// BYTE    rgbBlue;
+					// BYTE    rgbGreen;
+					// BYTE    rgbRed;
+					// BYTE    rgbReserved;
+					$blue  = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					$green = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					$red   = getid3_lib::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
+					if (($thisfile_bmp['type_os'] == 'OS/2') && ($thisfile_bmp['type_version'] == 1)) {
+						// no padding byte
+					} else {
+						$paletteoffset++; // padding byte
+					}
+					$thisfile_bmp['palette'][$i] = (($red << 16) | ($green << 8) | $blue);
+				}
+			}
+		}
+
+		if ($ExtractData) {
+			fseek($fd, $thisfile_bmp_header_raw['data_offset'], SEEK_SET);
+			$RowByteLength = ceil(($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8)) / 4) * 4; // round up to nearest DWORD boundry
+			$BMPpixelData = fread($fd, $thisfile_bmp_header_raw['height'] * $RowByteLength);
+			$pixeldataoffset = 0;
+			switch (@$thisfile_bmp_header_raw['compression']) {
+
+				case 0: // BI_RGB
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 1:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
+									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									for ($i = 7; $i >= 0; $i--) {
+										$paletteindex = ($paletteindexbyte & (0x01 << $i)) >> $i;
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+										$col++;
+									}
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 4:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
+									$paletteindexbyte = ord($BMPpixelData{$pixeldataoffset++});
+									for ($i = 1; $i >= 0; $i--) {
+										$paletteindex = ($paletteindexbyte & (0x0F << (4 * $i))) >> (4 * $i);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+										$col++;
+									}
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 8:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$paletteindex = ord($BMPpixelData{$pixeldataoffset++});
+									$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 24:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$pixeldataoffset += 3;
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 32:
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$thisfile_bmp['data'][$row][$col] = (ord($BMPpixelData{$pixeldataoffset+3}) << 24) | (ord($BMPpixelData{$pixeldataoffset+2}) << 16) | (ord($BMPpixelData{$pixeldataoffset+1}) << 8) | ord($BMPpixelData{$pixeldataoffset});
+									$pixeldataoffset += 4;
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						case 16:
+							// ?
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				case 1: // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 8:
+							$pixelcounter = 0;
+							while ($pixeldataoffset < strlen($BMPpixelData)) {
+								$firstbyte  = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								$secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								if ($firstbyte == 0) {
+
+									// escaped/absolute mode - the first byte of the pair can be set to zero to
+									// indicate an escape character that denotes the end of a line, the end of
+									// a bitmap, or a delta, depending on the value of the second byte.
+									switch ($secondbyte) {
+										case 0:
+											// end of line
+											// no need for special processing, just ignore
+											break;
+
+										case 1:
+											// end of bitmap
+											$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
+											break;
+
+										case 2:
+											// delta - The 2 bytes following the escape contain unsigned values
+											// indicating the horizontal and vertical offsets of the next pixel
+											// from the current position.
+											$colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
+											$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
+											$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
+											break;
+
+										default:
+											// In absolute mode, the first byte is zero and the second byte is a
+											// value in the range 03H through FFH. The second byte represents the
+											// number of bytes that follow, each of which contains the color index
+											// of a single pixel. Each run must be aligned on a word boundary.
+											for ($i = 0; $i < $secondbyte; $i++) {
+												$paletteindex = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+												$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+												$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+												$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+												$pixelcounter++;
+											}
+											while (($pixeldataoffset % 2) != 0) {
+												// Each run must be aligned on a word boundary.
+												$pixeldataoffset++;
+											}
+											break;
+									}
+
+								} else {
+
+									// encoded mode - the first byte specifies the number of consecutive pixels
+									// to be drawn using the color index contained in the second byte.
+									for ($i = 0; $i < $firstbyte; $i++) {
+										$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+										$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
+										$pixelcounter++;
+									}
+
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+
+				case 2: // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 4:
+							$pixelcounter = 0;
+							while ($pixeldataoffset < strlen($BMPpixelData)) {
+								$firstbyte  = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								$secondbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+								if ($firstbyte == 0) {
+
+									// escaped/absolute mode - the first byte of the pair can be set to zero to
+									// indicate an escape character that denotes the end of a line, the end of
+									// a bitmap, or a delta, depending on the value of the second byte.
+									switch ($secondbyte) {
+										case 0:
+											// end of line
+											// no need for special processing, just ignore
+											break;
+
+										case 1:
+											// end of bitmap
+											$pixeldataoffset = strlen($BMPpixelData); // force to exit loop just in case
+											break;
+
+										case 2:
+											// delta - The 2 bytes following the escape contain unsigned values
+											// indicating the horizontal and vertical offsets of the next pixel
+											// from the current position.
+											$colincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$rowincrement = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+											$col = ($pixelcounter % $thisfile_bmp_header_raw['width']) + $colincrement;
+											$row = ($thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width'])) - $rowincrement;
+											$pixelcounter = ($row * $thisfile_bmp_header_raw['width']) + $col;
+											break;
+
+										default:
+											// In absolute mode, the first byte is zero. The second byte contains the number
+											// of color indexes that follow. Subsequent bytes contain color indexes in their
+											// high- and low-order 4 bits, one color index for each pixel. In absolute mode,
+											// each run must be aligned on a word boundary.
+											unset($paletteindexes);
+											for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
+												$paletteindexbyte = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
+												$paletteindexes[] = ($paletteindexbyte & 0xF0) >> 4;
+												$paletteindexes[] = ($paletteindexbyte & 0x0F);
+											}
+											while (($pixeldataoffset % 2) != 0) {
+												// Each run must be aligned on a word boundary.
+												$pixeldataoffset++;
+											}
+
+											foreach ($paletteindexes as $paletteindex) {
+												$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+												$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+												$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
+												$pixelcounter++;
+											}
+											break;
+									}
+
+								} else {
+
+									// encoded mode - the first byte of the pair contains the number of pixels to be
+									// drawn using the color indexes in the second byte. The second byte contains two
+									// color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
+									// The first of the pixels is drawn using the color specified by the high-order
+									// 4 bits, the second is drawn using the color in the low-order 4 bits, the third
+									// is drawn using the color in the high-order 4 bits, and so on, until all the
+									// pixels specified by the first byte have been drawn.
+									$paletteindexes[0] = ($secondbyte & 0xF0) >> 4;
+									$paletteindexes[1] = ($secondbyte & 0x0F);
+									for ($i = 0; $i < $firstbyte; $i++) {
+										$col = $pixelcounter % $thisfile_bmp_header_raw['width'];
+										$row = $thisfile_bmp_header_raw['height'] - 1 - (($pixelcounter - $col) / $thisfile_bmp_header_raw['width']);
+										$thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[($i % 2)]];
+										$pixelcounter++;
+									}
+
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				case 3: // BI_BITFIELDS
+					switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
+						case 16:
+						case 32:
+							$redshift   = 0;
+							$greenshift = 0;
+							$blueshift  = 0;
+							while ((($thisfile_bmp_header_raw['red_mask'] >> $redshift) & 0x01) == 0) {
+								$redshift++;
+							}
+							while ((($thisfile_bmp_header_raw['green_mask'] >> $greenshift) & 0x01) == 0) {
+								$greenshift++;
+							}
+							while ((($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) & 0x01) == 0) {
+								$blueshift++;
+							}
+							for ($row = ($thisfile_bmp_header_raw['height'] - 1); $row >= 0; $row--) {
+								for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
+									$pixelvalue = getid3_lib::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
+									$pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
+
+									$red   = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['red_mask'])   >> $redshift)   / ($thisfile_bmp_header_raw['red_mask']   >> $redshift))   * 255));
+									$green = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift)) * 255));
+									$blue  = intval(round(((($pixelvalue & $thisfile_bmp_header_raw['blue_mask'])  >> $blueshift)  / ($thisfile_bmp_header_raw['blue_mask']  >> $blueshift))  * 255));
+									$thisfile_bmp['data'][$row][$col] = (($red << 16) | ($green << 8) | ($blue));
+								}
+								while (($pixeldataoffset % 4) != 0) {
+									// lines are padded to nearest DWORD
+									$pixeldataoffset++;
+								}
+							}
+							break;
+
+						default:
+							$ThisFileInfo['error'][] = 'Unknown bits-per-pixel value ('.$thisfile_bmp_header_raw['bits_per_pixel'].') - cannot read pixel data';
+							break;
+					}
+					break;
+
+
+				default: // unhandled compression type
+					$ThisFileInfo['error'][] = 'Unknown/unhandled compression type value ('.$thisfile_bmp_header_raw['compression'].') - cannot decompress pixel data';
+					break;
+			}
+		}
+
+		return true;
+	}
+
+
+	function PlotBMP(&$BMPinfo) {
+		$starttime = time();
+		if (!isset($BMPinfo['bmp']['data']) || !is_array($BMPinfo['bmp']['data'])) {
+			echo 'ERROR: no pixel data<BR>';
+			return false;
+		}
+		set_time_limit(intval(round($BMPinfo['resolution_x'] * $BMPinfo['resolution_y'] / 10000)));
+		if ($im = ImageCreateTrueColor($BMPinfo['resolution_x'], $BMPinfo['resolution_y'])) {
+			for ($row = 0; $row < $BMPinfo['resolution_y']; $row++) {
+				for ($col = 0; $col < $BMPinfo['resolution_x']; $col++) {
+					if (isset($BMPinfo['bmp']['data'][$row][$col])) {
+						$red   = ($BMPinfo['bmp']['data'][$row][$col] & 0x00FF0000) >> 16;
+						$green = ($BMPinfo['bmp']['data'][$row][$col] & 0x0000FF00) >> 8;
+						$blue  = ($BMPinfo['bmp']['data'][$row][$col] & 0x000000FF);
+						$pixelcolor = ImageColorAllocate($im, $red, $green, $blue);
+						ImageSetPixel($im, $col, $row, $pixelcolor);
+					} else {
+						//echo 'ERROR: no data for pixel '.$row.' x '.$col.'<BR>';
+						//return false;
+					}
+				}
+			}
+			if (headers_sent()) {
+				echo 'plotted '.($BMPinfo['resolution_x'] * $BMPinfo['resolution_y']).' pixels in '.(time() - $starttime).' seconds<BR>';
+				ImageDestroy($im);
+				exit;
+			} else {
+				header('Content-type: image/png');
+				ImagePNG($im);
+				ImageDestroy($im);
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function BMPcompressionWindowsLookup($compressionid) {
+		static $BMPcompressionWindowsLookup = array(
+			0 => 'BI_RGB',
+			1 => 'BI_RLE8',
+			2 => 'BI_RLE4',
+			3 => 'BI_BITFIELDS',
+			4 => 'BI_JPEG',
+			5 => 'BI_PNG'
+		);
+		return (isset($BMPcompressionWindowsLookup[$compressionid]) ? $BMPcompressionWindowsLookup[$compressionid] : 'invalid');
+	}
+
+	function BMPcompressionOS2Lookup($compressionid) {
+		static $BMPcompressionOS2Lookup = array(
+			0 => 'BI_RGB',
+			1 => 'BI_RLE8',
+			2 => 'BI_RLE4',
+			3 => 'Huffman 1D',
+			4 => 'BI_RLE24',
+		);
+		return (isset($BMPcompressionOS2Lookup[$compressionid]) ? $BMPcompressionOS2Lookup[$compressionid] : 'invalid');
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.gif.php b/apps/media/getID3/getid3/module.graphic.gif.php
new file mode 100644
index 0000000000000000000000000000000000000000..986ada30a7cd038ffa1b326e8e907528fca43cb0
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.gif.php
@@ -0,0 +1,183 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.gif.php                                      //
+// module for analyzing GIF Image files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_gif
+{
+
+	function getid3_gif(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']                  = 'gif';
+		$ThisFileInfo['video']['dataformat']         = 'gif';
+		$ThisFileInfo['video']['lossless']           = true;
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$GIFheader = fread($fd, 13);
+		$offset = 0;
+
+		$ThisFileInfo['gif']['header']['raw']['identifier']            =                              substr($GIFheader, $offset, 3);
+		$offset += 3;
+
+		if ($ThisFileInfo['gif']['header']['raw']['identifier'] != 'GIF') {
+			$ThisFileInfo['error'][] = 'Expecting "GIF" at offset '.$ThisFileInfo['avdataoffset'].', found "'.$ThisFileInfo['gif']['header']['raw']['identifier'].'"';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['gif']);
+			return false;
+		}
+
+		$ThisFileInfo['gif']['header']['raw']['version']               =                              substr($GIFheader, $offset, 3);
+		$offset += 3;
+		$ThisFileInfo['gif']['header']['raw']['width']                 = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
+		$offset += 2;
+		$ThisFileInfo['gif']['header']['raw']['height']                = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 2));
+		$offset += 2;
+		$ThisFileInfo['gif']['header']['raw']['flags']                 = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
+		$offset += 1;
+		$ThisFileInfo['gif']['header']['raw']['bg_color_index']        = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
+		$offset += 1;
+		$ThisFileInfo['gif']['header']['raw']['aspect_ratio']          = getid3_lib::LittleEndian2Int(substr($GIFheader, $offset, 1));
+		$offset += 1;
+
+		$ThisFileInfo['video']['resolution_x']                         = $ThisFileInfo['gif']['header']['raw']['width'];
+		$ThisFileInfo['video']['resolution_y']                         = $ThisFileInfo['gif']['header']['raw']['height'];
+		$ThisFileInfo['gif']['version']                                = $ThisFileInfo['gif']['header']['raw']['version'];
+		$ThisFileInfo['gif']['header']['flags']['global_color_table']  = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80);
+		if ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x80) {
+			// Number of bits per primary color available to the original image, minus 1
+			$ThisFileInfo['gif']['header']['bits_per_pixel']  = 3 * ((($ThisFileInfo['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
+		} else {
+			$ThisFileInfo['gif']['header']['bits_per_pixel']  = 0;
+		}
+		$ThisFileInfo['gif']['header']['flags']['global_color_sorted'] = (bool) ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x40);
+		if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
+			// the number of bytes contained in the Global Color Table. To determine that
+			// actual size of the color table, raise 2 to [the value of the field + 1]
+			$ThisFileInfo['gif']['header']['global_color_size'] = pow(2, ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1);
+			$ThisFileInfo['video']['bits_per_sample']           = ($ThisFileInfo['gif']['header']['raw']['flags'] & 0x07) + 1;
+		} else {
+			$ThisFileInfo['gif']['header']['global_color_size'] = 0;
+		}
+		if ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] != 0) {
+			// Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
+			$ThisFileInfo['gif']['header']['aspect_ratio'] = ($ThisFileInfo['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
+		}
+
+//		if ($ThisFileInfo['gif']['header']['flags']['global_color_table']) {
+//			$GIFcolorTable = fread($fd, 3 * $ThisFileInfo['gif']['header']['global_color_size']);
+//			$offset = 0;
+//			for ($i = 0; $i < $ThisFileInfo['gif']['header']['global_color_size']; $i++) {
+//				$red   = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+//				$green = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+//				$blue  = getid3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
+//				$ThisFileInfo['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
+//			}
+//		}
+//
+//		// Image Descriptor
+//		while (!feof($fd)) {
+//			$NextBlockTest = fread($fd, 1);
+//			switch ($NextBlockTest) {
+//
+//				case ',': // ',' - Image separator character
+//
+//					$ImageDescriptorData = $NextBlockTest.fread($fd, 9);
+//					$ImageDescriptor = array();
+//					$ImageDescriptor['image_left']   = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2));
+//					$ImageDescriptor['image_top']    = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2));
+//					$ImageDescriptor['image_width']  = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2));
+//					$ImageDescriptor['image_height'] = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2));
+//					$ImageDescriptor['flags_raw']    = getid3_lib::LittleEndian2Int(substr($ImageDescriptorData, 9, 1));
+//					$ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80);
+//					$ImageDescriptor['flags']['image_interlaced']    = (bool) ($ImageDescriptor['flags_raw'] & 0x40);
+//					$ThisFileInfo['gif']['image_descriptor'][] = $ImageDescriptor;
+//
+//					if ($ImageDescriptor['flags']['use_local_color_map']) {
+//
+//						$ThisFileInfo['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
+//						return true;
+//
+//					}
+//echo 'Start of raster data: '.ftell($fd).'<BR>';
+//					$RasterData = array();
+//					$RasterData['code_size']        = getid3_lib::LittleEndian2Int(fread($fd, 1));
+//					$RasterData['block_byte_count'] = getid3_lib::LittleEndian2Int(fread($fd, 1));
+//					$ThisFileInfo['gif']['raster_data'][count($ThisFileInfo['gif']['image_descriptor']) - 1] = $RasterData;
+//
+//					$CurrentCodeSize = $RasterData['code_size'] + 1;
+//					for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) {
+//						$DefaultDataLookupTable[$i] = chr($i);
+//					}
+//					$DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code
+//					$DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code
+//
+//
+//					$NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
+//					echo 'Clear Code: '.$NextValue.'<BR>';
+//
+//					$NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
+//					echo 'First Color: '.$NextValue.'<BR>';
+//
+//					$Prefix = $NextValue;
+//$i = 0;
+//					while ($i++ < 20) {
+//						$NextValue = $this->GetLSBits($fd, $CurrentCodeSize);
+//						echo $NextValue.'<BR>';
+//					}
+//return true;
+//					break;
+//
+//				case '!':
+//					// GIF Extension Block
+//					$ExtensionBlockData = $NextBlockTest.fread($fd, 2);
+//					$ExtensionBlock = array();
+//					$ExtensionBlock['function_code']  = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
+//					$ExtensionBlock['byte_length']    = getid3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
+//					$ExtensionBlock['data']           = fread($fd, $ExtensionBlock['byte_length']);
+//					$ThisFileInfo['gif']['extension_blocks'][] = $ExtensionBlock;
+//					break;
+//
+//				case ';':
+//					$ThisFileInfo['gif']['terminator_offset'] = ftell($fd) - 1;
+//					// GIF Terminator
+//					break;
+//
+//				default:
+//					break;
+//
+//
+//			}
+//		}
+
+		return true;
+	}
+
+
+	function GetLSBits($fd, $bits) {
+		static $bitbuffer = '';
+		while (strlen($bitbuffer) < $bits) {
+//echo 'Read another byte: '.ftell($fd).'<BR>';
+			$bitbuffer = str_pad(decbin(ord(fread($fd, 1))), 8, '0', STR_PAD_LEFT).$bitbuffer;
+		}
+
+		$value = bindec(substr($bitbuffer, 0 - $bits));
+		$bitbuffer = substr($bitbuffer, 0, 0 - $bits);
+
+		return $value;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.jpg.php b/apps/media/getID3/getid3/module.graphic.jpg.php
new file mode 100644
index 0000000000000000000000000000000000000000..0c2db92e6367d6d41d61b82e79a67e73bd90836b
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.jpg.php
@@ -0,0 +1,249 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.jpg.php                                      //
+// module for analyzing JPEG Image files                       //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_jpg
+{
+
+
+	function getid3_jpg(&$fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat']                  = 'jpg';
+		$ThisFileInfo['video']['dataformat']         = 'jpg';
+		$ThisFileInfo['video']['lossless']           = false;
+		$ThisFileInfo['video']['bits_per_sample']    = 24;
+		$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		$imageinfo = array();
+		list($width, $height, $type) = getid3_lib::GetDataImageSize(fread($fd, $ThisFileInfo['filesize']), $imageinfo);
+
+		if (isset($imageinfo['APP13'])) {
+			// http://php.net/iptcparse
+			// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
+	        $iptc_parsed = iptcparse($imageinfo['APP13']);
+	        if (is_array($iptc_parsed)) {
+		        foreach ($iptc_parsed as $iptc_key_raw => $iptc_values) {
+		        	list($iptc_record, $iptc_tagkey) = explode('#', $iptc_key_raw);
+		        	$iptc_tagkey = intval(ltrim($iptc_tagkey, '0'));
+		        	foreach ($iptc_values as $key => $value) {
+		        		@$ThisFileInfo['iptc'][$this->IPTCrecordName($iptc_record)][$this->IPTCrecordTagName($iptc_record, $iptc_tagkey)][] = $value;
+		        	}
+		        }
+		    }
+//echo '<pre>'.htmlentities(print_r($iptc_parsed, true)).'</pre>';
+		}
+
+		switch ($type) {
+			case 2: // JPEG
+				$ThisFileInfo['video']['resolution_x'] = $width;
+				$ThisFileInfo['video']['resolution_y'] = $height;
+
+				if (version_compare(phpversion(), '4.2.0', '>=')) {
+
+					if (function_exists('exif_read_data')) {
+
+						ob_start();
+						$ThisFileInfo['jpg']['exif'] = exif_read_data($ThisFileInfo['filenamepath'], '', true, false);
+						$errors = ob_get_contents();
+						if ($errors) {
+							$ThisFileInfo['warning'][] = strip_tags($errors);
+							unset($ThisFileInfo['jpg']['exif']);
+						}
+						ob_end_clean();
+
+					} else {
+
+						$ThisFileInfo['warning'][] = 'EXIF parsing only available when '.(GETID3_OS_ISWINDOWS ? 'php_exif.dll enabled' : 'compiled with --enable-exif');
+
+					}
+
+				} else {
+
+					$ThisFileInfo['warning'][] = 'EXIF parsing only available in PHP v4.2.0 and higher compiled with --enable-exif (or php_exif.dll enabled for Windows). You are using PHP v'.phpversion();
+
+				}
+
+				return true;
+				break;
+
+			default:
+				break;
+		}
+
+		unset($ThisFileInfo['fileformat']);
+		return false;
+	}
+
+
+	function IPTCrecordName($iptc_record) {
+		// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
+		static $IPTCrecordName = array();
+		if (empty($IPTCrecordName)) {
+			$IPTCrecordName = array(
+				1 => 'IPTCEnvelope',
+				2 => 'IPTCApplication',
+				3 => 'IPTCNewsPhoto',
+				7 => 'IPTCPreObjectData',
+				8 => 'IPTCObjectData',
+				9 => 'IPTCPostObjectData',
+			);
+		}
+		return (isset($IPTCrecordName[$iptc_record]) ? $IPTCrecordName[$iptc_record] : '');
+	}
+
+
+	function IPTCrecordTagName($iptc_record, $iptc_tagkey) {
+		// http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/IPTC.html
+		static $IPTCrecordTagName = array();
+		if (empty($IPTCrecordTagName)) {
+			$IPTCrecordTagName = array(
+				1 => array( // IPTC EnvelopeRecord Tags
+					0   => 'EnvelopeRecordVersion',
+					5   => 'Destination',
+					20  => 'FileFormat',
+					22  => 'FileVersion',
+					30  => 'ServiceIdentifier',
+					40  => 'EnvelopeNumber',
+					50  => 'ProductID',
+					60  => 'EnvelopePriority',
+					70  => 'DateSent',
+					80  => 'TimeSent',
+					90  => 'CodedCharacterSet',
+					100 => 'UniqueObjectName',
+					120 => 'ARMIdentifier',
+					122 => 'ARMVersion',
+				),
+				2 => array( // IPTC ApplicationRecord Tags
+					0   => 'ApplicationRecordVersion',
+					3   => 'ObjectTypeReference',
+					4   => 'ObjectAttributeReference',
+					5   => 'ObjectName',
+					7   => 'EditStatus',
+					8   => 'EditorialUpdate',
+					10  => 'Urgency',
+					12  => 'SubjectReference',
+					15  => 'Category',
+					20  => 'SupplementalCategories',
+					22  => 'FixtureIdentifier',
+					25  => 'Keywords',
+					26  => 'ContentLocationCode',
+					27  => 'ContentLocationName',
+					30  => 'ReleaseDate',
+					35  => 'ReleaseTime',
+					37  => 'ExpirationDate',
+					38  => 'ExpirationTime',
+					40  => 'SpecialInstructions',
+					42  => 'ActionAdvised',
+					45  => 'ReferenceService',
+					47  => 'ReferenceDate',
+					50  => 'ReferenceNumber',
+					55  => 'DateCreated',
+					60  => 'TimeCreated',
+					62  => 'DigitalCreationDate',
+					63  => 'DigitalCreationTime',
+					65  => 'OriginatingProgram',
+					70  => 'ProgramVersion',
+					75  => 'ObjectCycle',
+					80  => 'By-line',
+					85  => 'By-lineTitle',
+					90  => 'City',
+					92  => 'Sub-location',
+					95  => 'Province-State',
+					100 => 'Country-PrimaryLocationCode',
+					101 => 'Country-PrimaryLocationName',
+					103 => 'OriginalTransmissionReference',
+					105 => 'Headline',
+					110 => 'Credit',
+					115 => 'Source',
+					116 => 'CopyrightNotice',
+					118 => 'Contact',
+					120 => 'Caption-Abstract',
+					121 => 'LocalCaption',
+					122 => 'Writer-Editor',
+					125 => 'RasterizedCaption',
+					130 => 'ImageType',
+					131 => 'ImageOrientation',
+					135 => 'LanguageIdentifier',
+					150 => 'AudioType',
+					151 => 'AudioSamplingRate',
+					152 => 'AudioSamplingResolution',
+					153 => 'AudioDuration',
+					154 => 'AudioOutcue',
+					184 => 'JobID',
+					185 => 'MasterDocumentID',
+					186 => 'ShortDocumentID',
+					187 => 'UniqueDocumentID',
+					188 => 'OwnerID',
+					200 => 'ObjectPreviewFileFormat',
+					201 => 'ObjectPreviewFileVersion',
+					202 => 'ObjectPreviewData',
+					221 => 'Prefs',
+					225 => 'ClassifyState',
+					228 => 'SimilarityIndex',
+					230 => 'DocumentNotes',
+					231 => 'DocumentHistory',
+					232 => 'ExifCameraInfo',
+				),
+				3 => array( // IPTC NewsPhoto Tags
+					0   => 'NewsPhotoVersion',
+					10  => 'IPTCPictureNumber',
+					20  => 'IPTCImageWidth',
+					30  => 'IPTCImageHeight',
+					40  => 'IPTCPixelWidth',
+					50  => 'IPTCPixelHeight',
+					55  => 'SupplementalType',
+					60  => 'ColorRepresentation',
+					64  => 'InterchangeColorSpace',
+					65  => 'ColorSequence',
+					66  => 'ICC_Profile',
+					70  => 'ColorCalibrationMatrix',
+					80  => 'LookupTable',
+					84  => 'NumIndexEntries',
+					85  => 'ColorPalette',
+					86  => 'IPTCBitsPerSample',
+					90  => 'SampleStructure',
+					100 => 'ScanningDirection',
+					102 => 'IPTCImageRotation',
+					110 => 'DataCompressionMethod',
+					120 => 'QuantizationMethod',
+					125 => 'EndPoints',
+					130 => 'ExcursionTolerance',
+					135 => 'BitsPerComponent',
+					140 => 'MaximumDensityRange',
+					145 => 'GammaCompensatedValue',
+				),
+				7 => array( // IPTC PreObjectData Tags
+					10  => 'SizeMode',
+					20  => 'MaxSubfileSize',
+					90  => 'ObjectSizeAnnounced',
+					95  => 'MaximumObjectSize',
+				),
+				8 => array( // IPTC ObjectData Tags
+					10  => 'SubFile',
+				),
+				9 => array( // IPTC PostObjectData Tags
+					10  => 'ConfirmedObjectSize',
+				),
+			);
+
+		}
+		return (isset($IPTCrecordTagName[$iptc_record][$iptc_tagkey]) ? $IPTCrecordTagName[$iptc_record][$iptc_tagkey] : $iptc_tagkey);
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.pcd.php b/apps/media/getID3/getid3/module.graphic.pcd.php
new file mode 100644
index 0000000000000000000000000000000000000000..60efabdf61f70b92f84bb859ae655e8b1d81c60f
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.pcd.php
@@ -0,0 +1,130 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.pcd.php                                      //
+// module for analyzing PhotoCD (PCD) Image files              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_pcd
+{
+	function getid3_pcd(&$fd, &$ThisFileInfo, $ExtractData=0) {
+		$ThisFileInfo['fileformat']          = 'pcd';
+		$ThisFileInfo['video']['dataformat'] = 'pcd';
+		$ThisFileInfo['video']['lossless']   = false;
+
+
+		fseek($fd, $ThisFileInfo['avdataoffset'] + 72, SEEK_SET);
+
+		$PCDflags = fread($fd, 1);
+		$PCDisVertical = ((ord($PCDflags) & 0x01) ? true : false);
+
+
+		if ($PCDisVertical) {
+			$ThisFileInfo['video']['resolution_x'] = 3072;
+			$ThisFileInfo['video']['resolution_y'] = 2048;
+		} else {
+			$ThisFileInfo['video']['resolution_x'] = 2048;
+			$ThisFileInfo['video']['resolution_y'] = 3072;
+		}
+
+
+		if ($ExtractData > 3) {
+
+			$ThisFileInfo['error'][] = 'Cannot extract PSD image data for detail levels above BASE (3)';
+
+		} elseif ($ExtractData > 0) {
+
+			$PCD_levels[1] = array( 192,  128, 0x02000); // BASE/16
+			$PCD_levels[2] = array( 384,  256, 0x0B800); // BASE/4
+			$PCD_levels[3] = array( 768,  512, 0x30000); // BASE
+			//$PCD_levels[4] = array(1536, 1024,    ??); // BASE*4  - encrypted with Kodak-proprietary compression/encryption
+			//$PCD_levels[5] = array(3072, 2048,    ??); // BASE*16 - encrypted with Kodak-proprietary compression/encryption
+			//$PCD_levels[6] = array(6144, 4096,    ??); // BASE*64 - encrypted with Kodak-proprietary compression/encryption; PhotoCD-Pro only
+
+			list($PCD_width, $PCD_height, $PCD_dataOffset) = $PCD_levels[3];
+
+			fseek($fd, $ThisFileInfo['avdataoffset'] + $PCD_dataOffset, SEEK_SET);
+
+			for ($y = 0; $y < $PCD_height; $y += 2) {
+				// The image-data of these subtypes start at the respective offsets of 02000h, 0b800h and 30000h.
+				// To decode the YcbYr to the more usual RGB-code, three lines of data have to be read, each
+				// consisting of ‘w’ bytes, where ‘w’ is the width of the image-subtype. The first ‘w’ bytes and
+				// the first half of the third ‘w’ bytes contain data for the first RGB-line, the second ‘w’ bytes
+				// and the second half of the third ‘w’ bytes contain data for a second RGB-line.
+
+				$PCD_data_Y1 = fread($fd, $PCD_width);
+				$PCD_data_Y2 = fread($fd, $PCD_width);
+				$PCD_data_Cb = fread($fd, intval(round($PCD_width / 2)));
+				$PCD_data_Cr = fread($fd, intval(round($PCD_width / 2)));
+
+				for ($x = 0; $x < $PCD_width; $x++) {
+					if ($PCDisVertical) {
+						$ThisFileInfo['pcd']['data'][$PCD_width - $x][$y]     = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
+						$ThisFileInfo['pcd']['data'][$PCD_width - $x][$y + 1] = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
+					} else {
+						$ThisFileInfo['pcd']['data'][$y][$x]                  = $this->YCbCr2RGB(ord($PCD_data_Y1{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
+						$ThisFileInfo['pcd']['data'][$y + 1][$x]              = $this->YCbCr2RGB(ord($PCD_data_Y2{$x}), ord($PCD_data_Cb{floor($x / 2)}), ord($PCD_data_Cr{floor($x / 2)}));
+					}
+				}
+			}
+
+			// Example for plotting extracted data
+			//getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.ac3.php', __FILE__, true);
+			//if ($PCDisVertical) {
+			//	$BMPinfo['resolution_x'] = $PCD_height;
+			//	$BMPinfo['resolution_y'] = $PCD_width;
+			//} else {
+			//	$BMPinfo['resolution_x'] = $PCD_width;
+			//	$BMPinfo['resolution_y'] = $PCD_height;
+			//}
+			//$BMPinfo['bmp']['data'] = $ThisFileInfo['pcd']['data'];
+			//getid3_bmp::PlotBMP($BMPinfo);
+			//exit;
+
+		}
+
+	}
+
+	function YCbCr2RGB($Y, $Cb, $Cr) {
+		static $YCbCr_constants = array();
+		if (empty($YCbCr_constants)) {
+			$YCbCr_constants['red']['Y']    =  0.0054980 * 256;
+			$YCbCr_constants['red']['Cb']   =  0.0000000 * 256;
+			$YCbCr_constants['red']['Cr']   =  0.0051681 * 256;
+			$YCbCr_constants['green']['Y']  =  0.0054980 * 256;
+			$YCbCr_constants['green']['Cb'] = -0.0015446 * 256;
+			$YCbCr_constants['green']['Cr'] = -0.0026325 * 256;
+			$YCbCr_constants['blue']['Y']   =  0.0054980 * 256;
+			$YCbCr_constants['blue']['Cb']  =  0.0079533 * 256;
+			$YCbCr_constants['blue']['Cr']  =  0.0000000 * 256;
+		}
+
+		$RGBcolor = array('red'=>0, 'green'=>0, 'blue'=>0);
+		foreach ($RGBcolor as $rgbname => $dummy) {
+			$RGBcolor[$rgbname] = max(0,
+										min(255,
+											intval(
+												round(
+													($YCbCr_constants[$rgbname]['Y'] * $Y) +
+													($YCbCr_constants[$rgbname]['Cb'] * ($Cb - 156)) +
+													($YCbCr_constants[$rgbname]['Cr'] * ($Cr - 137))
+												)
+											)
+										)
+									);
+		}
+		return (($RGBcolor['red'] * 65536) + ($RGBcolor['green'] * 256) + $RGBcolor['blue']);
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.png.php b/apps/media/getID3/getid3/module.graphic.png.php
new file mode 100644
index 0000000000000000000000000000000000000000..b5b3d35941e8e992e718d3b71931a8b388f50bb5
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.png.php
@@ -0,0 +1,519 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.png.php                                      //
+// module for analyzing PNG Image files                        //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_png
+{
+
+	function getid3_png(&$fd, &$ThisFileInfo) {
+
+	    // shortcut
+	    $ThisFileInfo['png'] = array();
+	    $thisfile_png = &$ThisFileInfo['png'];
+
+		$ThisFileInfo['fileformat']          = 'png';
+		$ThisFileInfo['video']['dataformat'] = 'png';
+		$ThisFileInfo['video']['lossless']   = false;
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$PNGfiledata = fread($fd, GETID3_FREAD_BUFFER_SIZE);
+		$offset = 0;
+
+		$PNGidentifier = substr($PNGfiledata, $offset, 8); // $89 $50 $4E $47 $0D $0A $1A $0A
+		$offset += 8;
+
+		if ($PNGidentifier != "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A") {
+			$ThisFileInfo['error'][] = 'First 8 bytes of file ('.getid3_lib::PrintHexBytes($PNGidentifier).') did not match expected PNG identifier';
+			unset($ThisFileInfo['fileformat']);
+			return false;
+		}
+
+		while (((ftell($fd) - (strlen($PNGfiledata) - $offset)) < $ThisFileInfo['filesize'])) {
+			$chunk['data_length'] = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4));
+			$offset += 4;
+			while (((strlen($PNGfiledata) - $offset) < ($chunk['data_length'] + 4)) && (ftell($fd) < $ThisFileInfo['filesize'])) {
+				$PNGfiledata .= fread($fd, GETID3_FREAD_BUFFER_SIZE);
+			}
+			$chunk['type_text']   =               substr($PNGfiledata, $offset, 4);
+			$offset += 4;
+			$chunk['type_raw']    = getid3_lib::BigEndian2Int($chunk['type_text']);
+			$chunk['data']        =               substr($PNGfiledata, $offset, $chunk['data_length']);
+			$offset += $chunk['data_length'];
+			$chunk['crc']         = getid3_lib::BigEndian2Int(substr($PNGfiledata, $offset, 4));
+			$offset += 4;
+
+			$chunk['flags']['ancilliary']   = (bool) ($chunk['type_raw'] & 0x20000000);
+			$chunk['flags']['private']      = (bool) ($chunk['type_raw'] & 0x00200000);
+			$chunk['flags']['reserved']     = (bool) ($chunk['type_raw'] & 0x00002000);
+			$chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x00000020);
+
+			// shortcut
+			$thisfile_png[$chunk['type_text']] = array();
+			$thisfile_png_chunk_type_text = &$thisfile_png[$chunk['type_text']];
+
+			switch ($chunk['type_text']) {
+
+				case 'IHDR': // Image Header
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					$thisfile_png_chunk_type_text['width']                     = getid3_lib::BigEndian2Int(substr($chunk['data'],  0, 4));
+					$thisfile_png_chunk_type_text['height']                    = getid3_lib::BigEndian2Int(substr($chunk['data'],  4, 4));
+					$thisfile_png_chunk_type_text['raw']['bit_depth']          = getid3_lib::BigEndian2Int(substr($chunk['data'],  8, 1));
+					$thisfile_png_chunk_type_text['raw']['color_type']         = getid3_lib::BigEndian2Int(substr($chunk['data'],  9, 1));
+					$thisfile_png_chunk_type_text['raw']['compression_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 10, 1));
+					$thisfile_png_chunk_type_text['raw']['filter_method']      = getid3_lib::BigEndian2Int(substr($chunk['data'], 11, 1));
+					$thisfile_png_chunk_type_text['raw']['interlace_method']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 1));
+
+					$thisfile_png_chunk_type_text['compression_method_text']   = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['raw']['compression_method']);
+					$thisfile_png_chunk_type_text['color_type']['palette']     = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x01);
+					$thisfile_png_chunk_type_text['color_type']['true_color']  = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x02);
+					$thisfile_png_chunk_type_text['color_type']['alpha']       = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x04);
+
+					$ThisFileInfo['video']['resolution_x']    = $thisfile_png_chunk_type_text['width'];
+					$ThisFileInfo['video']['resolution_y']    = $thisfile_png_chunk_type_text['height'];
+
+					$ThisFileInfo['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']);
+					break;
+
+
+				case 'PLTE': // Palette
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					$paletteoffset = 0;
+					for ($i = 0; $i <= 255; $i++) {
+						//$thisfile_png_chunk_type_text['red'][$i]   = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
+						//$thisfile_png_chunk_type_text['green'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
+						//$thisfile_png_chunk_type_text['blue'][$i]  = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
+						$red   = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
+						$green = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
+						$blue  = getid3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
+						$thisfile_png_chunk_type_text[$i] = (($red << 16) | ($green << 8) | ($blue));
+					}
+					break;
+
+
+				case 'tRNS': // Transparency
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					switch ($thisfile_png['IHDR']['raw']['color_type']) {
+						case 0:
+							$thisfile_png_chunk_type_text['transparent_color_gray']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
+							break;
+
+						case 2:
+							$thisfile_png_chunk_type_text['transparent_color_red']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
+							$thisfile_png_chunk_type_text['transparent_color_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 2));
+							$thisfile_png_chunk_type_text['transparent_color_blue']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 2));
+							break;
+
+						case 3:
+							for ($i = 0; $i < strlen($chunk['data']); $i++) {
+								$thisfile_png_chunk_type_text['palette_opacity'][$i] = getid3_lib::BigEndian2Int(substr($chunk['data'], $i, 1));
+							}
+							break;
+
+						case 4:
+						case 6:
+							$ThisFileInfo['error'][] = 'Invalid color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type'];
+
+						default:
+							$ThisFileInfo['warning'][] = 'Unhandled color_type in tRNS chunk: '.$thisfile_png['IHDR']['raw']['color_type'];
+							break;
+					}
+					break;
+
+
+				case 'gAMA': // Image Gamma
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					$thisfile_png_chunk_type_text['gamma']  = getid3_lib::BigEndian2Int($chunk['data']) / 100000;
+					break;
+
+
+				case 'cHRM': // Primary Chromaticities
+					$thisfile_png_chunk_type_text['header']  = $chunk;
+					$thisfile_png_chunk_type_text['white_x'] = getid3_lib::BigEndian2Int(substr($chunk['data'],  0, 4)) / 100000;
+					$thisfile_png_chunk_type_text['white_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'],  4, 4)) / 100000;
+					$thisfile_png_chunk_type_text['red_y']   = getid3_lib::BigEndian2Int(substr($chunk['data'],  8, 4)) / 100000;
+					$thisfile_png_chunk_type_text['red_y']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 12, 4)) / 100000;
+					$thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 16, 4)) / 100000;
+					$thisfile_png_chunk_type_text['green_y'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 20, 4)) / 100000;
+					$thisfile_png_chunk_type_text['blue_y']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 24, 4)) / 100000;
+					$thisfile_png_chunk_type_text['blue_y']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 28, 4)) / 100000;
+					break;
+
+
+				case 'sRGB': // Standard RGB Color Space
+					$thisfile_png_chunk_type_text['header']                 = $chunk;
+					$thisfile_png_chunk_type_text['reindering_intent']      = getid3_lib::BigEndian2Int($chunk['data']);
+					$thisfile_png_chunk_type_text['reindering_intent_text'] = $this->PNGsRGBintentLookup($thisfile_png_chunk_type_text['reindering_intent']);
+					break;
+
+
+				case 'iCCP': // Embedded ICC Profile
+					$thisfile_png_chunk_type_text['header']                  = $chunk;
+					list($profilename, $compressiondata)                                 = explode("\x00", $chunk['data'], 2);
+					$thisfile_png_chunk_type_text['profile_name']            = $profilename;
+					$thisfile_png_chunk_type_text['compression_method']      = getid3_lib::BigEndian2Int(substr($compressiondata, 0, 1));
+					$thisfile_png_chunk_type_text['compression_profile']     = substr($compressiondata, 1);
+
+					$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
+					break;
+
+
+				case 'tEXt': // Textual Data
+					$thisfile_png_chunk_type_text['header']  = $chunk;
+					list($keyword, $text)                                = explode("\x00", $chunk['data'], 2);
+					$thisfile_png_chunk_type_text['keyword'] = $keyword;
+					$thisfile_png_chunk_type_text['text']    = $text;
+
+					$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
+					break;
+
+
+				case 'zTXt': // Compressed Textual Data
+					$thisfile_png_chunk_type_text['header']                  = $chunk;
+					list($keyword, $otherdata)                                           = explode("\x00", $chunk['data'], 2);
+					$thisfile_png_chunk_type_text['keyword']                 = $keyword;
+					$thisfile_png_chunk_type_text['compression_method']      = getid3_lib::BigEndian2Int(substr($otherdata, 0, 1));
+					$thisfile_png_chunk_type_text['compressed_text']         = substr($otherdata, 1);
+					$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
+					switch ($thisfile_png_chunk_type_text['compression_method']) {
+						case 0:
+							$thisfile_png_chunk_type_text['text']            = gzuncompress($thisfile_png_chunk_type_text['compressed_text']);
+							break;
+
+						default:
+							// unknown compression method
+							break;
+					}
+
+					if (isset($thisfile_png_chunk_type_text['text'])) {
+						$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
+					}
+					break;
+
+
+				case 'iTXt': // International Textual Data
+					$thisfile_png_chunk_type_text['header']                  = $chunk;
+					list($keyword, $otherdata)                                           = explode("\x00", $chunk['data'], 2);
+					$thisfile_png_chunk_type_text['keyword']                 = $keyword;
+					$thisfile_png_chunk_type_text['compression']             = (bool) getid3_lib::BigEndian2Int(substr($otherdata, 0, 1));
+					$thisfile_png_chunk_type_text['compression_method']      = getid3_lib::BigEndian2Int(substr($otherdata, 1, 1));
+					$thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
+					list($languagetag, $translatedkeyword, $text)                        = explode("\x00", substr($otherdata, 2), 3);
+					$thisfile_png_chunk_type_text['language_tag']            = $languagetag;
+					$thisfile_png_chunk_type_text['translated_keyword']      = $translatedkeyword;
+
+					if ($thisfile_png_chunk_type_text['compression']) {
+
+						switch ($thisfile_png_chunk_type_text['compression_method']) {
+							case 0:
+								$thisfile_png_chunk_type_text['text']        = gzuncompress($text);
+								break;
+
+							default:
+								// unknown compression method
+								break;
+						}
+
+					} else {
+
+						$thisfile_png_chunk_type_text['text']                = $text;
+
+					}
+
+					if (isset($thisfile_png_chunk_type_text['text'])) {
+						$thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
+					}
+					break;
+
+
+				case 'bKGD': // Background Color
+					$thisfile_png_chunk_type_text['header']                   = $chunk;
+					switch ($thisfile_png['IHDR']['raw']['color_type']) {
+						case 0:
+						case 4:
+							$thisfile_png_chunk_type_text['background_gray']  = getid3_lib::BigEndian2Int($chunk['data']);
+							break;
+
+						case 2:
+						case 6:
+							$thisfile_png_chunk_type_text['background_red']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 0 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
+							$thisfile_png_chunk_type_text['background_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
+							$thisfile_png_chunk_type_text['background_blue']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 2 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
+							break;
+
+						case 3:
+							$thisfile_png_chunk_type_text['background_index'] = getid3_lib::BigEndian2Int($chunk['data']);
+							break;
+
+						default:
+							break;
+					}
+					break;
+
+
+				case 'pHYs': // Physical Pixel Dimensions
+					$thisfile_png_chunk_type_text['header']                 = $chunk;
+					$thisfile_png_chunk_type_text['pixels_per_unit_x']      = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4));
+					$thisfile_png_chunk_type_text['pixels_per_unit_y']      = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4));
+					$thisfile_png_chunk_type_text['unit_specifier']         = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
+					$thisfile_png_chunk_type_text['unit']                   = $this->PNGpHYsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
+					break;
+
+
+				case 'sBIT': // Significant Bits
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					switch ($thisfile_png['IHDR']['raw']['color_type']) {
+						case 0:
+							$thisfile_png_chunk_type_text['significant_bits_gray']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
+							break;
+
+						case 2:
+						case 3:
+							$thisfile_png_chunk_type_text['significant_bits_red']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
+							$thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
+							$thisfile_png_chunk_type_text['significant_bits_blue']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
+							break;
+
+						case 4:
+							$thisfile_png_chunk_type_text['significant_bits_gray']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
+							$thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
+							break;
+
+						case 6:
+							$thisfile_png_chunk_type_text['significant_bits_red']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
+							$thisfile_png_chunk_type_text['significant_bits_green'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
+							$thisfile_png_chunk_type_text['significant_bits_blue']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
+							$thisfile_png_chunk_type_text['significant_bits_alpha'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 3, 1));
+							break;
+
+						default:
+							break;
+					}
+					break;
+
+
+				case 'sPLT': // Suggested Palette
+					$thisfile_png_chunk_type_text['header']                           = $chunk;
+					list($palettename, $otherdata)                                                = explode("\x00", $chunk['data'], 2);
+					$thisfile_png_chunk_type_text['palette_name']                     = $palettename;
+					$sPLToffset = 0;
+					$thisfile_png_chunk_type_text['sample_depth_bits']                = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 1));
+					$sPLToffset += 1;
+					$thisfile_png_chunk_type_text['sample_depth_bytes']               = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8;
+					$paletteCounter = 0;
+					while ($sPLToffset < strlen($otherdata)) {
+						$thisfile_png_chunk_type_text['red'][$paletteCounter]       = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
+						$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
+						$thisfile_png_chunk_type_text['green'][$paletteCounter]     = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
+						$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
+						$thisfile_png_chunk_type_text['blue'][$paletteCounter]      = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
+						$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
+						$thisfile_png_chunk_type_text['alpha'][$paletteCounter]     = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
+						$sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
+						$thisfile_png_chunk_type_text['frequency'][$paletteCounter] = getid3_lib::BigEndian2Int(substr($otherdata, $sPLToffset, 2));
+						$sPLToffset += 2;
+						$paletteCounter++;
+					}
+					break;
+
+
+				case 'hIST': // Palette Histogram
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					$hISTcounter = 0;
+					while ($hISTcounter < strlen($chunk['data'])) {
+						$thisfile_png_chunk_type_text[$hISTcounter] = getid3_lib::BigEndian2Int(substr($chunk['data'], $hISTcounter / 2, 2));
+						$hISTcounter += 2;
+					}
+					break;
+
+
+				case 'tIME': // Image Last-Modification Time
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					$thisfile_png_chunk_type_text['year']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 2));
+					$thisfile_png_chunk_type_text['month']  = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 1));
+					$thisfile_png_chunk_type_text['day']    = getid3_lib::BigEndian2Int(substr($chunk['data'], 3, 1));
+					$thisfile_png_chunk_type_text['hour']   = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 1));
+					$thisfile_png_chunk_type_text['minute'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 5, 1));
+					$thisfile_png_chunk_type_text['second'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 6, 1));
+					$thisfile_png_chunk_type_text['unix']   = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']);
+					break;
+
+
+				case 'oFFs': // Image Offset
+					$thisfile_png_chunk_type_text['header']         = $chunk;
+					$thisfile_png_chunk_type_text['position_x']     = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 4), false, true);
+					$thisfile_png_chunk_type_text['position_y']     = getid3_lib::BigEndian2Int(substr($chunk['data'], 4, 4), false, true);
+					$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 8, 1));
+					$thisfile_png_chunk_type_text['unit']           = $this->PNGoFFsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
+					break;
+
+
+				case 'pCAL': // Calibration Of Pixel Values
+					$thisfile_png_chunk_type_text['header']             = $chunk;
+					list($calibrationname, $otherdata)                              = explode("\x00", $chunk['data'], 2);
+					$thisfile_png_chunk_type_text['calibration_name']   = $calibrationname;
+					$pCALoffset = 0;
+					$thisfile_png_chunk_type_text['original_zero']      = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
+					$pCALoffset += 4;
+					$thisfile_png_chunk_type_text['original_max']       = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
+					$pCALoffset += 4;
+					$thisfile_png_chunk_type_text['equation_type']      = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
+					$pCALoffset += 1;
+					$thisfile_png_chunk_type_text['equation_type_text'] = $this->PNGpCALequationTypeLookup($thisfile_png_chunk_type_text['equation_type']);
+					$thisfile_png_chunk_type_text['parameter_count']    = getid3_lib::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
+					$pCALoffset += 1;
+					$thisfile_png_chunk_type_text['parameters']         = explode("\x00", substr($chunk['data'], $pCALoffset));
+					break;
+
+
+				case 'sCAL': // Physical Scale Of Image Subject
+					$thisfile_png_chunk_type_text['header']         = $chunk;
+					$thisfile_png_chunk_type_text['unit_specifier'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
+					$thisfile_png_chunk_type_text['unit']           = $this->PNGsCALUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
+					list($pixelwidth, $pixelheight)                             = explode("\x00", substr($chunk['data'], 1));
+					$thisfile_png_chunk_type_text['pixel_width']    = $pixelwidth;
+					$thisfile_png_chunk_type_text['pixel_height']   = $pixelheight;
+					break;
+
+
+				case 'gIFg': // GIF Graphic Control Extension
+					$gIFgCounter = 0;
+					if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
+						$gIFgCounter = count($thisfile_png_chunk_type_text);
+					}
+					$thisfile_png_chunk_type_text[$gIFgCounter]['header']          = $chunk;
+					$thisfile_png_chunk_type_text[$gIFgCounter]['disposal_method'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 0, 1));
+					$thisfile_png_chunk_type_text[$gIFgCounter]['user_input_flag'] = getid3_lib::BigEndian2Int(substr($chunk['data'], 1, 1));
+					$thisfile_png_chunk_type_text[$gIFgCounter]['delay_time']      = getid3_lib::BigEndian2Int(substr($chunk['data'], 2, 2));
+					break;
+
+
+				case 'gIFx': // GIF Application Extension
+					$gIFxCounter = 0;
+					if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
+						$gIFxCounter = count($thisfile_png_chunk_type_text);
+					}
+					$thisfile_png_chunk_type_text[$gIFxCounter]['header']                 = $chunk;
+					$thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($chunk['data'],  0, 8);
+					$thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code']    = substr($chunk['data'],  8, 3);
+					$thisfile_png_chunk_type_text[$gIFxCounter]['application_data']       = substr($chunk['data'], 11);
+					break;
+
+
+				case 'IDAT': // Image Data
+					$idatinformationfieldindex = 0;
+					if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) {
+						$idatinformationfieldindex = count($thisfile_png['IDAT']);
+					}
+					unset($chunk['data']);
+					$thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk;
+					break;
+
+
+				case 'IEND': // Image Trailer
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					break;
+
+
+				default:
+					//unset($chunk['data']);
+					$thisfile_png_chunk_type_text['header'] = $chunk;
+					$ThisFileInfo['warning'][] = 'Unhandled chunk type: '.$chunk['type_text'];
+					break;
+			}
+		}
+
+		return true;
+	}
+
+	function PNGsRGBintentLookup($sRGB) {
+		static $PNGsRGBintentLookup = array(
+			0 => 'Perceptual',
+			1 => 'Relative colorimetric',
+			2 => 'Saturation',
+			3 => 'Absolute colorimetric'
+		);
+		return (isset($PNGsRGBintentLookup[$sRGB]) ? $PNGsRGBintentLookup[$sRGB] : 'invalid');
+	}
+
+	function PNGcompressionMethodLookup($compressionmethod) {
+		static $PNGcompressionMethodLookup = array(
+			0 => 'deflate/inflate'
+		);
+		return (isset($PNGcompressionMethodLookup[$compressionmethod]) ? $PNGcompressionMethodLookup[$compressionmethod] : 'invalid');
+	}
+
+	function PNGpHYsUnitLookup($unitid) {
+		static $PNGpHYsUnitLookup = array(
+			0 => 'unknown',
+			1 => 'meter'
+		);
+		return (isset($PNGpHYsUnitLookup[$unitid]) ? $PNGpHYsUnitLookup[$unitid] : 'invalid');
+	}
+
+	function PNGoFFsUnitLookup($unitid) {
+		static $PNGoFFsUnitLookup = array(
+			0 => 'pixel',
+			1 => 'micrometer'
+		);
+		return (isset($PNGoFFsUnitLookup[$unitid]) ? $PNGoFFsUnitLookup[$unitid] : 'invalid');
+	}
+
+	function PNGpCALequationTypeLookup($equationtype) {
+		static $PNGpCALequationTypeLookup = array(
+			0 => 'Linear mapping',
+			1 => 'Base-e exponential mapping',
+			2 => 'Arbitrary-base exponential mapping',
+			3 => 'Hyperbolic mapping'
+		);
+		return (isset($PNGpCALequationTypeLookup[$equationtype]) ? $PNGpCALequationTypeLookup[$equationtype] : 'invalid');
+	}
+
+	function PNGsCALUnitLookup($unitid) {
+		static $PNGsCALUnitLookup = array(
+			0 => 'meter',
+			1 => 'radian'
+		);
+		return (isset($PNGsCALUnitLookup[$unitid]) ? $PNGsCALUnitLookup[$unitid] : 'invalid');
+	}
+
+	function IHDRcalculateBitsPerSample($color_type, $bit_depth) {
+		switch ($color_type) {
+			case 0: // Each pixel is a grayscale sample.
+				return $bit_depth;
+				break;
+
+			case 2: // Each pixel is an R,G,B triple
+				return 3 * $bit_depth;
+				break;
+
+			case 3: // Each pixel is a palette index; a PLTE chunk must appear.
+				return $bit_depth;
+				break;
+
+			case 4: // Each pixel is a grayscale sample, followed by an alpha sample.
+				return 2 * $bit_depth;
+				break;
+
+			case 6: // Each pixel is an R,G,B triple, followed by an alpha sample.
+				return 4 * $bit_depth;
+				break;
+		}
+		return false;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.svg.php b/apps/media/getID3/getid3/module.graphic.svg.php
new file mode 100644
index 0000000000000000000000000000000000000000..e4471456ce9c03144c18a337abd0c0b355afd22b
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.svg.php
@@ -0,0 +1,52 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.graphic.svg.php                                      //
+// module for analyzing SVG Image files                        //
+// dependencies: NONE                                          //
+// author: Bryce Harrington <bryceØbryceharrington*org>        //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_svg
+{
+
+
+	function getid3_svg(&$fd, &$ThisFileInfo) {
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+
+		// I'm making this up, please modify as appropriate
+		$SVGheader = fread($fd, 32);
+		$ThisFileInfo['svg']['magic']  = substr($SVGheader, 0, 4);
+		if ($ThisFileInfo['svg']['magic'] == 'aBcD') {
+
+			$ThisFileInfo['fileformat']                  = 'svg';
+			$ThisFileInfo['video']['dataformat']         = 'svg';
+			$ThisFileInfo['video']['lossless']           = true;
+			$ThisFileInfo['video']['bits_per_sample']    = 24;
+			$ThisFileInfo['video']['pixel_aspect_ratio'] = (float) 1;
+
+			$ThisFileInfo['svg']['width']  = getid3_lib::LittleEndian2Int(substr($fileData, 4, 4));
+			$ThisFileInfo['svg']['height'] = getid3_lib::LittleEndian2Int(substr($fileData, 8, 4));
+
+			$ThisFileInfo['video']['resolution_x'] = $ThisFileInfo['svg']['width'];
+			$ThisFileInfo['video']['resolution_y'] = $ThisFileInfo['svg']['height'];
+
+			return true;
+		}
+		$ThisFileInfo['error'][] = 'Did not find SVG magic bytes "aBcD" at '.$ThisFileInfo['avdataoffset'];
+		unset($ThisFileInfo['fileformat']);
+		return false;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.graphic.tiff.php b/apps/media/getID3/getid3/module.graphic.tiff.php
new file mode 100644
index 0000000000000000000000000000000000000000..ae57cd6ad2ce35661594478d89800f381e9f5e0e
--- /dev/null
+++ b/apps/media/getID3/getid3/module.graphic.tiff.php
@@ -0,0 +1,221 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.tiff.php                                     //
+// module for analyzing TIFF files                             //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_tiff
+{
+
+	function getid3_tiff(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$TIFFheader = fread($fd, 4);
+
+		switch (substr($TIFFheader, 0, 2)) {
+			case 'II':
+				$ThisFileInfo['tiff']['byte_order'] = 'Intel';
+				break;
+			case 'MM':
+				$ThisFileInfo['tiff']['byte_order'] = 'Motorola';
+				break;
+			default:
+				$ThisFileInfo['error'][] = 'Invalid TIFF byte order identifier ('.substr($TIFFheader, 0, 2).') at offset '.$ThisFileInfo['avdataoffset'];
+				return false;
+				break;
+		}
+
+		$ThisFileInfo['fileformat']          = 'tiff';
+		$ThisFileInfo['video']['dataformat'] = 'tiff';
+		$ThisFileInfo['video']['lossless']   = true;
+		$ThisFileInfo['tiff']['ifd']         = array();
+		$CurrentIFD                          = array();
+
+		$FieldTypeByteLength = array(1=>1, 2=>1, 3=>2, 4=>4, 5=>8);
+
+		$nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
+
+		while ($nextIFDoffset > 0) {
+
+			$CurrentIFD['offset'] = $nextIFDoffset;
+
+			fseek($fd, $ThisFileInfo['avdataoffset'] + $nextIFDoffset, SEEK_SET);
+			$CurrentIFD['fieldcount'] = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
+
+			for ($i = 0; $i < $CurrentIFD['fieldcount']; $i++) {
+				$CurrentIFD['fields'][$i]['raw']['tag']    = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
+				$CurrentIFD['fields'][$i]['raw']['type']   = $this->TIFFendian2Int(fread($fd, 2), $ThisFileInfo['tiff']['byte_order']);
+				$CurrentIFD['fields'][$i]['raw']['length'] = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
+				$CurrentIFD['fields'][$i]['raw']['offset'] =                       fread($fd, 4);
+
+				switch ($CurrentIFD['fields'][$i]['raw']['type']) {
+					case 1: // BYTE  An 8-bit unsigned integer.
+						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
+							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 1), $ThisFileInfo['tiff']['byte_order']);
+						} else {
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
+						}
+						break;
+
+					case 2: // ASCII 8-bit bytes  that store ASCII codes; the last byte must be null.
+						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 4) {
+							$CurrentIFD['fields'][$i]['value']  = substr($CurrentIFD['fields'][$i]['raw']['offset'], 3);
+						} else {
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
+						}
+						break;
+
+					case 3: // SHORT A 16-bit (2-byte) unsigned integer.
+						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 2) {
+							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int(substr($CurrentIFD['fields'][$i]['raw']['offset'], 0, 2), $ThisFileInfo['tiff']['byte_order']);
+						} else {
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
+						}
+						break;
+
+					case 4: // LONG  A 32-bit (4-byte) unsigned integer.
+						if ($CurrentIFD['fields'][$i]['raw']['length'] <= 1) {
+							$CurrentIFD['fields'][$i]['value']  = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
+						} else {
+							$CurrentIFD['fields'][$i]['offset'] = $this->TIFFendian2Int($CurrentIFD['fields'][$i]['raw']['offset'], $ThisFileInfo['tiff']['byte_order']);
+						}
+						break;
+
+					case 5: // RATIONAL   Two LONG_s:  the first represents the numerator of a fraction, the second the denominator.
+						break;
+				}
+			}
+
+			$ThisFileInfo['tiff']['ifd'][] = $CurrentIFD;
+			$CurrentIFD = array();
+			$nextIFDoffset = $this->TIFFendian2Int(fread($fd, 4), $ThisFileInfo['tiff']['byte_order']);
+
+		}
+
+		foreach ($ThisFileInfo['tiff']['ifd'] as $IFDid => $IFDarray) {
+			foreach ($IFDarray['fields'] as $key => $fieldarray) {
+				switch ($fieldarray['raw']['tag']) {
+					case 256: // ImageWidth
+					case 257: // ImageLength
+					case 258: // BitsPerSample
+					case 259: // Compression
+						if (!isset($fieldarray['value'])) {
+							fseek($fd, $fieldarray['offset'], SEEK_SET);
+							$ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
+
+						}
+						break;
+
+					case 270: // ImageDescription
+					case 271: // Make
+					case 272: // Model
+					case 305: // Software
+					case 306: // DateTime
+					case 315: // Artist
+					case 316: // HostComputer
+						if (isset($fieldarray['value'])) {
+							$ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = $fieldarray['value'];
+						} else {
+							fseek($fd, $fieldarray['offset'], SEEK_SET);
+							$ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'] = fread($fd, $fieldarray['raw']['length'] * $FieldTypeByteLength[$fieldarray['raw']['type']]);
+
+						}
+						break;
+				}
+				switch ($fieldarray['raw']['tag']) {
+					case 256: // ImageWidth
+						$ThisFileInfo['video']['resolution_x'] = $fieldarray['value'];
+						break;
+
+					case 257: // ImageLength
+						$ThisFileInfo['video']['resolution_y'] = $fieldarray['value'];
+						break;
+
+					case 258: // BitsPerSample
+						if (isset($fieldarray['value'])) {
+							$ThisFileInfo['video']['bits_per_sample'] = $fieldarray['value'];
+						} else {
+							$ThisFileInfo['video']['bits_per_sample'] = 0;
+							for ($i = 0; $i < $fieldarray['raw']['length']; $i++) {
+								$ThisFileInfo['video']['bits_per_sample'] += $this->TIFFendian2Int(substr($ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'], $i * $FieldTypeByteLength[$fieldarray['raw']['type']], $FieldTypeByteLength[$fieldarray['raw']['type']]), $ThisFileInfo['tiff']['byte_order']);
+							}
+						}
+						break;
+
+					case 259: // Compression
+						$ThisFileInfo['video']['codec'] = $this->TIFFcompressionMethod($fieldarray['value']);
+						break;
+
+					case 270: // ImageDescription
+					case 271: // Make
+					case 272: // Model
+					case 305: // Software
+					case 306: // DateTime
+					case 315: // Artist
+					case 316: // HostComputer
+						@$ThisFileInfo['tiff']['comments'][$this->TIFFcommentName($fieldarray['raw']['tag'])][] = $ThisFileInfo['tiff']['ifd'][$IFDid]['fields'][$key]['raw']['data'];
+						break;
+
+					default:
+						break;
+				}
+			}
+		}
+
+		return true;
+	}
+
+
+	function TIFFendian2Int($bytestring, $byteorder) {
+		if ($byteorder == 'Intel') {
+			return getid3_lib::LittleEndian2Int($bytestring);
+		} elseif ($byteorder == 'Motorola') {
+			return getid3_lib::BigEndian2Int($bytestring);
+		}
+		return false;
+	}
+
+	function TIFFcompressionMethod($id) {
+		static $TIFFcompressionMethod = array();
+		if (empty($TIFFcompressionMethod)) {
+			$TIFFcompressionMethod = array(
+				1     => 'Uncompressed',
+				2     => 'Huffman',
+				3     => 'Fax - CCITT 3',
+				5     => 'LZW',
+				32773 => 'PackBits',
+			);
+		}
+		return (isset($TIFFcompressionMethod[$id]) ? $TIFFcompressionMethod[$id] : 'unknown/invalid ('.$id.')');
+	}
+
+	function TIFFcommentName($id) {
+		static $TIFFcommentName = array();
+		if (empty($TIFFcommentName)) {
+			$TIFFcommentName = array(
+				270 => 'imagedescription',
+				271 => 'make',
+				272 => 'model',
+				305 => 'software',
+				306 => 'datetime',
+				315 => 'artist',
+				316 => 'hostcomputer',
+			);
+		}
+		return (isset($TIFFcommentName[$id]) ? $TIFFcommentName[$id] : 'unknown/invalid ('.$id.')');
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.misc.doc.php b/apps/media/getID3/getid3/module.misc.doc.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb65abf22371cf44f76c8099d49fe4f10154e5f1
--- /dev/null
+++ b/apps/media/getID3/getid3/module.misc.doc.php
@@ -0,0 +1,32 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.doc.php                                      //
+// module for analyzing MS Office (.doc, .xls, etc) files      //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_doc
+{
+
+	function getid3_doc(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat'] = 'doc';
+
+		$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
+		return false;
+
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.misc.exe.php b/apps/media/getID3/getid3/module.misc.exe.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c6bfcf99fbdf680eb3c2ad13d7e26d157e0fb92
--- /dev/null
+++ b/apps/media/getID3/getid3/module.misc.exe.php
@@ -0,0 +1,59 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.misc.exe.php                                         //
+// module for analyzing EXE files                              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_exe
+{
+
+	function getid3_exe(&$fd, &$ThisFileInfo) {
+
+		fseek($fd, $ThisFileInfo['avdataoffset'], SEEK_SET);
+		$EXEheader = fread($fd, 28);
+
+		if (substr($EXEheader, 0, 2) != 'MZ') {
+			$ThisFileInfo['error'][] = 'Expecting "MZ" at offset '.$ThisFileInfo['avdataoffset'].', found "'.substr($EXEheader, 0, 2).'" instead.';
+			return false;
+		}
+
+		$ThisFileInfo['fileformat'] = 'exe';
+		$ThisFileInfo['exe']['mz']['magic'] = 'MZ';
+
+		$ThisFileInfo['exe']['mz']['raw']['last_page_size']          = getid3_lib::LittleEndian2Int(substr($EXEheader,  2, 2));
+		$ThisFileInfo['exe']['mz']['raw']['page_count']              = getid3_lib::LittleEndian2Int(substr($EXEheader,  4, 2));
+		$ThisFileInfo['exe']['mz']['raw']['relocation_count']        = getid3_lib::LittleEndian2Int(substr($EXEheader,  6, 2));
+		$ThisFileInfo['exe']['mz']['raw']['header_paragraphs']       = getid3_lib::LittleEndian2Int(substr($EXEheader,  8, 2));
+		$ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs']   = getid3_lib::LittleEndian2Int(substr($EXEheader, 10, 2));
+		$ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs']   = getid3_lib::LittleEndian2Int(substr($EXEheader, 12, 2));
+		$ThisFileInfo['exe']['mz']['raw']['initial_ss']              = getid3_lib::LittleEndian2Int(substr($EXEheader, 14, 2));
+		$ThisFileInfo['exe']['mz']['raw']['initial_sp']              = getid3_lib::LittleEndian2Int(substr($EXEheader, 16, 2));
+		$ThisFileInfo['exe']['mz']['raw']['checksum']                = getid3_lib::LittleEndian2Int(substr($EXEheader, 18, 2));
+		$ThisFileInfo['exe']['mz']['raw']['cs_ip']                   = getid3_lib::LittleEndian2Int(substr($EXEheader, 20, 4));
+		$ThisFileInfo['exe']['mz']['raw']['relocation_table_offset'] = getid3_lib::LittleEndian2Int(substr($EXEheader, 24, 2));
+		$ThisFileInfo['exe']['mz']['raw']['overlay_number']          = getid3_lib::LittleEndian2Int(substr($EXEheader, 26, 2));
+
+		$ThisFileInfo['exe']['mz']['byte_size']          = (($ThisFileInfo['exe']['mz']['raw']['page_count'] - 1)) * 512 + $ThisFileInfo['exe']['mz']['raw']['last_page_size'];
+		$ThisFileInfo['exe']['mz']['header_size']        = $ThisFileInfo['exe']['mz']['raw']['header_paragraphs'] * 16;
+		$ThisFileInfo['exe']['mz']['memory_minimum']     = $ThisFileInfo['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
+		$ThisFileInfo['exe']['mz']['memory_recommended'] = $ThisFileInfo['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
+
+$ThisFileInfo['error'][] = 'EXE parsing not enabled in this version of getID3()';
+return false;
+
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.misc.iso.php b/apps/media/getID3/getid3/module.misc.iso.php
new file mode 100644
index 0000000000000000000000000000000000000000..94df9294f4e07c1fc48b415c00a7bf48a8a56038
--- /dev/null
+++ b/apps/media/getID3/getid3/module.misc.iso.php
@@ -0,0 +1,386 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.misc.iso.php                                         //
+// module for analyzing ISO files                              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_iso
+{
+
+	function getid3_iso($fd, &$ThisFileInfo) {
+		$ThisFileInfo['fileformat'] = 'iso';
+
+		for ($i = 16; $i <= 19; $i++) {
+			fseek($fd, 2048 * $i, SEEK_SET);
+			$ISOheader = fread($fd, 2048);
+			if (substr($ISOheader, 1, 5) == 'CD001') {
+				switch (ord($ISOheader{0})) {
+					case 1:
+						$ThisFileInfo['iso']['primary_volume_descriptor']['offset'] = 2048 * $i;
+						$this->ParsePrimaryVolumeDescriptor($ISOheader, $ThisFileInfo);
+						break;
+
+					case 2:
+						$ThisFileInfo['iso']['supplementary_volume_descriptor']['offset'] = 2048 * $i;
+						$this->ParseSupplementaryVolumeDescriptor($ISOheader, $ThisFileInfo);
+						break;
+
+					default:
+						// skip
+						break;
+				}
+			}
+		}
+
+		$this->ParsePathTable($fd, $ThisFileInfo);
+
+		$ThisFileInfo['iso']['files'] = array();
+		foreach ($ThisFileInfo['iso']['path_table']['directories'] as $directorynum => $directorydata) {
+
+			$ThisFileInfo['iso']['directories'][$directorynum] = $this->ParseDirectoryRecord($fd, $directorydata, $ThisFileInfo);
+
+		}
+
+		return true;
+
+	}
+
+
+	function ParsePrimaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
+		// ISO integer values are stored *BOTH* Little-Endian AND Big-Endian format!!
+		// ie 12345 == 0x3039  is stored as $39 $30 $30 $39 in a 4-byte field
+
+		// shortcuts
+		$ThisFileInfo['iso']['primary_volume_descriptor']['raw'] = array();
+		$thisfile_iso_primaryVD     = &$ThisFileInfo['iso']['primary_volume_descriptor'];
+		$thisfile_iso_primaryVD_raw = &$thisfile_iso_primaryVD['raw'];
+
+		$thisfile_iso_primaryVD_raw['volume_descriptor_type']         = getid3_lib::LittleEndian2Int(substr($ISOheader,    0, 1));
+		$thisfile_iso_primaryVD_raw['standard_identifier']            =                  substr($ISOheader,    1, 5);
+		if ($thisfile_iso_primaryVD_raw['standard_identifier'] != 'CD001') {
+			$ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_primaryVD['offset'] + 1).'), found "'.$thisfile_iso_primaryVD_raw['standard_identifier'].'" instead';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['iso']);
+			return false;
+		}
+
+
+		$thisfile_iso_primaryVD_raw['volume_descriptor_version']     = getid3_lib::LittleEndian2Int(substr($ISOheader,    6, 1));
+		//$thisfile_iso_primaryVD_raw['unused_1']                      =                              substr($ISOheader,    7, 1);
+		$thisfile_iso_primaryVD_raw['system_identifier']             =                              substr($ISOheader,    8, 32);
+		$thisfile_iso_primaryVD_raw['volume_identifier']             =                              substr($ISOheader,   40, 32);
+		//$thisfile_iso_primaryVD_raw['unused_2']                      =                              substr($ISOheader,   72, 8);
+		$thisfile_iso_primaryVD_raw['volume_space_size']             = getid3_lib::LittleEndian2Int(substr($ISOheader,   80, 4));
+		//$thisfile_iso_primaryVD_raw['unused_3']                      =                              substr($ISOheader,   88, 32);
+		$thisfile_iso_primaryVD_raw['volume_set_size']               = getid3_lib::LittleEndian2Int(substr($ISOheader,  120, 2));
+		$thisfile_iso_primaryVD_raw['volume_sequence_number']        = getid3_lib::LittleEndian2Int(substr($ISOheader,  124, 2));
+		$thisfile_iso_primaryVD_raw['logical_block_size']            = getid3_lib::LittleEndian2Int(substr($ISOheader,  128, 2));
+		$thisfile_iso_primaryVD_raw['path_table_size']               = getid3_lib::LittleEndian2Int(substr($ISOheader,  132, 4));
+		$thisfile_iso_primaryVD_raw['path_table_l_location']         = getid3_lib::LittleEndian2Int(substr($ISOheader,  140, 2));
+		$thisfile_iso_primaryVD_raw['path_table_l_opt_location']     = getid3_lib::LittleEndian2Int(substr($ISOheader,  144, 2));
+		$thisfile_iso_primaryVD_raw['path_table_m_location']         = getid3_lib::LittleEndian2Int(substr($ISOheader,  148, 2));
+		$thisfile_iso_primaryVD_raw['path_table_m_opt_location']     = getid3_lib::LittleEndian2Int(substr($ISOheader,  152, 2));
+		$thisfile_iso_primaryVD_raw['root_directory_record']         =                              substr($ISOheader,  156, 34);
+		$thisfile_iso_primaryVD_raw['volume_set_identifier']         =                              substr($ISOheader,  190, 128);
+		$thisfile_iso_primaryVD_raw['publisher_identifier']          =                              substr($ISOheader,  318, 128);
+		$thisfile_iso_primaryVD_raw['data_preparer_identifier']      =                              substr($ISOheader,  446, 128);
+		$thisfile_iso_primaryVD_raw['application_identifier']        =                              substr($ISOheader,  574, 128);
+		$thisfile_iso_primaryVD_raw['copyright_file_identifier']     =                              substr($ISOheader,  702, 37);
+		$thisfile_iso_primaryVD_raw['abstract_file_identifier']      =                              substr($ISOheader,  739, 37);
+		$thisfile_iso_primaryVD_raw['bibliographic_file_identifier'] =                              substr($ISOheader,  776, 37);
+		$thisfile_iso_primaryVD_raw['volume_creation_date_time']     =                              substr($ISOheader,  813, 17);
+		$thisfile_iso_primaryVD_raw['volume_modification_date_time'] =                              substr($ISOheader,  830, 17);
+		$thisfile_iso_primaryVD_raw['volume_expiration_date_time']   =                              substr($ISOheader,  847, 17);
+		$thisfile_iso_primaryVD_raw['volume_effective_date_time']    =                              substr($ISOheader,  864, 17);
+		$thisfile_iso_primaryVD_raw['file_structure_version']        = getid3_lib::LittleEndian2Int(substr($ISOheader,  881, 1));
+		//$thisfile_iso_primaryVD_raw['unused_4']                      = getid3_lib::LittleEndian2Int(substr($ISOheader,  882, 1));
+		$thisfile_iso_primaryVD_raw['application_data']              =                              substr($ISOheader,  883, 512);
+		//$thisfile_iso_primaryVD_raw['unused_5']                      =                              substr($ISOheader, 1395, 653);
+
+		$thisfile_iso_primaryVD['system_identifier']             = trim($thisfile_iso_primaryVD_raw['system_identifier']);
+		$thisfile_iso_primaryVD['volume_identifier']             = trim($thisfile_iso_primaryVD_raw['volume_identifier']);
+		$thisfile_iso_primaryVD['volume_set_identifier']         = trim($thisfile_iso_primaryVD_raw['volume_set_identifier']);
+		$thisfile_iso_primaryVD['publisher_identifier']          = trim($thisfile_iso_primaryVD_raw['publisher_identifier']);
+		$thisfile_iso_primaryVD['data_preparer_identifier']      = trim($thisfile_iso_primaryVD_raw['data_preparer_identifier']);
+		$thisfile_iso_primaryVD['application_identifier']        = trim($thisfile_iso_primaryVD_raw['application_identifier']);
+		$thisfile_iso_primaryVD['copyright_file_identifier']     = trim($thisfile_iso_primaryVD_raw['copyright_file_identifier']);
+		$thisfile_iso_primaryVD['abstract_file_identifier']      = trim($thisfile_iso_primaryVD_raw['abstract_file_identifier']);
+		$thisfile_iso_primaryVD['bibliographic_file_identifier'] = trim($thisfile_iso_primaryVD_raw['bibliographic_file_identifier']);
+		$thisfile_iso_primaryVD['volume_creation_date_time']     = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_creation_date_time']);
+		$thisfile_iso_primaryVD['volume_modification_date_time'] = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_modification_date_time']);
+		$thisfile_iso_primaryVD['volume_expiration_date_time']   = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_expiration_date_time']);
+		$thisfile_iso_primaryVD['volume_effective_date_time']    = $this->ISOtimeText2UNIXtime($thisfile_iso_primaryVD_raw['volume_effective_date_time']);
+
+		if (($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048) > $ThisFileInfo['filesize']) {
+			$ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_primaryVD_raw['volume_space_size'] * 2048).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)';
+		}
+
+		return true;
+	}
+
+
+	function ParseSupplementaryVolumeDescriptor(&$ISOheader, &$ThisFileInfo) {
+		// ISO integer values are stored Both-Endian format!!
+		// ie 12345 == 0x3039  is stored as $39 $30 $30 $39 in a 4-byte field
+
+		// shortcuts
+		$ThisFileInfo['iso']['supplementary_volume_descriptor']['raw'] = array();
+		$thisfile_iso_supplementaryVD     = &$ThisFileInfo['iso']['supplementary_volume_descriptor'];
+		$thisfile_iso_supplementaryVD_raw = &$thisfile_iso_supplementaryVD['raw'];
+
+		$thisfile_iso_supplementaryVD_raw['volume_descriptor_type'] = getid3_lib::LittleEndian2Int(substr($ISOheader,    0, 1));
+		$thisfile_iso_supplementaryVD_raw['standard_identifier']    =                  substr($ISOheader,    1, 5);
+		if ($thisfile_iso_supplementaryVD_raw['standard_identifier'] != 'CD001') {
+			$ThisFileInfo['error'][] = 'Expected "CD001" at offset ('.($thisfile_iso_supplementaryVD['offset'] + 1).'), found "'.$thisfile_iso_supplementaryVD_raw['standard_identifier'].'" instead';
+			unset($ThisFileInfo['fileformat']);
+			unset($ThisFileInfo['iso']);
+			return false;
+		}
+
+		$thisfile_iso_supplementaryVD_raw['volume_descriptor_version'] = getid3_lib::LittleEndian2Int(substr($ISOheader,    6, 1));
+		//$thisfile_iso_supplementaryVD_raw['unused_1']                  =                              substr($ISOheader,    7, 1);
+		$thisfile_iso_supplementaryVD_raw['system_identifier']         =                              substr($ISOheader,    8, 32);
+		$thisfile_iso_supplementaryVD_raw['volume_identifier']         =                              substr($ISOheader,   40, 32);
+		//$thisfile_iso_supplementaryVD_raw['unused_2']                  =                              substr($ISOheader,   72, 8);
+		$thisfile_iso_supplementaryVD_raw['volume_space_size']         = getid3_lib::LittleEndian2Int(substr($ISOheader,   80, 4));
+		if ($thisfile_iso_supplementaryVD_raw['volume_space_size'] == 0) {
+			// Supplementary Volume Descriptor not used
+			//unset($thisfile_iso_supplementaryVD);
+			//return false;
+		}
+
+		//$thisfile_iso_supplementaryVD_raw['unused_3']                       =                              substr($ISOheader,   88, 32);
+		$thisfile_iso_supplementaryVD_raw['volume_set_size']                = getid3_lib::LittleEndian2Int(substr($ISOheader,  120, 2));
+		$thisfile_iso_supplementaryVD_raw['volume_sequence_number']         = getid3_lib::LittleEndian2Int(substr($ISOheader,  124, 2));
+		$thisfile_iso_supplementaryVD_raw['logical_block_size']             = getid3_lib::LittleEndian2Int(substr($ISOheader,  128, 2));
+		$thisfile_iso_supplementaryVD_raw['path_table_size']                = getid3_lib::LittleEndian2Int(substr($ISOheader,  132, 4));
+		$thisfile_iso_supplementaryVD_raw['path_table_l_location']          = getid3_lib::LittleEndian2Int(substr($ISOheader,  140, 2));
+		$thisfile_iso_supplementaryVD_raw['path_table_l_opt_location']      = getid3_lib::LittleEndian2Int(substr($ISOheader,  144, 2));
+		$thisfile_iso_supplementaryVD_raw['path_table_m_location']          = getid3_lib::LittleEndian2Int(substr($ISOheader,  148, 2));
+		$thisfile_iso_supplementaryVD_raw['path_table_m_opt_location']      = getid3_lib::LittleEndian2Int(substr($ISOheader,  152, 2));
+		$thisfile_iso_supplementaryVD_raw['root_directory_record']          =                              substr($ISOheader,  156, 34);
+		$thisfile_iso_supplementaryVD_raw['volume_set_identifier']          =                              substr($ISOheader,  190, 128);
+		$thisfile_iso_supplementaryVD_raw['publisher_identifier']           =                              substr($ISOheader,  318, 128);
+		$thisfile_iso_supplementaryVD_raw['data_preparer_identifier']       =                              substr($ISOheader,  446, 128);
+		$thisfile_iso_supplementaryVD_raw['application_identifier']         =                              substr($ISOheader,  574, 128);
+		$thisfile_iso_supplementaryVD_raw['copyright_file_identifier']      =                              substr($ISOheader,  702, 37);
+		$thisfile_iso_supplementaryVD_raw['abstract_file_identifier']       =                              substr($ISOheader,  739, 37);
+		$thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']  =                              substr($ISOheader,  776, 37);
+		$thisfile_iso_supplementaryVD_raw['volume_creation_date_time']      =                              substr($ISOheader,  813, 17);
+		$thisfile_iso_supplementaryVD_raw['volume_modification_date_time']  =                              substr($ISOheader,  830, 17);
+		$thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']    =                              substr($ISOheader,  847, 17);
+		$thisfile_iso_supplementaryVD_raw['volume_effective_date_time']     =                              substr($ISOheader,  864, 17);
+		$thisfile_iso_supplementaryVD_raw['file_structure_version']         = getid3_lib::LittleEndian2Int(substr($ISOheader,  881, 1));
+		//$thisfile_iso_supplementaryVD_raw['unused_4']                       = getid3_lib::LittleEndian2Int(substr($ISOheader,  882, 1));
+		$thisfile_iso_supplementaryVD_raw['application_data']               =                              substr($ISOheader,  883, 512);
+		//$thisfile_iso_supplementaryVD_raw['unused_5']                       =                              substr($ISOheader, 1395, 653);
+
+		$thisfile_iso_supplementaryVD['system_identifier']              = trim($thisfile_iso_supplementaryVD_raw['system_identifier']);
+		$thisfile_iso_supplementaryVD['volume_identifier']              = trim($thisfile_iso_supplementaryVD_raw['volume_identifier']);
+		$thisfile_iso_supplementaryVD['volume_set_identifier']          = trim($thisfile_iso_supplementaryVD_raw['volume_set_identifier']);
+		$thisfile_iso_supplementaryVD['publisher_identifier']           = trim($thisfile_iso_supplementaryVD_raw['publisher_identifier']);
+		$thisfile_iso_supplementaryVD['data_preparer_identifier']       = trim($thisfile_iso_supplementaryVD_raw['data_preparer_identifier']);
+		$thisfile_iso_supplementaryVD['application_identifier']         = trim($thisfile_iso_supplementaryVD_raw['application_identifier']);
+		$thisfile_iso_supplementaryVD['copyright_file_identifier']      = trim($thisfile_iso_supplementaryVD_raw['copyright_file_identifier']);
+		$thisfile_iso_supplementaryVD['abstract_file_identifier']       = trim($thisfile_iso_supplementaryVD_raw['abstract_file_identifier']);
+		$thisfile_iso_supplementaryVD['bibliographic_file_identifier']  = trim($thisfile_iso_supplementaryVD_raw['bibliographic_file_identifier']);
+		$thisfile_iso_supplementaryVD['volume_creation_date_time']      = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_creation_date_time']);
+		$thisfile_iso_supplementaryVD['volume_modification_date_time']  = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_modification_date_time']);
+		$thisfile_iso_supplementaryVD['volume_expiration_date_time']    = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_expiration_date_time']);
+		$thisfile_iso_supplementaryVD['volume_effective_date_time']     = $this->ISOtimeText2UNIXtime($thisfile_iso_supplementaryVD_raw['volume_effective_date_time']);
+
+		if (($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']) > $ThisFileInfo['filesize']) {
+			$ThisFileInfo['error'][] = 'Volume Space Size ('.($thisfile_iso_supplementaryVD_raw['volume_space_size'] * $thisfile_iso_supplementaryVD_raw['logical_block_size']).' bytes) is larger than the file size ('.$ThisFileInfo['filesize'].' bytes) (truncated file?)';
+		}
+
+		return true;
+	}
+
+
+	function ParsePathTable($fd, &$ThisFileInfo) {
+		if (!isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location']) && !isset($ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'])) {
+			return false;
+		}
+		if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'])) {
+			$PathTableLocation = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_l_location'];
+			$PathTableSize     = $ThisFileInfo['iso']['supplementary_volume_descriptor']['raw']['path_table_size'];
+			$TextEncoding      = 'UTF-16BE'; // Big-Endian Unicode
+		} else {
+			$PathTableLocation = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_l_location'];
+			$PathTableSize     = $ThisFileInfo['iso']['primary_volume_descriptor']['raw']['path_table_size'];
+			$TextEncoding      = 'ISO-8859-1'; // Latin-1
+		}
+
+		if (($PathTableLocation * 2048) > $ThisFileInfo['filesize']) {
+			$ThisFileInfo['error'][] = 'Path Table Location specifies an offset ('.($PathTableLocation * 2048).') beyond the end-of-file ('.$ThisFileInfo['filesize'].')';
+			return false;
+		}
+
+		$ThisFileInfo['iso']['path_table']['offset'] = $PathTableLocation * 2048;
+		fseek($fd, $ThisFileInfo['iso']['path_table']['offset'], SEEK_SET);
+		$ThisFileInfo['iso']['path_table']['raw'] = fread($fd, $PathTableSize);
+
+		$offset = 0;
+		$pathcounter = 1;
+		while ($offset < $PathTableSize) {
+			// shortcut
+			$ThisFileInfo['iso']['path_table']['directories'][$pathcounter] = array();
+			$thisfile_iso_pathtable_directories_current = &$ThisFileInfo['iso']['path_table']['directories'][$pathcounter];
+
+			$thisfile_iso_pathtable_directories_current['length']           = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1));
+			$offset += 1;
+			$thisfile_iso_pathtable_directories_current['extended_length']  = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 1));
+			$offset += 1;
+			$thisfile_iso_pathtable_directories_current['location_logical'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 4));
+			$offset += 4;
+			$thisfile_iso_pathtable_directories_current['parent_directory'] = getid3_lib::LittleEndian2Int(substr($ThisFileInfo['iso']['path_table']['raw'], $offset, 2));
+			$offset += 2;
+			$thisfile_iso_pathtable_directories_current['name']             =                  substr($ThisFileInfo['iso']['path_table']['raw'], $offset, $thisfile_iso_pathtable_directories_current['length']);
+			$offset += $thisfile_iso_pathtable_directories_current['length'] + ($thisfile_iso_pathtable_directories_current['length'] % 2);
+
+			$thisfile_iso_pathtable_directories_current['name_ascii']       = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $thisfile_iso_pathtable_directories_current['name']);
+
+			$thisfile_iso_pathtable_directories_current['location_bytes'] = $thisfile_iso_pathtable_directories_current['location_logical'] * 2048;
+			if ($pathcounter == 1) {
+				$thisfile_iso_pathtable_directories_current['full_path'] = '/';
+			} else {
+				$thisfile_iso_pathtable_directories_current['full_path'] = $ThisFileInfo['iso']['path_table']['directories'][$thisfile_iso_pathtable_directories_current['parent_directory']]['full_path'].$thisfile_iso_pathtable_directories_current['name_ascii'].'/';
+			}
+			$FullPathArray[] = $thisfile_iso_pathtable_directories_current['full_path'];
+
+			$pathcounter++;
+		}
+
+		return true;
+	}
+
+
+	function ParseDirectoryRecord(&$fd, $directorydata, &$ThisFileInfo) {
+		if (isset($ThisFileInfo['iso']['supplementary_volume_descriptor'])) {
+			$TextEncoding = 'UTF-16BE';   // Big-Endian Unicode
+		} else {
+			$TextEncoding = 'ISO-8859-1'; // Latin-1
+		}
+
+		fseek($fd, $directorydata['location_bytes'], SEEK_SET);
+		$DirectoryRecordData = fread($fd, 1);
+
+		while (ord($DirectoryRecordData{0}) > 33) {
+
+			$DirectoryRecordData .= fread($fd, ord($DirectoryRecordData{0}) - 1);
+
+			$ThisDirectoryRecord['raw']['length']                    = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData,  0, 1));
+			$ThisDirectoryRecord['raw']['extended_attribute_length'] = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData,  1, 1));
+			$ThisDirectoryRecord['raw']['offset_logical']            = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData,  2, 4));
+			$ThisDirectoryRecord['raw']['filesize']                  = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
+			$ThisDirectoryRecord['raw']['recording_date_time']       =                  substr($DirectoryRecordData, 18, 7);
+			$ThisDirectoryRecord['raw']['file_flags']                = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 25, 1));
+			$ThisDirectoryRecord['raw']['file_unit_size']            = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 26, 1));
+			$ThisDirectoryRecord['raw']['interleave_gap_size']       = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 27, 1));
+			$ThisDirectoryRecord['raw']['volume_sequence_number']    = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 28, 2));
+			$ThisDirectoryRecord['raw']['file_identifier_length']    = getid3_lib::LittleEndian2Int(substr($DirectoryRecordData, 32, 1));
+			$ThisDirectoryRecord['raw']['file_identifier']           =                  substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']);
+
+			$ThisDirectoryRecord['file_identifier_ascii']            = getid3_lib::iconv_fallback($TextEncoding, $ThisFileInfo['encoding'], $ThisDirectoryRecord['raw']['file_identifier']);
+
+			$ThisDirectoryRecord['filesize']                  = $ThisDirectoryRecord['raw']['filesize'];
+			$ThisDirectoryRecord['offset_bytes']              = $ThisDirectoryRecord['raw']['offset_logical'] * 2048;
+			$ThisDirectoryRecord['file_flags']['hidden']      = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x01);
+			$ThisDirectoryRecord['file_flags']['directory']   = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x02);
+			$ThisDirectoryRecord['file_flags']['associated']  = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x04);
+			$ThisDirectoryRecord['file_flags']['extended']    = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x08);
+			$ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10);
+			$ThisDirectoryRecord['file_flags']['multiple']    = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80);
+			$ThisDirectoryRecord['recording_timestamp']       = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']);
+
+			if ($ThisDirectoryRecord['file_flags']['directory']) {
+				$ThisDirectoryRecord['filename'] = $directorydata['full_path'];
+			} else {
+				$ThisDirectoryRecord['filename'] = $directorydata['full_path'].$this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']);
+				$ThisFileInfo['iso']['files'] = getid3_lib::array_merge_clobber($ThisFileInfo['iso']['files'], getid3_lib::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize']));
+			}
+
+			$DirectoryRecord[] = $ThisDirectoryRecord;
+			$DirectoryRecordData = fread($fd, 1);
+		}
+
+		return $DirectoryRecord;
+	}
+
+	function ISOstripFilenameVersion($ISOfilename) {
+		// convert 'filename.ext;1' to 'filename.ext'
+		if (!strstr($ISOfilename, ';')) {
+			return $ISOfilename;
+		} else {
+			return substr($ISOfilename, 0, strpos($ISOfilename, ';'));
+		}
+	}
+
+	function ISOtimeText2UNIXtime($ISOtime) {
+
+		$UNIXyear   = (int) substr($ISOtime,  0, 4);
+		$UNIXmonth  = (int) substr($ISOtime,  4, 2);
+		$UNIXday    = (int) substr($ISOtime,  6, 2);
+		$UNIXhour   = (int) substr($ISOtime,  8, 2);
+		$UNIXminute = (int) substr($ISOtime, 10, 2);
+		$UNIXsecond = (int) substr($ISOtime, 12, 2);
+
+		if (!$UNIXyear) {
+			return false;
+		}
+		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+	}
+
+	function ISOtime2UNIXtime($ISOtime) {
+		// Represented by seven bytes:
+		// 1: Number of years since 1900
+		// 2: Month of the year from 1 to 12
+		// 3: Day of the Month from 1 to 31
+		// 4: Hour of the day from 0 to 23
+		// 5: Minute of the hour from 0 to 59
+		// 6: second of the minute from 0 to 59
+		// 7: Offset from Greenwich Mean Time in number of 15 minute intervals from -48 (West) to +52 (East)
+
+		$UNIXyear   = ord($ISOtime{0}) + 1900;
+		$UNIXmonth  = ord($ISOtime{1});
+		$UNIXday    = ord($ISOtime{2});
+		$UNIXhour   = ord($ISOtime{3});
+		$UNIXminute = ord($ISOtime{4});
+		$UNIXsecond = ord($ISOtime{5});
+		$GMToffset  = $this->TwosCompliment2Decimal(ord($ISOtime{5}));
+
+		return gmmktime($UNIXhour, $UNIXminute, $UNIXsecond, $UNIXmonth, $UNIXday, $UNIXyear);
+	}
+
+	function TwosCompliment2Decimal($BinaryValue) {
+		// http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
+		// First check if the number is negative or positive by looking at the sign bit.
+		// If it is positive, simply convert it to decimal.
+		// If it is negative, make it positive by inverting the bits and adding one.
+		// Then, convert the result to decimal.
+		// The negative of this number is the value of the original binary.
+
+		if ($BinaryValue & 0x80) {
+
+			// negative number
+			return (0 - ((~$BinaryValue & 0xFF) + 1));
+		} else {
+			// positive number
+			return $BinaryValue;
+		}
+	}
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.misc.msoffice.php b/apps/media/getID3/getid3/module.misc.msoffice.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb65abf22371cf44f76c8099d49fe4f10154e5f1
--- /dev/null
+++ b/apps/media/getID3/getid3/module.misc.msoffice.php
@@ -0,0 +1,32 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.archive.doc.php                                      //
+// module for analyzing MS Office (.doc, .xls, etc) files      //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_doc
+{
+
+	function getid3_doc(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat'] = 'doc';
+
+		$ThisFileInfo['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of getID3()';
+		return false;
+
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.misc.par2.php b/apps/media/getID3/getid3/module.misc.par2.php
new file mode 100644
index 0000000000000000000000000000000000000000..9f0e22180f827e045fdf593788f55d01f282e9ef
--- /dev/null
+++ b/apps/media/getID3/getid3/module.misc.par2.php
@@ -0,0 +1,32 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.misc.par2.php                                        //
+// module for analyzing PAR2 files                             //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_par2
+{
+
+	function getid3_par2(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat'] = 'par2';
+
+		$ThisFileInfo['error'][] = 'PAR2 parsing not enabled in this version of getID3()';
+		return false;
+
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.misc.pdf.php b/apps/media/getID3/getid3/module.misc.pdf.php
new file mode 100644
index 0000000000000000000000000000000000000000..c6e3294b69537d07781363a12989a491659bdab2
--- /dev/null
+++ b/apps/media/getID3/getid3/module.misc.pdf.php
@@ -0,0 +1,32 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.misc.pdf.php                                         //
+// module for analyzing PDF files                              //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_pdf
+{
+
+	function getid3_pdf(&$fd, &$ThisFileInfo) {
+
+		$ThisFileInfo['fileformat'] = 'pdf';
+
+		$ThisFileInfo['error'][] = 'PDF parsing not enabled in this version of getID3()';
+		return false;
+
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.tag.apetag.php b/apps/media/getID3/getid3/module.tag.apetag.php
new file mode 100644
index 0000000000000000000000000000000000000000..2b67e14b4ac8badc8b0e79c02516a22d983d7668
--- /dev/null
+++ b/apps/media/getID3/getid3/module.tag.apetag.php
@@ -0,0 +1,290 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.tag.apetag.php                                       //
+// module for analyzing APE tags                               //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+class getid3_apetag
+{
+
+	function getid3_apetag(&$fd, &$ThisFileInfo, $overrideendoffset=0) {
+
+		if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
+			$ThisFileInfo['warning'][] = 'Unable to check for APEtags because file is larger than 2GB';
+			return false;
+		}
+
+		$id3v1tagsize     = 128;
+		$apetagheadersize = 32;
+		$lyrics3tagsize   = 10;
+
+		if ($overrideendoffset == 0) {
+
+			fseek($fd, 0 - $id3v1tagsize - $apetagheadersize - $lyrics3tagsize, SEEK_END);
+			$APEfooterID3v1 = fread($fd, $id3v1tagsize + $apetagheadersize + $lyrics3tagsize);
+
+			//if (preg_match('/APETAGEX.{24}TAG.{125}$/i', $APEfooterID3v1)) {
+			if (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $id3v1tagsize - $apetagheadersize, 8) == 'APETAGEX') {
+
+				// APE tag found before ID3v1
+				$ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'] - $id3v1tagsize;
+
+			//} elseif (preg_match('/APETAGEX.{24}$/i', $APEfooterID3v1)) {
+			} elseif (substr($APEfooterID3v1, strlen($APEfooterID3v1) - $apetagheadersize, 8) == 'APETAGEX') {
+
+				// APE tag found, no ID3v1
+				$ThisFileInfo['ape']['tag_offset_end'] = $ThisFileInfo['filesize'];
+
+			}
+
+		} else {
+
+			fseek($fd, $overrideendoffset - $apetagheadersize, SEEK_SET);
+			if (fread($fd, 8) == 'APETAGEX') {
+				$ThisFileInfo['ape']['tag_offset_end'] = $overrideendoffset;
+			}
+
+		}
+		if (!isset($ThisFileInfo['ape']['tag_offset_end'])) {
+
+			// APE tag not found
+			unset($ThisFileInfo['ape']);
+			return false;
+
+		}
+
+		// shortcut
+		$thisfile_ape = &$ThisFileInfo['ape'];
+
+		fseek($fd, $thisfile_ape['tag_offset_end'] - $apetagheadersize, SEEK_SET);
+		$APEfooterData = fread($fd, 32);
+		if (!($thisfile_ape['footer'] = $this->parseAPEheaderFooter($APEfooterData))) {
+			$ThisFileInfo['error'][] = 'Error parsing APE footer at offset '.$thisfile_ape['tag_offset_end'];
+			return false;
+		}
+
+		if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
+			fseek($fd, $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'] - $apetagheadersize, SEEK_SET);
+			$thisfile_ape['tag_offset_start'] = ftell($fd);
+			$APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize'] + $apetagheadersize);
+		} else {
+			$thisfile_ape['tag_offset_start'] = $thisfile_ape['tag_offset_end'] - $thisfile_ape['footer']['raw']['tagsize'];
+			fseek($fd, $thisfile_ape['tag_offset_start'], SEEK_SET);
+			$APEtagData = fread($fd, $thisfile_ape['footer']['raw']['tagsize']);
+		}
+		$ThisFileInfo['avdataend'] = $thisfile_ape['tag_offset_start'];
+
+		if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $thisfile_ape['tag_offset_end'])) {
+			$ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in APEtag data';
+			unset($ThisFileInfo['id3v1']);
+			foreach ($ThisFileInfo['warning'] as $key => $value) {
+				if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
+					unset($ThisFileInfo['warning'][$key]);
+					sort($ThisFileInfo['warning']);
+					break;
+				}
+			}
+		}
+
+		$offset = 0;
+		if (isset($thisfile_ape['footer']['flags']['header']) && $thisfile_ape['footer']['flags']['header']) {
+			if ($thisfile_ape['header'] = $this->parseAPEheaderFooter(substr($APEtagData, 0, $apetagheadersize))) {
+				$offset += $apetagheadersize;
+			} else {
+				$ThisFileInfo['error'][] = 'Error parsing APE header at offset '.$thisfile_ape['tag_offset_start'];
+				return false;
+			}
+		}
+
+		// shortcut
+		$ThisFileInfo['replay_gain'] = array();
+		$thisfile_replaygain = &$ThisFileInfo['replay_gain'];
+
+		for ($i = 0; $i < $thisfile_ape['footer']['raw']['tag_items']; $i++) {
+			$value_size = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
+			$offset += 4;
+			$item_flags = getid3_lib::LittleEndian2Int(substr($APEtagData, $offset, 4));
+			$offset += 4;
+			if (strstr(substr($APEtagData, $offset), "\x00") === false) {
+				$ThisFileInfo['error'][] = 'Cannot find null-byte (0x00) seperator between ItemKey #'.$i.' and value. ItemKey starts '.$offset.' bytes into the APE tag, at file offset '.($thisfile_ape['tag_offset_start'] + $offset);
+				return false;
+			}
+			$ItemKeyLength = strpos($APEtagData, "\x00", $offset) - $offset;
+			$item_key      = strtolower(substr($APEtagData, $offset, $ItemKeyLength));
+
+			// shortcut
+			$thisfile_ape['items'][$item_key] = array();
+			$thisfile_ape_items_current = &$thisfile_ape['items'][$item_key];
+
+			$offset += ($ItemKeyLength + 1); // skip 0x00 terminator
+			$thisfile_ape_items_current['data'] = substr($APEtagData, $offset, $value_size);
+			$offset += $value_size;
+
+			$thisfile_ape_items_current['flags'] = $this->parseAPEtagFlags($item_flags);
+			switch ($thisfile_ape_items_current['flags']['item_contents_raw']) {
+				case 0: // UTF-8
+				case 3: // Locator (URL, filename, etc), UTF-8 encoded
+					$thisfile_ape_items_current['data'] = explode("\x00", trim($thisfile_ape_items_current['data']));
+					break;
+
+				default: // binary data
+					break;
+			}
+
+			switch (strtolower($item_key)) {
+				case 'replaygain_track_gain':
+					$thisfile_replaygain['track']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					$thisfile_replaygain['track']['originator'] = 'unspecified';
+					break;
+
+				case 'replaygain_track_peak':
+					$thisfile_replaygain['track']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					$thisfile_replaygain['track']['originator'] = 'unspecified';
+					if ($thisfile_replaygain['track']['peak'] <= 0) {
+						$ThisFileInfo['warning'][] = 'ReplayGain Track peak from APEtag appears invalid: '.$thisfile_replaygain['track']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
+					}
+					break;
+
+				case 'replaygain_album_gain':
+					$thisfile_replaygain['album']['adjustment'] = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					$thisfile_replaygain['album']['originator'] = 'unspecified';
+					break;
+
+				case 'replaygain_album_peak':
+					$thisfile_replaygain['album']['peak']       = (float) str_replace(',', '.', $thisfile_ape_items_current['data'][0]); // float casting will see "0,95" as zero!
+					$thisfile_replaygain['album']['originator'] = 'unspecified';
+					if ($thisfile_replaygain['album']['peak'] <= 0) {
+						$ThisFileInfo['warning'][] = 'ReplayGain Album peak from APEtag appears invalid: '.$thisfile_replaygain['album']['peak'].' (original value = "'.$thisfile_ape_items_current['data'][0].'")';
+					}
+					break;
+
+				case 'mp3gain_undo':
+					list($mp3gain_undo_left, $mp3gain_undo_right, $mp3gain_undo_wrap) = explode(',', $thisfile_ape_items_current['data'][0]);
+					$thisfile_replaygain['mp3gain']['undo_left']  = intval($mp3gain_undo_left);
+					$thisfile_replaygain['mp3gain']['undo_right'] = intval($mp3gain_undo_right);
+					$thisfile_replaygain['mp3gain']['undo_wrap']  = (($mp3gain_undo_wrap == 'Y') ? true : false);
+					break;
+
+				case 'mp3gain_minmax':
+					list($mp3gain_globalgain_min, $mp3gain_globalgain_max) = explode(',', $thisfile_ape_items_current['data'][0]);
+					$thisfile_replaygain['mp3gain']['globalgain_track_min'] = intval($mp3gain_globalgain_min);
+					$thisfile_replaygain['mp3gain']['globalgain_track_max'] = intval($mp3gain_globalgain_max);
+					break;
+
+				case 'mp3gain_album_minmax':
+					list($mp3gain_globalgain_album_min, $mp3gain_globalgain_album_max) = explode(',', $thisfile_ape_items_current['data'][0]);
+					$thisfile_replaygain['mp3gain']['globalgain_album_min'] = intval($mp3gain_globalgain_album_min);
+					$thisfile_replaygain['mp3gain']['globalgain_album_max'] = intval($mp3gain_globalgain_album_max);
+					break;
+
+				case 'tracknumber':
+					foreach ($thisfile_ape_items_current['data'] as $comment) {
+						$thisfile_ape['comments']['track'][] = $comment;
+					}
+					break;
+
+				default:
+					foreach ($thisfile_ape_items_current['data'] as $comment) {
+						$thisfile_ape['comments'][strtolower($item_key)][] = $comment;
+					}
+					break;
+			}
+
+		}
+		if (empty($thisfile_replaygain)) {
+			unset($ThisFileInfo['replay_gain']);
+		}
+
+		return true;
+	}
+
+	function parseAPEheaderFooter($APEheaderFooterData) {
+		// http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
+
+		// shortcut
+		$headerfooterinfo['raw'] = array();
+		$headerfooterinfo_raw = &$headerfooterinfo['raw'];
+
+		$headerfooterinfo_raw['footer_tag']   =                  substr($APEheaderFooterData,  0, 8);
+		if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
+			return false;
+		}
+		$headerfooterinfo_raw['version']      = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData,  8, 4));
+		$headerfooterinfo_raw['tagsize']      = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
+		$headerfooterinfo_raw['tag_items']    = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
+		$headerfooterinfo_raw['global_flags'] = getid3_lib::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
+		$headerfooterinfo_raw['reserved']     =                              substr($APEheaderFooterData, 24, 8);
+
+		$headerfooterinfo['tag_version']         = $headerfooterinfo_raw['version'] / 1000;
+		if ($headerfooterinfo['tag_version'] >= 2) {
+			$headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
+		}
+		return $headerfooterinfo;
+	}
+
+	function parseAPEtagFlags($rawflagint) {
+		// "Note: APE Tags 1.0 do not use any of the APE Tag flags.
+		// All are set to zero on creation and ignored on reading."
+		// http://www.uni-jena.de/~pfk/mpp/sv8/apetagflags.html
+		$flags['header']            = (bool) ($rawflagint & 0x80000000);
+		$flags['footer']            = (bool) ($rawflagint & 0x40000000);
+		$flags['this_is_header']    = (bool) ($rawflagint & 0x20000000);
+		$flags['item_contents_raw'] =        ($rawflagint & 0x00000006) >> 1;
+		$flags['read_only']         = (bool) ($rawflagint & 0x00000001);
+
+		$flags['item_contents']     = $this->APEcontentTypeFlagLookup($flags['item_contents_raw']);
+
+		return $flags;
+	}
+
+	function APEcontentTypeFlagLookup($contenttypeid) {
+		static $APEcontentTypeFlagLookup = array(
+			0 => 'utf-8',
+			1 => 'binary',
+			2 => 'external',
+			3 => 'reserved'
+		);
+		return (isset($APEcontentTypeFlagLookup[$contenttypeid]) ? $APEcontentTypeFlagLookup[$contenttypeid] : 'invalid');
+	}
+
+	function APEtagItemIsUTF8Lookup($itemkey) {
+		static $APEtagItemIsUTF8Lookup = array(
+			'title',
+			'subtitle',
+			'artist',
+			'album',
+			'debut album',
+			'publisher',
+			'conductor',
+			'track',
+			'composer',
+			'comment',
+			'copyright',
+			'publicationright',
+			'file',
+			'year',
+			'record date',
+			'record location',
+			'genre',
+			'media',
+			'related',
+			'isrc',
+			'abstract',
+			'language',
+			'bibliography'
+		);
+		return in_array(strtolower($itemkey), $APEtagItemIsUTF8Lookup);
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.tag.id3v1.php b/apps/media/getID3/getid3/module.tag.id3v1.php
new file mode 100644
index 0000000000000000000000000000000000000000..6fd2dd84a7b9bba6d35bb6ed4f5b58c9ded67530
--- /dev/null
+++ b/apps/media/getID3/getid3/module.tag.id3v1.php
@@ -0,0 +1,361 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// module.tag.id3v1.php                                        //
+// module for analyzing ID3v1 tags                             //
+// dependencies: NONE                                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_id3v1
+{
+
+	function getid3_id3v1(&$fd, &$ThisFileInfo) {
+
+		if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
+			$ThisFileInfo['warning'][] = 'Unable to check for ID3v1 because file is larger than 2GB';
+			return false;
+		}
+
+		fseek($fd, -256, SEEK_END);
+		$preid3v1 = fread($fd, 128);
+		$id3v1tag = fread($fd, 128);
+
+		if (substr($id3v1tag, 0, 3) == 'TAG') {
+
+			$ThisFileInfo['avdataend'] = $ThisFileInfo['filesize'] - 128;
+
+			$ParsedID3v1['title']   = $this->cutfield(substr($id3v1tag,   3, 30));
+			$ParsedID3v1['artist']  = $this->cutfield(substr($id3v1tag,  33, 30));
+			$ParsedID3v1['album']   = $this->cutfield(substr($id3v1tag,  63, 30));
+			$ParsedID3v1['year']    = $this->cutfield(substr($id3v1tag,  93,  4));
+			$ParsedID3v1['comment'] =                 substr($id3v1tag,  97, 30);  // can't remove nulls yet, track detection depends on them
+			$ParsedID3v1['genreid'] =             ord(substr($id3v1tag, 127,  1));
+
+			// If second-last byte of comment field is null and last byte of comment field is non-null
+			// then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
+			if (($id3v1tag{125} === "\x00") && ($id3v1tag{126} !== "\x00")) {
+				$ParsedID3v1['track']   = ord(substr($ParsedID3v1['comment'], 29,  1));
+				$ParsedID3v1['comment'] =     substr($ParsedID3v1['comment'],  0, 28);
+			}
+			$ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
+
+			$ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
+			if (!empty($ParsedID3v1['genre'])) {
+				unset($ParsedID3v1['genreid']);
+			}
+			if (empty($ParsedID3v1['genre']) || (@$ParsedID3v1['genre'] == 'Unknown')) {
+				unset($ParsedID3v1['genre']);
+			}
+
+			foreach ($ParsedID3v1 as $key => $value) {
+				$ParsedID3v1['comments'][$key][0] = $value;
+			}
+
+			// ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
+			$GoodFormatID3v1tag = $this->GenerateID3v1Tag(
+											$ParsedID3v1['title'],
+											$ParsedID3v1['artist'],
+											$ParsedID3v1['album'],
+											$ParsedID3v1['year'],
+											(isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false),
+											$ParsedID3v1['comment'],
+											@$ParsedID3v1['track']);
+			$ParsedID3v1['padding_valid'] = true;
+			if ($id3v1tag !== $GoodFormatID3v1tag) {
+				$ParsedID3v1['padding_valid'] = false;
+				$ThisFileInfo['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
+			}
+
+			$ParsedID3v1['tag_offset_end']   = $ThisFileInfo['filesize'];
+			$ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
+
+			$ThisFileInfo['id3v1'] = $ParsedID3v1;
+		}
+
+		if (substr($preid3v1, 0, 3) == 'TAG') {
+			// The way iTunes handles tags is, well, brain-damaged.
+			// It completely ignores v1 if ID3v2 is present.
+			// This goes as far as adding a new v1 tag *even if there already is one*
+
+			// A suspected double-ID3v1 tag has been detected, but it could be that
+			// the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
+			if (substr($preid3v1, 96, 8) == 'APETAGEX') {
+				// an APE tag footer was found before the last ID3v1, assume false "TAG" synch
+			} elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
+				// a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
+			} else {
+				// APE and Lyrics3 footers not found - assume double ID3v1
+				$ThisFileInfo['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
+				$ThisFileInfo['avdataend'] -= 128;
+			}
+		}
+
+		return true;
+	}
+
+	function cutfield($str) {
+		return trim(substr($str, 0, strcspn($str, "\x00")));
+	}
+
+	function ArrayOfGenres($allowSCMPXextended=false) {
+		static $GenreLookup = array(
+			0    => 'Blues',
+			1    => 'Classic Rock',
+			2    => 'Country',
+			3    => 'Dance',
+			4    => 'Disco',
+			5    => 'Funk',
+			6    => 'Grunge',
+			7    => 'Hip-Hop',
+			8    => 'Jazz',
+			9    => 'Metal',
+			10   => 'New Age',
+			11   => 'Oldies',
+			12   => 'Other',
+			13   => 'Pop',
+			14   => 'R&B',
+			15   => 'Rap',
+			16   => 'Reggae',
+			17   => 'Rock',
+			18   => 'Techno',
+			19   => 'Industrial',
+			20   => 'Alternative',
+			21   => 'Ska',
+			22   => 'Death Metal',
+			23   => 'Pranks',
+			24   => 'Soundtrack',
+			25   => 'Euro-Techno',
+			26   => 'Ambient',
+			27   => 'Trip-Hop',
+			28   => 'Vocal',
+			29   => 'Jazz+Funk',
+			30   => 'Fusion',
+			31   => 'Trance',
+			32   => 'Classical',
+			33   => 'Instrumental',
+			34   => 'Acid',
+			35   => 'House',
+			36   => 'Game',
+			37   => 'Sound Clip',
+			38   => 'Gospel',
+			39   => 'Noise',
+			40   => 'Alt. Rock',
+			41   => 'Bass',
+			42   => 'Soul',
+			43   => 'Punk',
+			44   => 'Space',
+			45   => 'Meditative',
+			46   => 'Instrumental Pop',
+			47   => 'Instrumental Rock',
+			48   => 'Ethnic',
+			49   => 'Gothic',
+			50   => 'Darkwave',
+			51   => 'Techno-Industrial',
+			52   => 'Electronic',
+			53   => 'Pop-Folk',
+			54   => 'Eurodance',
+			55   => 'Dream',
+			56   => 'Southern Rock',
+			57   => 'Comedy',
+			58   => 'Cult',
+			59   => 'Gangsta Rap',
+			60   => 'Top 40',
+			61   => 'Christian Rap',
+			62   => 'Pop/Funk',
+			63   => 'Jungle',
+			64   => 'Native American',
+			65   => 'Cabaret',
+			66   => 'New Wave',
+			67   => 'Psychedelic',
+			68   => 'Rave',
+			69   => 'Showtunes',
+			70   => 'Trailer',
+			71   => 'Lo-Fi',
+			72   => 'Tribal',
+			73   => 'Acid Punk',
+			74   => 'Acid Jazz',
+			75   => 'Polka',
+			76   => 'Retro',
+			77   => 'Musical',
+			78   => 'Rock & Roll',
+			79   => 'Hard Rock',
+			80   => 'Folk',
+			81   => 'Folk/Rock',
+			82   => 'National Folk',
+			83   => 'Swing',
+			84   => 'Fast-Fusion',
+			85   => 'Bebob',
+			86   => 'Latin',
+			87   => 'Revival',
+			88   => 'Celtic',
+			89   => 'Bluegrass',
+			90   => 'Avantgarde',
+			91   => 'Gothic Rock',
+			92   => 'Progressive Rock',
+			93   => 'Psychedelic Rock',
+			94   => 'Symphonic Rock',
+			95   => 'Slow Rock',
+			96   => 'Big Band',
+			97   => 'Chorus',
+			98   => 'Easy Listening',
+			99   => 'Acoustic',
+			100  => 'Humour',
+			101  => 'Speech',
+			102  => 'Chanson',
+			103  => 'Opera',
+			104  => 'Chamber Music',
+			105  => 'Sonata',
+			106  => 'Symphony',
+			107  => 'Booty Bass',
+			108  => 'Primus',
+			109  => 'Porn Groove',
+			110  => 'Satire',
+			111  => 'Slow Jam',
+			112  => 'Club',
+			113  => 'Tango',
+			114  => 'Samba',
+			115  => 'Folklore',
+			116  => 'Ballad',
+			117  => 'Power Ballad',
+			118  => 'Rhythmic Soul',
+			119  => 'Freestyle',
+			120  => 'Duet',
+			121  => 'Punk Rock',
+			122  => 'Drum Solo',
+			123  => 'A Cappella',
+			124  => 'Euro-House',
+			125  => 'Dance Hall',
+			126  => 'Goa',
+			127  => 'Drum & Bass',
+			128  => 'Club-House',
+			129  => 'Hardcore',
+			130  => 'Terror',
+			131  => 'Indie',
+			132  => 'BritPop',
+			133  => 'Negerpunk',
+			134  => 'Polsk Punk',
+			135  => 'Beat',
+			136  => 'Christian Gangsta Rap',
+			137  => 'Heavy Metal',
+			138  => 'Black Metal',
+			139  => 'Crossover',
+			140  => 'Contemporary Christian',
+			141  => 'Christian Rock',
+			142  => 'Merengue',
+			143  => 'Salsa',
+			144  => 'Trash Metal',
+			145  => 'Anime',
+			146  => 'JPop',
+			147  => 'Synthpop',
+
+			255  => 'Unknown',
+
+			'CR' => 'Cover',
+			'RX' => 'Remix'
+		);
+
+		static $GenreLookupSCMPX = array();
+		if ($allowSCMPXextended && empty($GenreLookupSCMPX)) {
+			$GenreLookupSCMPX = $GenreLookup;
+			// http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
+			// Extended ID3v1 genres invented by SCMPX
+			// Note that 255 "Japanese Anime" conflicts with standard "Unknown"
+			$GenreLookupSCMPX[240] = 'Sacred';
+			$GenreLookupSCMPX[241] = 'Northern Europe';
+			$GenreLookupSCMPX[242] = 'Irish & Scottish';
+			$GenreLookupSCMPX[243] = 'Scotland';
+			$GenreLookupSCMPX[244] = 'Ethnic Europe';
+			$GenreLookupSCMPX[245] = 'Enka';
+			$GenreLookupSCMPX[246] = 'Children\'s Song';
+			$GenreLookupSCMPX[247] = 'Japanese Sky';
+			$GenreLookupSCMPX[248] = 'Japanese Heavy Rock';
+			$GenreLookupSCMPX[249] = 'Japanese Doom Rock';
+			$GenreLookupSCMPX[250] = 'Japanese J-POP';
+			$GenreLookupSCMPX[251] = 'Japanese Seiyu';
+			$GenreLookupSCMPX[252] = 'Japanese Ambient Techno';
+			$GenreLookupSCMPX[253] = 'Japanese Moemoe';
+			$GenreLookupSCMPX[254] = 'Japanese Tokusatsu';
+			//$GenreLookupSCMPX[255] = 'Japanese Anime';
+		}
+
+		return ($allowSCMPXextended ? $GenreLookupSCMPX : $GenreLookup);
+	}
+
+	function LookupGenreName($genreid, $allowSCMPXextended=true) {
+		switch ($genreid) {
+			case 'RX':
+			case 'CR':
+				break;
+			default:
+				$genreid = intval($genreid); // to handle 3 or '3' or '03'
+				break;
+		}
+		$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
+		return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
+	}
+
+	function LookupGenreID($genre, $allowSCMPXextended=false) {
+		$GenreLookup = getid3_id3v1::ArrayOfGenres($allowSCMPXextended);
+		$LowerCaseNoSpaceSearchTerm = strtolower(str_replace(' ', '', $genre));
+		foreach ($GenreLookup as $key => $value) {
+			foreach ($GenreLookup as $key => $value) {
+				if (strtolower(str_replace(' ', '', $value)) == $LowerCaseNoSpaceSearchTerm) {
+					return $key;
+				}
+			}
+			return false;
+		}
+		return (isset($GenreLookup[$genreid]) ? $GenreLookup[$genreid] : false);
+	}
+
+	function StandardiseID3v1GenreName($OriginalGenre) {
+		if (($GenreID = getid3_id3v1::LookupGenreID($OriginalGenre)) !== false) {
+			return getid3_id3v1::LookupGenreName($GenreID);
+		}
+		return $OriginalGenre;
+	}
+
+	function GenerateID3v1Tag($title, $artist, $album, $year, $genreid, $comment, $track='') {
+		$ID3v1Tag  = 'TAG';
+		$ID3v1Tag .= str_pad(trim(substr($title,  0, 30)), 30, "\x00", STR_PAD_RIGHT);
+		$ID3v1Tag .= str_pad(trim(substr($artist, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
+		$ID3v1Tag .= str_pad(trim(substr($album,  0, 30)), 30, "\x00", STR_PAD_RIGHT);
+		$ID3v1Tag .= str_pad(trim(substr($year,   0,  4)),  4, "\x00", STR_PAD_LEFT);
+		if (!empty($track) && ($track > 0) && ($track <= 255)) {
+			$ID3v1Tag .= str_pad(trim(substr($comment, 0, 28)), 28, "\x00", STR_PAD_RIGHT);
+			$ID3v1Tag .= "\x00";
+			if (gettype($track) == 'string') {
+				$track = (int) $track;
+			}
+			$ID3v1Tag .= chr($track);
+		} else {
+			$ID3v1Tag .= str_pad(trim(substr($comment, 0, 30)), 30, "\x00", STR_PAD_RIGHT);
+		}
+		if (($genreid < 0) || ($genreid > 147)) {
+			$genreid = 255; // 'unknown' genre
+		}
+		switch (gettype($genreid)) {
+			case 'string':
+			case 'integer':
+				$ID3v1Tag .= chr(intval($genreid));
+				break;
+			default:
+				$ID3v1Tag .= chr(255); // 'unknown' genre
+				break;
+		}
+
+		return $ID3v1Tag;
+	}
+
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/module.tag.id3v2.php b/apps/media/getID3/getid3/module.tag.id3v2.php
new file mode 100644
index 0000000000000000000000000000000000000000..701d72800c74a04dcd3f510e54645f2b3bfb48fe
--- /dev/null
+++ b/apps/media/getID3/getid3/module.tag.id3v2.php
@@ -0,0 +1,3200 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+///                                                            //
+// module.tag.id3v2.php                                        //
+// module for analyzing ID3v2 tags                             //
+// dependencies: module.tag.id3v1.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
+
+class getid3_id3v2
+{
+
+	function getid3_id3v2(&$fd, &$ThisFileInfo, $StartingOffset=0) {
+		//    Overall tag structure:
+		//        +-----------------------------+
+		//        |      Header (10 bytes)      |
+		//        +-----------------------------+
+		//        |       Extended Header       |
+		//        | (variable length, OPTIONAL) |
+		//        +-----------------------------+
+		//        |   Frames (variable length)  |
+		//        +-----------------------------+
+		//        |           Padding           |
+		//        | (variable length, OPTIONAL) |
+		//        +-----------------------------+
+		//        | Footer (10 bytes, OPTIONAL) |
+		//        +-----------------------------+
+
+		//    Header
+		//        ID3v2/file identifier      "ID3"
+		//        ID3v2 version              $04 00
+		//        ID3v2 flags                (%ab000000 in v2.2, %abc00000 in v2.3, %abcd0000 in v2.4.x)
+		//        ID3v2 size             4 * %0xxxxxxx
+
+
+		// shortcuts
+		$ThisFileInfo['id3v2']['header'] = true;
+		$thisfile_id3v2                  = &$ThisFileInfo['id3v2'];
+		$thisfile_id3v2['flags']         =  array();
+		$thisfile_id3v2_flags            = &$thisfile_id3v2['flags'];
+
+
+		fseek($fd, $StartingOffset, SEEK_SET);
+		$header = fread($fd, 10);
+		if (substr($header, 0, 3) == 'ID3'  &&  strlen($header) == 10) {
+
+			$thisfile_id3v2['majorversion'] = ord($header{3});
+			$thisfile_id3v2['minorversion'] = ord($header{4});
+
+			// shortcut
+			$id3v2_majorversion = &$thisfile_id3v2['majorversion'];
+
+		} else {
+
+			unset($ThisFileInfo['id3v2']);
+			return false;
+
+		}
+
+		if ($id3v2_majorversion > 4) { // this script probably won't correctly parse ID3v2.5.x and above (if it ever exists)
+
+			$ThisFileInfo['error'][] = 'this script only parses up to ID3v2.4.x - this tag is ID3v2.'.$id3v2_majorversion.'.'.$thisfile_id3v2['minorversion'];
+			return false;
+
+		}
+
+		$id3_flags = ord($header{5});
+		switch ($id3v2_majorversion) {
+			case 2:
+				// %ab000000 in v2.2
+				$thisfile_id3v2_flags['unsynch']     = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
+				$thisfile_id3v2_flags['compression'] = (bool) ($id3_flags & 0x40); // b - Compression
+				break;
+
+			case 3:
+				// %abc00000 in v2.3
+				$thisfile_id3v2_flags['unsynch']     = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
+				$thisfile_id3v2_flags['exthead']     = (bool) ($id3_flags & 0x40); // b - Extended header
+				$thisfile_id3v2_flags['experim']     = (bool) ($id3_flags & 0x20); // c - Experimental indicator
+				break;
+
+			case 4:
+				// %abcd0000 in v2.4
+				$thisfile_id3v2_flags['unsynch']     = (bool) ($id3_flags & 0x80); // a - Unsynchronisation
+				$thisfile_id3v2_flags['exthead']     = (bool) ($id3_flags & 0x40); // b - Extended header
+				$thisfile_id3v2_flags['experim']     = (bool) ($id3_flags & 0x20); // c - Experimental indicator
+				$thisfile_id3v2_flags['isfooter']    = (bool) ($id3_flags & 0x10); // d - Footer present
+				break;
+		}
+
+		$thisfile_id3v2['headerlength'] = getid3_lib::BigEndian2Int(substr($header, 6, 4), 1) + 10; // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
+
+		$thisfile_id3v2['tag_offset_start'] = $StartingOffset;
+		$thisfile_id3v2['tag_offset_end']   = $thisfile_id3v2['tag_offset_start'] + $thisfile_id3v2['headerlength'];
+
+
+
+		// create 'encoding' key - used by getid3::HandleAllTags()
+		// in ID3v2 every field can have it's own encoding type
+		// so force everything to UTF-8 so it can be handled consistantly
+		$thisfile_id3v2['encoding'] = 'UTF-8';
+
+
+	//    Frames
+
+	//        All ID3v2 frames consists of one frame header followed by one or more
+	//        fields containing the actual information. The header is always 10
+	//        bytes and laid out as follows:
+	//
+	//        Frame ID      $xx xx xx xx  (four characters)
+	//        Size      4 * %0xxxxxxx
+	//        Flags         $xx xx
+
+		$sizeofframes = $thisfile_id3v2['headerlength'] - 10; // not including 10-byte initial header
+		if (@$thisfile_id3v2['exthead']['length']) {
+			$sizeofframes -= ($thisfile_id3v2['exthead']['length'] + 4);
+		}
+		if (@$thisfile_id3v2_flags['isfooter']) {
+			$sizeofframes -= 10; // footer takes last 10 bytes of ID3v2 header, after frame data, before audio
+		}
+		if ($sizeofframes > 0) {
+
+			$framedata = fread($fd, $sizeofframes); // read all frames from file into $framedata variable
+
+			//    if entire frame data is unsynched, de-unsynch it now (ID3v2.3.x)
+			if (@$thisfile_id3v2_flags['unsynch'] && ($id3v2_majorversion <= 3)) {
+				$framedata = $this->DeUnsynchronise($framedata);
+			}
+			//        [in ID3v2.4.0] Unsynchronisation [S:6.1] is done on frame level, instead
+			//        of on tag level, making it easier to skip frames, increasing the streamability
+			//        of the tag. The unsynchronisation flag in the header [S:3.1] indicates that
+			//        there exists an unsynchronised frame, while the new unsynchronisation flag in
+			//        the frame header [S:4.1.2] indicates unsynchronisation.
+
+
+			//$framedataoffset = 10 + (@$thisfile_id3v2['exthead']['length'] ? $thisfile_id3v2['exthead']['length'] + 4 : 0); // how many bytes into the stream - start from after the 10-byte header (and extended header length+4, if present)
+			$framedataoffset = 10; // how many bytes into the stream - start from after the 10-byte header
+
+
+			//    Extended Header
+			if (@$thisfile_id3v2_flags['exthead']) {
+				$extended_header_offset = 0;
+
+				if ($id3v2_majorversion == 3) {
+
+					// v2.3 definition:
+					//Extended header size  $xx xx xx xx   // 32-bit integer
+					//Extended Flags        $xx xx
+					//     %x0000000 %00000000 // v2.3
+					//     x - CRC data present
+					//Size of padding       $xx xx xx xx
+
+					$thisfile_id3v2['exthead']['length'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 0);
+					$extended_header_offset += 4;
+
+					$thisfile_id3v2['exthead']['flag_bytes'] = 2;
+					$thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
+					$extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
+
+					$thisfile_id3v2['exthead']['flags']['crc'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x8000);
+
+					$thisfile_id3v2['exthead']['padding_size'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
+					$extended_header_offset += 4;
+
+					if ($thisfile_id3v2['exthead']['flags']['crc']) {
+						$thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4));
+						$extended_header_offset += 4;
+					}
+					$extended_header_offset += $thisfile_id3v2['exthead']['padding_size'];
+
+				} elseif ($id3v2_majorversion == 4) {
+
+					// v2.4 definition:
+					//Extended header size   4 * %0xxxxxxx // 28-bit synchsafe integer
+					//Number of flag bytes       $01
+					//Extended Flags             $xx
+					//     %0bcd0000 // v2.4
+					//     b - Tag is an update
+					//         Flag data length       $00
+					//     c - CRC data present
+					//         Flag data length       $05
+					//         Total frame CRC    5 * %0xxxxxxx
+					//     d - Tag restrictions
+					//         Flag data length       $01
+
+					$thisfile_id3v2['exthead']['length']     = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 4), 1);
+					$extended_header_offset += 4;
+
+					$thisfile_id3v2['exthead']['flag_bytes'] = 1;
+					$thisfile_id3v2['exthead']['flag_raw'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, $thisfile_id3v2['exthead']['flag_bytes']));
+					$extended_header_offset += $thisfile_id3v2['exthead']['flag_bytes'];
+
+					$thisfile_id3v2['exthead']['flags']['update']       = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x4000);
+					$thisfile_id3v2['exthead']['flags']['crc']          = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x2000);
+					$thisfile_id3v2['exthead']['flags']['restrictions'] = (bool) ($thisfile_id3v2['exthead']['flag_raw'] & 0x1000);
+
+					if ($thisfile_id3v2['exthead']['flags']['crc']) {
+						$thisfile_id3v2['exthead']['flag_data']['crc'] = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 5), 1);
+						$extended_header_offset += 5;
+					}
+					if ($thisfile_id3v2['exthead']['flags']['restrictions']) {
+						// %ppqrrstt
+						$restrictions_raw = getid3_lib::BigEndian2Int(substr($framedata, $extended_header_offset, 1));
+						$extended_header_offset += 1;
+						$thisfile_id3v2['exthead']['flags']['restrictions']['tagsize']  = ($restrictions_raw && 0xC0) >> 6; // p - Tag size restrictions
+						$thisfile_id3v2['exthead']['flags']['restrictions']['textenc']  = ($restrictions_raw && 0x20) >> 5; // q - Text encoding restrictions
+						$thisfile_id3v2['exthead']['flags']['restrictions']['textsize'] = ($restrictions_raw && 0x18) >> 3; // r - Text fields size restrictions
+						$thisfile_id3v2['exthead']['flags']['restrictions']['imgenc']   = ($restrictions_raw && 0x04) >> 2; // s - Image encoding restrictions
+						$thisfile_id3v2['exthead']['flags']['restrictions']['imgsize']  = ($restrictions_raw && 0x03) >> 0; // t - Image size restrictions
+					}
+
+				}
+				$framedataoffset += $extended_header_offset;
+				$framedata = substr($framedata, $extended_header_offset);
+			} // end extended header
+
+
+			while (isset($framedata) && (strlen($framedata) > 0)) { // cycle through until no more frame data is left to parse
+				if (strlen($framedata) <= $this->ID3v2HeaderLength($id3v2_majorversion)) {
+					// insufficient room left in ID3v2 header for actual data - must be padding
+					$thisfile_id3v2['padding']['start']  = $framedataoffset;
+					$thisfile_id3v2['padding']['length'] = strlen($framedata);
+					$thisfile_id3v2['padding']['valid']  = true;
+					for ($i = 0; $i < $thisfile_id3v2['padding']['length']; $i++) {
+						if ($framedata{$i} != "\x00") {
+							$thisfile_id3v2['padding']['valid'] = false;
+							$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
+							$ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
+							break;
+						}
+					}
+					break; // skip rest of ID3v2 header
+				}
+				if ($id3v2_majorversion == 2) {
+					// Frame ID  $xx xx xx (three characters)
+					// Size      $xx xx xx (24-bit integer)
+					// Flags     $xx xx
+
+					$frame_header = substr($framedata, 0, 6); // take next 6 bytes for header
+					$framedata    = substr($framedata, 6);    // and leave the rest in $framedata
+					$frame_name   = substr($frame_header, 0, 3);
+					$frame_size   = getid3_lib::BigEndian2Int(substr($frame_header, 3, 3), 0);
+					$frame_flags  = 0; // not used for anything in ID3v2.2, just set to avoid E_NOTICEs
+
+				} elseif ($id3v2_majorversion > 2) {
+
+					// Frame ID  $xx xx xx xx (four characters)
+					// Size      $xx xx xx xx (32-bit integer in v2.3, 28-bit synchsafe in v2.4+)
+					// Flags     $xx xx
+
+					$frame_header = substr($framedata, 0, 10); // take next 10 bytes for header
+					$framedata    = substr($framedata, 10);    // and leave the rest in $framedata
+
+					$frame_name = substr($frame_header, 0, 4);
+					if ($id3v2_majorversion == 3) {
+						$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
+					} else { // ID3v2.4+
+						$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 1); // 32-bit synchsafe integer (28-bit value)
+					}
+
+					if ($frame_size < (strlen($framedata) + 4)) {
+						$nextFrameID = substr($framedata, $frame_size, 4);
+						if ($this->IsValidID3v2FrameName($nextFrameID, $id3v2_majorversion)) {
+							// next frame is OK
+						} elseif (($frame_name == "\x00".'MP3') || ($frame_name == "\x00\x00".'MP') || ($frame_name == ' MP3') || ($frame_name == 'MP3e')) {
+							// MP3ext known broken frames - "ok" for the purposes of this test
+						} elseif (($id3v2_majorversion == 4) && ($this->IsValidID3v2FrameName(substr($framedata, getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0), 4), 3))) {
+							$ThisFileInfo['warning'][] = 'ID3v2 tag written as ID3v2.4, but with non-synchsafe integers (ID3v2.3 style). Older versions of (Helium2; iTunes) are known culprits of this. Tag has been parsed as ID3v2.3';
+							$id3v2_majorversion = 3;
+							$frame_size = getid3_lib::BigEndian2Int(substr($frame_header, 4, 4), 0); // 32-bit integer
+						}
+					}
+
+
+					$frame_flags = getid3_lib::BigEndian2Int(substr($frame_header, 8, 2));
+				}
+
+				if ((($id3v2_majorversion == 2) && ($frame_name == "\x00\x00\x00")) || ($frame_name == "\x00\x00\x00\x00")) {
+					// padding encountered
+
+					$thisfile_id3v2['padding']['start']  = $framedataoffset;
+					$thisfile_id3v2['padding']['length'] = strlen($frame_header) + strlen($framedata);
+					$thisfile_id3v2['padding']['valid']  = true;
+
+					$len = strlen($framedata);
+					for ($i = 0; $i < $len; $i++) {
+						if ($framedata{$i} != "\x00") {
+							$thisfile_id3v2['padding']['valid'] = false;
+							$thisfile_id3v2['padding']['errorpos'] = $thisfile_id3v2['padding']['start'] + $i;
+							$ThisFileInfo['warning'][] = 'Invalid ID3v2 padding found at offset '.$thisfile_id3v2['padding']['errorpos'].' (the remaining '.($thisfile_id3v2['padding']['length'] - $i).' bytes are considered invalid)';
+							break;
+						}
+					}
+					break; // skip rest of ID3v2 header
+				}
+
+				if ($frame_name == 'COM ') {
+					$ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably others too)]';
+					$frame_name = 'COMM';
+				}
+				if (($frame_size <= strlen($framedata)) && ($this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion))) {
+
+					unset($parsedFrame);
+					$parsedFrame['frame_name']      = $frame_name;
+					$parsedFrame['frame_flags_raw'] = $frame_flags;
+					$parsedFrame['data']            = substr($framedata, 0, $frame_size);
+					$parsedFrame['datalength']      = getid3_lib::CastAsInt($frame_size);
+					$parsedFrame['dataoffset']      = $framedataoffset;
+
+					$this->ParseID3v2Frame($parsedFrame, $ThisFileInfo);
+					$thisfile_id3v2[$frame_name][] = $parsedFrame;
+
+					$framedata = substr($framedata, $frame_size);
+
+				} else { // invalid frame length or FrameID
+
+					if ($frame_size <= strlen($framedata)) {
+
+						if ($this->IsValidID3v2FrameName(substr($framedata, $frame_size, 4), $id3v2_majorversion)) {
+
+							// next frame is valid, just skip the current frame
+							$framedata = substr($framedata, $frame_size);
+							$ThisFileInfo['warning'][] = 'Next ID3v2 frame is valid, skipping current frame.';
+
+						} else {
+
+							// next frame is invalid too, abort processing
+							//unset($framedata);
+							$framedata = null;
+							$ThisFileInfo['error'][] = 'Next ID3v2 frame is also invalid, aborting processing.';
+
+						}
+
+					} elseif ($frame_size == strlen($framedata)) {
+
+						// this is the last frame, just skip
+						$ThisFileInfo['warning'][] = 'This was the last ID3v2 frame.';
+
+					} else {
+
+						// next frame is invalid too, abort processing
+						//unset($framedata);
+						$framedata = null;
+						$ThisFileInfo['warning'][] = 'Invalid ID3v2 frame size, aborting.';
+
+					}
+					if (!$this->IsValidID3v2FrameName($frame_name, $id3v2_majorversion)) {
+
+						switch ($frame_name) {
+							case "\x00\x00".'MP':
+							case "\x00".'MP3':
+							case ' MP3':
+							case 'MP3e':
+							case "\x00".'MP':
+							case ' MP':
+							case 'MP3':
+								$ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))). [Note: this particular error has been known to happen with tags edited by "MP3ext (www.mutschler.de/mp3ext/)"]';
+								break;
+
+							default:
+								$ThisFileInfo['warning'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: !IsValidID3v2FrameName("'.str_replace("\x00", ' ', $frame_name).'", '.$id3v2_majorversion.'))).';
+								break;
+						}
+
+					} elseif ($frame_size > strlen(@$framedata)){
+
+						$ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag). (ERROR: $frame_size ('.$frame_size.') > strlen($framedata) ('.strlen($framedata).')).';
+
+					} else {
+
+						$ThisFileInfo['error'][] = 'error parsing "'.$frame_name.'" ('.$framedataoffset.' bytes into the ID3v2.'.$id3v2_majorversion.' tag).';
+
+					}
+
+				}
+				$framedataoffset += ($frame_size + $this->ID3v2HeaderLength($id3v2_majorversion));
+
+			}
+
+		}
+
+
+	//    Footer
+
+	//    The footer is a copy of the header, but with a different identifier.
+	//        ID3v2 identifier           "3DI"
+	//        ID3v2 version              $04 00
+	//        ID3v2 flags                %abcd0000
+	//        ID3v2 size             4 * %0xxxxxxx
+
+		if (isset($thisfile_id3v2_flags['isfooter']) && $thisfile_id3v2_flags['isfooter']) {
+			$footer = fread($fd, 10);
+			if (substr($footer, 0, 3) == '3DI') {
+				$thisfile_id3v2['footer'] = true;
+				$thisfile_id3v2['majorversion_footer'] = ord($footer{3});
+				$thisfile_id3v2['minorversion_footer'] = ord($footer{4});
+			}
+			if ($thisfile_id3v2['majorversion_footer'] <= 4) {
+				$id3_flags = ord(substr($footer{5}));
+				$thisfile_id3v2_flags['unsynch_footer']  = (bool) ($id3_flags & 0x80);
+				$thisfile_id3v2_flags['extfoot_footer']  = (bool) ($id3_flags & 0x40);
+				$thisfile_id3v2_flags['experim_footer']  = (bool) ($id3_flags & 0x20);
+				$thisfile_id3v2_flags['isfooter_footer'] = (bool) ($id3_flags & 0x10);
+
+				$thisfile_id3v2['footerlength'] = getid3_lib::BigEndian2Int(substr($footer, 6, 4), 1);
+			}
+		} // end footer
+
+		if (isset($thisfile_id3v2['comments']['genre'])) {
+			foreach ($thisfile_id3v2['comments']['genre'] as $key => $value) {
+				unset($thisfile_id3v2['comments']['genre'][$key]);
+				$thisfile_id3v2['comments'] = getid3_lib::array_merge_noclobber($thisfile_id3v2['comments'], $this->ParseID3v2GenreString($value));
+			}
+		}
+
+		if (isset($thisfile_id3v2['comments']['track'])) {
+			foreach ($thisfile_id3v2['comments']['track'] as $key => $value) {
+				if (strstr($value, '/')) {
+					list($thisfile_id3v2['comments']['tracknum'][$key], $thisfile_id3v2['comments']['totaltracks'][$key]) = explode('/', $thisfile_id3v2['comments']['track'][$key]);
+				}
+			}
+		}
+
+		if (!isset($thisfile_id3v2['comments']['year']) && ereg('^([0-9]{4})', trim(@$thisfile_id3v2['comments']['recording_time'][0]), $matches)) {
+			$thisfile_id3v2['comments']['year'] = array($matches[1]);
+		}
+
+
+		// Set avdataoffset
+		$ThisFileInfo['avdataoffset'] = $thisfile_id3v2['headerlength'];
+		if (isset($thisfile_id3v2['footer'])) {
+			$ThisFileInfo['avdataoffset'] += 10;
+		}
+
+		return true;
+	}
+
+
+	function ParseID3v2GenreString($genrestring) {
+		// Parse genres into arrays of genreName and genreID
+		// ID3v2.2.x, ID3v2.3.x: '(21)' or '(4)Eurodisco' or '(51)(39)' or '(55)((I think...)'
+		// ID3v2.4.x: '21' $00 'Eurodisco' $00
+
+		$genrestring = trim($genrestring);
+		$returnarray = array();
+		if (strpos($genrestring, "\x00") !== false) {
+			$unprocessed = trim($genrestring); // trailing nulls will cause an infinite loop.
+			$genrestring = '';
+			while (strpos($unprocessed, "\x00") !== false) {
+				// convert null-seperated v2.4-format into v2.3 ()-seperated format
+				$endpos = strpos($unprocessed, "\x00");
+				$genrestring .= '('.substr($unprocessed, 0, $endpos).')';
+				$unprocessed = substr($unprocessed, $endpos + 1);
+			}
+			unset($unprocessed);
+        } elseif (eregi('^([0-9]+|CR|RX)$', $genrestring)) {
+        	// some tagging program (including some that use TagLib) fail to include null byte after numeric genre
+			$genrestring = '('.$genrestring.')';
+        }
+		if (getid3_id3v1::LookupGenreID($genrestring)) {
+
+			$returnarray['genre'][] = $genrestring;
+
+		} else {
+
+			while (strpos($genrestring, '(') !== false) {
+
+				$startpos = strpos($genrestring, '(');
+				$endpos   = strpos($genrestring, ')');
+				if (substr($genrestring, $startpos + 1, 1) == '(') {
+					$genrestring = substr($genrestring, 0, $startpos).substr($genrestring, $startpos + 1);
+					$endpos--;
+				}
+				$element     = substr($genrestring, $startpos + 1, $endpos - ($startpos + 1));
+				$genrestring = substr($genrestring, 0, $startpos).substr($genrestring, $endpos + 1);
+				if (getid3_id3v1::LookupGenreName($element)) { // $element is a valid genre id/abbreviation
+
+					if (empty($returnarray['genre']) || !in_array(getid3_id3v1::LookupGenreName($element), $returnarray['genre'])) { // avoid duplicate entires
+						$returnarray['genre'][] = getid3_id3v1::LookupGenreName($element);
+					}
+
+				} else {
+
+					if (empty($returnarray['genre']) || !in_array($element, $returnarray['genre'])) { // avoid duplicate entires
+						$returnarray['genre'][] = $element;
+					}
+
+				}
+			}
+		}
+		if ($genrestring) {
+			if (empty($returnarray['genre']) || !in_array($genrestring, $returnarray['genre'])) { // avoid duplicate entires
+				$returnarray['genre'][]   = $genrestring;
+			}
+		}
+
+		return $returnarray;
+	}
+
+
+	function ParseID3v2Frame(&$parsedFrame, &$ThisFileInfo) {
+
+		// shortcuts
+		$id3v2_majorversion = $ThisFileInfo['id3v2']['majorversion'];
+
+		$parsedFrame['framenamelong']  = $this->FrameNameLongLookup($parsedFrame['frame_name']);
+		if (empty($parsedFrame['framenamelong'])) {
+			unset($parsedFrame['framenamelong']);
+		}
+		$parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
+		if (empty($parsedFrame['framenameshort'])) {
+			unset($parsedFrame['framenameshort']);
+		}
+
+		if ($id3v2_majorversion >= 3) { // frame flags are not part of the ID3v2.2 standard
+			if ($id3v2_majorversion == 3) {
+				//    Frame Header Flags
+				//    %abc00000 %ijk00000
+				$parsedFrame['flags']['TagAlterPreservation']  = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000); // a - Tag alter preservation
+				$parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // b - File alter preservation
+				$parsedFrame['flags']['ReadOnly']              = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // c - Read only
+				$parsedFrame['flags']['compression']           = (bool) ($parsedFrame['frame_flags_raw'] & 0x0080); // i - Compression
+				$parsedFrame['flags']['Encryption']            = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // j - Encryption
+				$parsedFrame['flags']['GroupingIdentity']      = (bool) ($parsedFrame['frame_flags_raw'] & 0x0020); // k - Grouping identity
+
+			} elseif ($id3v2_majorversion == 4) {
+				//    Frame Header Flags
+				//    %0abc0000 %0h00kmnp
+				$parsedFrame['flags']['TagAlterPreservation']  = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000); // a - Tag alter preservation
+				$parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000); // b - File alter preservation
+				$parsedFrame['flags']['ReadOnly']              = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000); // c - Read only
+				$parsedFrame['flags']['GroupingIdentity']      = (bool) ($parsedFrame['frame_flags_raw'] & 0x0040); // h - Grouping identity
+				$parsedFrame['flags']['compression']           = (bool) ($parsedFrame['frame_flags_raw'] & 0x0008); // k - Compression
+				$parsedFrame['flags']['Encryption']            = (bool) ($parsedFrame['frame_flags_raw'] & 0x0004); // m - Encryption
+				$parsedFrame['flags']['Unsynchronisation']     = (bool) ($parsedFrame['frame_flags_raw'] & 0x0002); // n - Unsynchronisation
+				$parsedFrame['flags']['DataLengthIndicator']   = (bool) ($parsedFrame['frame_flags_raw'] & 0x0001); // p - Data length indicator
+
+				// Frame-level de-unsynchronisation - ID3v2.4
+				if ($parsedFrame['flags']['Unsynchronisation']) {
+					$parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
+				}
+			}
+
+			//    Frame-level de-compression
+			if ($parsedFrame['flags']['compression']) {
+				$parsedFrame['decompressed_size'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
+				if (!function_exists('gzuncompress')) {
+					$ThisFileInfo['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "'.$parsedFrame['frame_name'].'"';
+				} elseif ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
+					$parsedFrame['data'] = $decompresseddata;
+				} else {
+					$ThisFileInfo['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "'.$parsedFrame['frame_name'].'"';
+				}
+			}
+		}
+
+		if (isset($parsedFrame['datalength']) && ($parsedFrame['datalength'] == 0)) {
+
+			$warning = 'Frame "'.$parsedFrame['frame_name'].'" at offset '.$parsedFrame['dataoffset'].' has no data portion';
+			switch ($parsedFrame['frame_name']) {
+				case 'WCOM':
+					$warning .= ' (this is known to happen with files tagged by RioPort)';
+					break;
+
+				default:
+					break;
+			}
+			$ThisFileInfo['warning'][] = $warning;
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'UFID')) || // 4.1   UFID Unique file identifier
+			(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'UFI'))) {  // 4.1   UFI  Unique file identifier
+			//   There may be more than one 'UFID' frame in a tag,
+			//   but only one with the same 'Owner identifier'.
+			// <Header for 'Unique file identifier', ID: 'UFID'>
+			// Owner identifier        <text string> $00
+			// Identifier              <up to 64 bytes binary data>
+
+			$frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
+			$frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
+			$parsedFrame['ownerid'] = $frame_idstring;
+			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'TXXX')) || // 4.2.2 TXXX User defined text information frame
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'TXX'))) {    // 4.2.2 TXX  User defined text information frame
+			//   There may be more than one 'TXXX' frame in each tag,
+			//   but only one with the same description.
+			// <Header for 'User defined text information frame', ID: 'TXXX'>
+			// Text encoding     $xx
+			// Description       <text string according to encoding> $00 (00)
+			// Value             <text string according to encoding>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$parsedFrame['encodingid']  = $frame_textencoding;
+			$parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['description'] = $frame_description;
+			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
+			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']));
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ($parsedFrame['frame_name']{0} == 'T') { // 4.2. T??[?] Text information frame
+			//   There may only be one text information frame of its kind in an tag.
+			// <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
+			// excluding 'TXXX' described in 4.2.6.>
+			// Text encoding                $xx
+			// Information                  <text string(s) according to encoding>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+
+			$parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
+
+			$parsedFrame['encodingid'] = $frame_textencoding;
+			$parsedFrame['encoding']   = $this->TextEncodingNameLookup($frame_textencoding);
+
+			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+
+				// remove possible terminating \x00 (put by encoding id or software bug)
+                $string = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
+                if ($string[strlen($string) - 1] == "\x00") {
+                    $string = substr($string, 0, strlen($string) - 1);
+                }
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
+				unset($string);
+			}
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'WXXX')) || // 4.3.2 WXXX User defined URL link frame
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'WXX'))) {    // 4.3.2 WXX  User defined URL link frame
+			//   There may be more than one 'WXXX' frame in each tag,
+			//   but only one with the same description
+			// <Header for 'User defined URL link frame', ID: 'WXXX'>
+			// Text encoding     $xx
+			// Description       <text string according to encoding> $00 (00)
+			// URL               <text string>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
+
+			$frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			if ($frame_terminatorpos) {
+				// there are null bytes after the data - this is not according to spec
+				// only use data up to first null byte
+				$frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
+			} else {
+				// no null bytes following data, just use all data
+				$frame_urldata = (string) $parsedFrame['data'];
+			}
+
+			$parsedFrame['encodingid']  = $frame_textencoding;
+			$parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['url']         = $frame_urldata;
+			$parsedFrame['description'] = $frame_description;
+			if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['url']);
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ($parsedFrame['frame_name']{0} == 'W') { // 4.3. W??? URL link frames
+			//   There may only be one URL link frame of its kind in a tag,
+			//   except when stated otherwise in the frame description
+			// <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
+			// described in 4.3.2.>
+			// URL              <text string>
+
+			$parsedFrame['url'] = trim($parsedFrame['data']);
+			if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'IPLS')) || // 4.4  IPLS Involved people list (ID3v2.3 only)
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'IPL'))) {     // 4.4  IPL  Involved people list (ID3v2.2 only)
+			//   There may only be one 'IPL' frame in each tag
+			// <Header for 'User defined URL link frame', ID: 'IPL'>
+			// Text encoding     $xx
+			// People list strings    <textstrings>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$parsedFrame['encodingid'] = $frame_textencoding;
+			$parsedFrame['encoding']   = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
+
+			$parsedFrame['data']       = (string) substr($parsedFrame['data'], $frame_offset);
+			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
+			}
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MCDI')) || // 4.4   MCDI Music CD identifier
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MCI'))) {     // 4.5   MCI  Music CD identifier
+			//   There may only be one 'MCDI' frame in each tag
+			// <Header for 'Music CD identifier', ID: 'MCDI'>
+			// CD TOC                <binary data>
+
+			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
+			}
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ETCO')) || // 4.5   ETCO Event timing codes
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ETC'))) {     // 4.6   ETC  Event timing codes
+			//   There may only be one 'ETCO' frame in each tag
+			// <Header for 'Event timing codes', ID: 'ETCO'>
+			// Time stamp format    $xx
+			//   Where time stamp format is:
+			// $01  (32-bit value) MPEG frames from beginning of file
+			// $02  (32-bit value) milliseconds from beginning of file
+			//   Followed by a list of key events in the following format:
+			// Type of event   $xx
+			// Time stamp      $xx (xx ...)
+			//   The 'Time stamp' is set to zero if directly at the beginning of the sound
+			//   or after the previous event. All events MUST be sorted in chronological order.
+
+			$frame_offset = 0;
+			$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+
+			while ($frame_offset < strlen($parsedFrame['data'])) {
+				$parsedFrame['typeid']    = substr($parsedFrame['data'], $frame_offset++, 1);
+				$parsedFrame['type']      = $this->ETCOEventLookup($parsedFrame['typeid']);
+				$parsedFrame['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
+				$frame_offset += 4;
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'MLLT')) || // 4.6   MLLT MPEG location lookup table
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'MLL'))) {     // 4.7   MLL MPEG location lookup table
+			//   There may only be one 'MLLT' frame in each tag
+			// <Header for 'Location lookup table', ID: 'MLLT'>
+			// MPEG frames between reference  $xx xx
+			// Bytes between reference        $xx xx xx
+			// Milliseconds between reference $xx xx xx
+			// Bits for bytes deviation       $xx
+			// Bits for milliseconds dev.     $xx
+			//   Then for every reference the following data is included;
+			// Deviation in bytes         %xxx....
+			// Deviation in milliseconds  %xxx....
+
+			$frame_offset = 0;
+			$parsedFrame['framesbetweenreferences'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
+			$parsedFrame['bytesbetweenreferences']  = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
+			$parsedFrame['msbetweenreferences']     = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
+			$parsedFrame['bitsforbytesdeviation']   = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
+			$parsedFrame['bitsformsdeviation']      = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
+			$parsedFrame['data'] = substr($parsedFrame['data'], 10);
+			while ($frame_offset < strlen($parsedFrame['data'])) {
+				$deviationbitstream .= getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
+			}
+			$reference_counter = 0;
+			while (strlen($deviationbitstream) > 0) {
+				$parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
+				$parsedFrame[$reference_counter]['msdeviation']   = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
+				$deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
+				$reference_counter++;
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYTC')) || // 4.7   SYTC Synchronised tempo codes
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'STC'))) {     // 4.8   STC  Synchronised tempo codes
+			//   There may only be one 'SYTC' frame in each tag
+			// <Header for 'Synchronised tempo codes', ID: 'SYTC'>
+			// Time stamp format   $xx
+			// Tempo data          <binary data>
+			//   Where time stamp format is:
+			// $01  (32-bit value) MPEG frames from beginning of file
+			// $02  (32-bit value) milliseconds from beginning of file
+
+			$frame_offset = 0;
+			$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$timestamp_counter = 0;
+			while ($frame_offset < strlen($parsedFrame['data'])) {
+				$parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+				if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
+					$parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
+				}
+				$parsedFrame[$timestamp_counter]['timestamp'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
+				$frame_offset += 4;
+				$timestamp_counter++;
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USLT')) || // 4.8   USLT Unsynchronised lyric/text transcription
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'ULT'))) {     // 4.9   ULT  Unsynchronised lyric/text transcription
+			//   There may be more than one 'Unsynchronised lyrics/text transcription' frame
+			//   in each tag, but only one with the same language and content descriptor.
+			// <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
+			// Text encoding        $xx
+			// Language             $xx xx xx
+			// Content descriptor   <text string according to encoding> $00 (00)
+			// Lyrics/text          <full text string according to encoding>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
+			$frame_offset += 3;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
+
+			$parsedFrame['encodingid']   = $frame_textencoding;
+			$parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['data']         = $parsedFrame['data'];
+			$parsedFrame['language']     = $frame_language;
+			$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
+			$parsedFrame['description']  = $frame_description;
+			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'SYLT')) || // 4.9   SYLT Synchronised lyric/text
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'SLT'))) {     // 4.10  SLT  Synchronised lyric/text
+			//   There may be more than one 'SYLT' frame in each tag,
+			//   but only one with the same language and content descriptor.
+			// <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
+			// Text encoding        $xx
+			// Language             $xx xx xx
+			// Time stamp format    $xx
+			//   $01  (32-bit value) MPEG frames from beginning of file
+			//   $02  (32-bit value) milliseconds from beginning of file
+			// Content type         $xx
+			// Content descriptor   <text string according to encoding> $00 (00)
+			//   Terminated text to be synced (typically a syllable)
+			//   Sync identifier (terminator to above string)   $00 (00)
+			//   Time stamp                                     $xx (xx ...)
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
+			$frame_offset += 3;
+			$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['contenttypeid']   = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['contenttype']     = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
+			$parsedFrame['encodingid']      = $frame_textencoding;
+			$parsedFrame['encoding']        = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['language']        = $frame_language;
+			$parsedFrame['languagename']    = $this->LanguageLookup($frame_language, false);
+
+			$timestampindex = 0;
+			$frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
+			while (strlen($frame_remainingdata)) {
+				$frame_offset = 0;
+				$frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
+				if ($frame_terminatorpos === false) {
+					$frame_remainingdata = '';
+				} else {
+					if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+						$frame_terminatorpos++; // strpos() fooled because 2nd byte of Unicode chars are often 0x00
+					}
+					$parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
+
+					$frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
+					if (($timestampindex == 0) && (ord($frame_remainingdata{0}) != 0)) {
+						// timestamp probably omitted for first data item
+					} else {
+						$parsedFrame['lyrics'][$timestampindex]['timestamp'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 4));
+						$frame_remainingdata = substr($frame_remainingdata, 4);
+					}
+					$timestampindex++;
+				}
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMM')) || // 4.10  COMM Comments
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'COM'))) {     // 4.11  COM  Comments
+			//   There may be more than one comment frame in each tag,
+			//   but only one with the same language and content descriptor.
+			// <Header for 'Comment', ID: 'COMM'>
+			// Text encoding          $xx
+			// Language               $xx xx xx
+			// Short content descrip. <text string according to encoding> $00 (00)
+			// The actual text        <full text string according to encoding>
+
+			if (strlen($parsedFrame['data']) < 5) {
+
+				$ThisFileInfo['warning'][] = 'Invalid data (too short) for "'.$parsedFrame['frame_name'].'" frame at offset '.$parsedFrame['dataoffset'];
+
+			} else {
+
+				$frame_offset = 0;
+				$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+				if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+					$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+				}
+				$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
+				$frame_offset += 3;
+				$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+				if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+					$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+				}
+				$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+				if (ord($frame_description) === 0) {
+					$frame_description = '';
+				}
+				$frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
+
+				$parsedFrame['encodingid']   = $frame_textencoding;
+				$parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
+
+				$parsedFrame['language']     = $frame_language;
+				$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
+				$parsedFrame['description']  = $frame_description;
+				$parsedFrame['data']         = $frame_text;
+				if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+					$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
+				}
+
+			}
+
+		} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'RVA2')) { // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
+			//   There may be more than one 'RVA2' frame in each tag,
+			//   but only one with the same identification string
+			// <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
+			// Identification          <text string> $00
+			//   The 'identification' string is used to identify the situation and/or
+			//   device where this adjustment should apply. The following is then
+			//   repeated for every channel:
+			// Type of channel         $xx
+			// Volume adjustment       $xx xx
+			// Bits representing peak  $xx
+			// Peak volume             $xx (xx ...)
+
+			$frame_terminatorpos = strpos($parsedFrame['data'], "\x00");
+			$frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
+			if (ord($frame_idstring) === 0) {
+				$frame_idstring = '';
+			}
+			$frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
+			$parsedFrame['description'] = $frame_idstring;
+			while (strlen($frame_remainingdata)) {
+				$frame_offset = 0;
+				$frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
+				$parsedFrame[$frame_channeltypeid]['channeltypeid']  = $frame_channeltypeid;
+				$parsedFrame[$frame_channeltypeid]['channeltype']    = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
+				$parsedFrame[$frame_channeltypeid]['volumeadjust']   = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true); // 16-bit signed
+				$frame_offset += 2;
+				$parsedFrame[$frame_channeltypeid]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
+				$frame_bytespeakvolume = ceil($parsedFrame[$frame_channeltypeid]['bitspeakvolume'] / 8);
+				$parsedFrame[$frame_channeltypeid]['peakvolume']     = getid3_lib::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
+				$frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'RVAD')) || // 4.12  RVAD Relative volume adjustment (ID3v2.3 only)
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'RVA'))) {     // 4.12  RVA  Relative volume adjustment (ID3v2.2 only)
+			//   There may only be one 'RVA' frame in each tag
+			// <Header for 'Relative volume adjustment', ID: 'RVA'>
+			// ID3v2.2 => Increment/decrement     %000000ba
+			// ID3v2.3 => Increment/decrement     %00fedcba
+			// Bits used for volume descr.        $xx
+			// Relative volume change, right      $xx xx (xx ...) // a
+			// Relative volume change, left       $xx xx (xx ...) // b
+			// Peak volume right                  $xx xx (xx ...)
+			// Peak volume left                   $xx xx (xx ...)
+			//   ID3v2.3 only, optional (not present in ID3v2.2):
+			// Relative volume change, right back $xx xx (xx ...) // c
+			// Relative volume change, left back  $xx xx (xx ...) // d
+			// Peak volume right back             $xx xx (xx ...)
+			// Peak volume left back              $xx xx (xx ...)
+			//   ID3v2.3 only, optional (not present in ID3v2.2):
+			// Relative volume change, center     $xx xx (xx ...) // e
+			// Peak volume center                 $xx xx (xx ...)
+			//   ID3v2.3 only, optional (not present in ID3v2.2):
+			// Relative volume change, bass       $xx xx (xx ...) // f
+			// Peak volume bass                   $xx xx (xx ...)
+
+			$frame_offset = 0;
+			$frame_incrdecrflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
+			$parsedFrame['incdec']['left']  = (bool) substr($frame_incrdecrflags, 7, 1);
+			$parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
+			$parsedFrame['volumechange']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+			if ($parsedFrame['incdec']['right'] === false) {
+				$parsedFrame['volumechange']['right'] *= -1;
+			}
+			$frame_offset += $frame_bytesvolume;
+			$parsedFrame['volumechange']['left'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+			if ($parsedFrame['incdec']['left'] === false) {
+				$parsedFrame['volumechange']['left'] *= -1;
+			}
+			$frame_offset += $frame_bytesvolume;
+			$parsedFrame['peakvolume']['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+			$frame_offset += $frame_bytesvolume;
+			$parsedFrame['peakvolume']['left']  = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+			$frame_offset += $frame_bytesvolume;
+			if ($id3v2_majorversion == 3) {
+				$parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
+				if (strlen($parsedFrame['data']) > 0) {
+					$parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
+					$parsedFrame['incdec']['leftrear']  = (bool) substr($frame_incrdecrflags, 5, 1);
+					$parsedFrame['volumechange']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					if ($parsedFrame['incdec']['rightrear'] === false) {
+						$parsedFrame['volumechange']['rightrear'] *= -1;
+					}
+					$frame_offset += $frame_bytesvolume;
+					$parsedFrame['volumechange']['leftrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					if ($parsedFrame['incdec']['leftrear'] === false) {
+						$parsedFrame['volumechange']['leftrear'] *= -1;
+					}
+					$frame_offset += $frame_bytesvolume;
+					$parsedFrame['peakvolume']['rightrear'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					$frame_offset += $frame_bytesvolume;
+					$parsedFrame['peakvolume']['leftrear']  = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					$frame_offset += $frame_bytesvolume;
+				}
+				$parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
+				if (strlen($parsedFrame['data']) > 0) {
+					$parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
+					$parsedFrame['volumechange']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					if ($parsedFrame['incdec']['center'] === false) {
+						$parsedFrame['volumechange']['center'] *= -1;
+					}
+					$frame_offset += $frame_bytesvolume;
+					$parsedFrame['peakvolume']['center'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					$frame_offset += $frame_bytesvolume;
+				}
+				$parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
+				if (strlen($parsedFrame['data']) > 0) {
+					$parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
+					$parsedFrame['volumechange']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					if ($parsedFrame['incdec']['bass'] === false) {
+						$parsedFrame['volumechange']['bass'] *= -1;
+					}
+					$frame_offset += $frame_bytesvolume;
+					$parsedFrame['peakvolume']['bass'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
+					$frame_offset += $frame_bytesvolume;
+				}
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'EQU2')) { // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
+			//   There may be more than one 'EQU2' frame in each tag,
+			//   but only one with the same identification string
+			// <Header of 'Equalisation (2)', ID: 'EQU2'>
+			// Interpolation method  $xx
+			//   $00  Band
+			//   $01  Linear
+			// Identification        <text string> $00
+			//   The following is then repeated for every adjustment point
+			// Frequency          $xx xx
+			// Volume adjustment  $xx xx
+
+			$frame_offset = 0;
+			$frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_idstring) === 0) {
+				$frame_idstring = '';
+			}
+			$parsedFrame['description'] = $frame_idstring;
+			$frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen("\x00"));
+			while (strlen($frame_remainingdata)) {
+				$frame_frequency = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
+				$parsedFrame['data'][$frame_frequency] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
+				$frame_remainingdata = substr($frame_remainingdata, 4);
+			}
+			$parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion == 3) && ($parsedFrame['frame_name'] == 'EQUA')) || // 4.12  EQUA Equalisation (ID3v2.3 only)
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'EQU'))) {     // 4.13  EQU  Equalisation (ID3v2.2 only)
+			//   There may only be one 'EQUA' frame in each tag
+			// <Header for 'Relative volume adjustment', ID: 'EQU'>
+			// Adjustment bits    $xx
+			//   This is followed by 2 bytes + ('adjustment bits' rounded up to the
+			//   nearest byte) for every equalisation band in the following format,
+			//   giving a frequency range of 0 - 32767Hz:
+			// Increment/decrement   %x (MSB of the Frequency)
+			// Frequency             (lower 15 bits)
+			// Adjustment            $xx (xx ...)
+
+			$frame_offset = 0;
+			$parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
+			$frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
+
+			$frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
+			while (strlen($frame_remainingdata) > 0) {
+				$frame_frequencystr = getid3_lib::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
+				$frame_incdec    = (bool) substr($frame_frequencystr, 0, 1);
+				$frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
+				$parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
+				$parsedFrame[$frame_frequency]['adjustment'] = getid3_lib::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
+				if ($parsedFrame[$frame_frequency]['incdec'] === false) {
+					$parsedFrame[$frame_frequency]['adjustment'] *= -1;
+				}
+				$frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RVRB')) || // 4.13  RVRB Reverb
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'REV'))) {     // 4.14  REV  Reverb
+			//   There may only be one 'RVRB' frame in each tag.
+			// <Header for 'Reverb', ID: 'RVRB'>
+			// Reverb left (ms)                 $xx xx
+			// Reverb right (ms)                $xx xx
+			// Reverb bounces, left             $xx
+			// Reverb bounces, right            $xx
+			// Reverb feedback, left to left    $xx
+			// Reverb feedback, left to right   $xx
+			// Reverb feedback, right to right  $xx
+			// Reverb feedback, right to left   $xx
+			// Premix left to right             $xx
+			// Premix right to left             $xx
+
+			$frame_offset = 0;
+			$parsedFrame['left']  = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$parsedFrame['right'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$parsedFrame['bouncesL']      = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['bouncesR']      = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['feedbackLL']    = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['feedbackLR']    = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['feedbackRR']    = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['feedbackRL']    = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['premixLR']      = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['premixRL']      = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'APIC')) || // 4.14  APIC Attached picture
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'PIC'))) {     // 4.15  PIC  Attached picture
+			//   There may be several pictures attached to one file,
+			//   each in their individual 'APIC' frame, but only one
+			//   with the same content descriptor
+			// <Header for 'Attached picture', ID: 'APIC'>
+			// Text encoding      $xx
+			// ID3v2.3+ => MIME type          <text string> $00
+			// ID3v2.2  => Image format       $xx xx xx
+			// Picture type       $xx
+			// Description        <text string according to encoding> $00 (00)
+			// Picture data       <binary data>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+
+			if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
+				$frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
+				if (strtolower($frame_imagetype) == 'ima') {
+					// complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
+					// MIME type instead of 3-char ID3v2.2-format image type  (thanks xbhoffØpacbell*net)
+					$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+					$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+					if (ord($frame_mimetype) === 0) {
+						$frame_mimetype = '';
+					}
+					$frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
+					if ($frame_imagetype == 'JPEG') {
+						$frame_imagetype = 'JPG';
+					}
+					$frame_offset = $frame_terminatorpos + strlen("\x00");
+				} else {
+					$frame_offset += 3;
+				}
+			}
+			if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
+				$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+				$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+				if (ord($frame_mimetype) === 0) {
+					$frame_mimetype = '';
+				}
+				$frame_offset = $frame_terminatorpos + strlen("\x00");
+			}
+
+			$frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$parsedFrame['encodingid']       = $frame_textencoding;
+			$parsedFrame['encoding']         = $this->TextEncodingNameLookup($frame_textencoding);
+
+			if ($id3v2_majorversion == 2) {
+				$parsedFrame['imagetype']    = $frame_imagetype;
+			} else {
+				$parsedFrame['mime']         = $frame_mimetype;
+			}
+			$parsedFrame['picturetypeid']    = $frame_picturetype;
+			$parsedFrame['picturetype']      = $this->APICPictureTypeLookup($frame_picturetype);
+			$parsedFrame['description']      = $frame_description;
+			$parsedFrame['data']             = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
+
+			$imageinfo = array();
+			$imagechunkcheck = getid3_lib::GetDataImageSize($parsedFrame['data'], $imageinfo);
+			if (($imagechunkcheck[2] >= 1) && ($imagechunkcheck[2] <= 3)) {
+				$parsedFrame['image_mime']       = 'image/'.getid3_lib::ImageTypesLookup($imagechunkcheck[2]);
+				if ($imagechunkcheck[0]) {
+					$parsedFrame['image_width']  = $imagechunkcheck[0];
+				}
+				if ($imagechunkcheck[1]) {
+					$parsedFrame['image_height'] = $imagechunkcheck[1];
+				}
+				$parsedFrame['image_bytes']      = strlen($parsedFrame['data']);
+			}
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GEOB')) || // 4.15  GEOB General encapsulated object
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'GEO'))) {     // 4.16  GEO  General encapsulated object
+			//   There may be more than one 'GEOB' frame in each tag,
+			//   but only one with the same content descriptor
+			// <Header for 'General encapsulated object', ID: 'GEOB'>
+			// Text encoding          $xx
+			// MIME type              <text string> $00
+			// Filename               <text string according to encoding> $00 (00)
+			// Content description    <text string according to encoding> $00 (00)
+			// Encapsulated object    <binary data>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_mimetype) === 0) {
+				$frame_mimetype = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_filename) === 0) {
+				$frame_filename = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
+
+			$parsedFrame['objectdata']  = (string) substr($parsedFrame['data'], $frame_offset);
+			$parsedFrame['encodingid']  = $frame_textencoding;
+			$parsedFrame['encoding']    = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['mime']        = $frame_mimetype;
+			$parsedFrame['filename']    = $frame_filename;
+			$parsedFrame['description'] = $frame_description;
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PCNT')) || // 4.16  PCNT Play counter
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CNT'))) {     // 4.17  CNT  Play counter
+			//   There may only be one 'PCNT' frame in each tag.
+			//   When the counter reaches all one's, one byte is inserted in
+			//   front of the counter thus making the counter eight bits bigger
+			// <Header for 'Play counter', ID: 'PCNT'>
+			// Counter        $xx xx xx xx (xx ...)
+
+			$parsedFrame['data']          = getid3_lib::BigEndian2Int($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POPM')) || // 4.17  POPM Popularimeter
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'POP'))) {     // 4.18  POP  Popularimeter
+			//   There may be more than one 'POPM' frame in each tag,
+			//   but only one with the same email address
+			// <Header for 'Popularimeter', ID: 'POPM'>
+			// Email to user   <text string> $00
+			// Rating          $xx
+			// Counter         $xx xx xx xx (xx ...)
+
+			$frame_offset = 0;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_emailaddress) === 0) {
+				$frame_emailaddress = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+			$frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['data'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
+			$parsedFrame['email']  = $frame_emailaddress;
+			$parsedFrame['rating'] = $frame_rating;
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RBUF')) || // 4.18  RBUF Recommended buffer size
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'BUF'))) {     // 4.19  BUF  Recommended buffer size
+			//   There may only be one 'RBUF' frame in each tag
+			// <Header for 'Recommended buffer size', ID: 'RBUF'>
+			// Buffer size               $xx xx xx
+			// Embedded info flag        %0000000x
+			// Offset to next tag        $xx xx xx xx
+
+			$frame_offset = 0;
+			$parsedFrame['buffersize'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
+			$frame_offset += 3;
+
+			$frame_embeddedinfoflags = getid3_lib::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
+			$parsedFrame['nexttagoffset'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRM')) { // 4.20  Encrypted meta frame (ID3v2.2 only)
+			//   There may be more than one 'CRM' frame in a tag,
+			//   but only one with the same 'owner identifier'
+			// <Header for 'Encrypted meta frame', ID: 'CRM'>
+			// Owner identifier      <textstring> $00 (00)
+			// Content/explanation   <textstring> $00 (00)
+			// Encrypted datablock   <binary data>
+
+			$frame_offset = 0;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$parsedFrame['ownerid']     = $frame_ownerid;
+			$parsedFrame['data']        = (string) substr($parsedFrame['data'], $frame_offset);
+			$parsedFrame['description'] = $frame_description;
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'AENC')) || // 4.19  AENC Audio encryption
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'CRA'))) {     // 4.21  CRA  Audio encryption
+			//   There may be more than one 'AENC' frames in a tag,
+			//   but only one with the same 'Owner identifier'
+			// <Header for 'Audio encryption', ID: 'AENC'>
+			// Owner identifier   <text string> $00
+			// Preview start      $xx xx
+			// Preview length     $xx xx
+			// Encryption info    <binary data>
+
+			$frame_offset = 0;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_ownerid) === 0) {
+				$frame_ownerid == '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+			$parsedFrame['ownerid'] = $frame_ownerid;
+			$parsedFrame['previewstart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$parsedFrame['previewlength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
+			unset($parsedFrame['data']);
+
+
+		} elseif ((($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'LINK')) || // 4.20  LINK Linked information
+				(($id3v2_majorversion == 2) && ($parsedFrame['frame_name'] == 'LNK'))) {     // 4.22  LNK  Linked information
+			//   There may be more than one 'LINK' frame in a tag,
+			//   but only one with the same contents
+			// <Header for 'Linked information', ID: 'LINK'>
+			// ID3v2.3+ => Frame identifier   $xx xx xx xx
+			// ID3v2.2  => Frame identifier   $xx xx xx
+			// URL                            <text string> $00
+			// ID and additional data         <text string(s)>
+
+			$frame_offset = 0;
+			if ($id3v2_majorversion == 2) {
+				$parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
+				$frame_offset += 3;
+			} else {
+				$parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
+				$frame_offset += 4;
+			}
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_url) === 0) {
+				$frame_url = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+			$parsedFrame['url'] = $frame_url;
+
+			$parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
+			if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'POSS')) { // 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
+			//   There may only be one 'POSS' frame in each tag
+			// <Head for 'Position synchronisation', ID: 'POSS'>
+			// Time stamp format         $xx
+			// Position                  $xx (xx ...)
+
+			$frame_offset = 0;
+			$parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['position']        = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'USER')) { // 4.22  USER Terms of use (ID3v2.3+ only)
+			//   There may be more than one 'Terms of use' frame in a tag,
+			//   but only one with the same 'Language'
+			// <Header for 'Terms of use frame', ID: 'USER'>
+			// Text encoding        $xx
+			// Language             $xx xx xx
+			// The actual text      <text string according to encoding>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$frame_language = substr($parsedFrame['data'], $frame_offset, 3);
+			$frame_offset += 3;
+			$parsedFrame['language']     = $frame_language;
+			$parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
+			$parsedFrame['encodingid']   = $frame_textencoding;
+			$parsedFrame['encoding']     = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['data']         = (string) substr($parsedFrame['data'], $frame_offset);
+			if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
+				$ThisFileInfo['id3v2']['comments'][$parsedFrame['framenameshort']][] = getid3_lib::iconv_fallback($parsedFrame['encoding'], $ThisFileInfo['id3v2']['encoding'], $parsedFrame['data']);
+			}
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'OWNE')) { // 4.23  OWNE Ownership frame (ID3v2.3+ only)
+			//   There may only be one 'OWNE' frame in a tag
+			// <Header for 'Ownership frame', ID: 'OWNE'>
+			// Text encoding     $xx
+			// Price paid        <text string> $00
+			// Date of purch.    <text string>
+			// Seller            <text string according to encoding>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+			$parsedFrame['encodingid'] = $frame_textencoding;
+			$parsedFrame['encoding']   = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
+			$parsedFrame['pricepaid']['currency']   = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
+			$parsedFrame['pricepaid']['value']      = substr($frame_pricepaid, 3);
+
+			$parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
+			if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
+				$parsedFrame['purchasedateunix'] = mktime (0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
+			}
+			$frame_offset += 8;
+
+			$parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'COMR')) { // 4.24  COMR Commercial frame (ID3v2.3+ only)
+			//   There may be more than one 'commercial frame' in a tag,
+			//   but no two may be identical
+			// <Header for 'Commercial frame', ID: 'COMR'>
+			// Text encoding      $xx
+			// Price string       <text string> $00
+			// Valid until        <text string>
+			// Contact URL        <text string> $00
+			// Received as        $xx
+			// Name of seller     <text string according to encoding> $00 (00)
+			// Description        <text string according to encoding> $00 (00)
+			// Picture MIME type  <string> $00
+			// Seller logo        <binary data>
+
+			$frame_offset = 0;
+			$frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			if ((($id3v2_majorversion <= 3) && ($frame_textencoding > 1)) || (($id3v2_majorversion == 4) && ($frame_textencoding > 3))) {
+				$ThisFileInfo['warning'][] = 'Invalid text encoding byte ('.$frame_textencoding.') in frame "'.$parsedFrame['frame_name'].'" - defaulting to ISO-8859-1 encoding';
+			}
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+			$frame_rawpricearray = explode('/', $frame_pricestring);
+			foreach ($frame_rawpricearray as $key => $val) {
+				$frame_currencyid = substr($val, 0, 3);
+				$parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
+				$parsedFrame['price'][$frame_currencyid]['value']    = substr($val, 3);
+			}
+
+			$frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
+			$frame_offset += 8;
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_sellername) === 0) {
+				$frame_sellername = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
+			if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
+				$frame_terminatorpos++; // @strpos() fooled because 2nd byte of Unicode chars are often 0x00
+			}
+			$frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_description) === 0) {
+				$frame_description = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
+
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
+
+			$parsedFrame['encodingid']        = $frame_textencoding;
+			$parsedFrame['encoding']          = $this->TextEncodingNameLookup($frame_textencoding);
+
+			$parsedFrame['pricevaliduntil']   = $frame_datestring;
+			$parsedFrame['contacturl']        = $frame_contacturl;
+			$parsedFrame['receivedasid']      = $frame_receivedasid;
+			$parsedFrame['receivedas']        = $this->COMRReceivedAsLookup($frame_receivedasid);
+			$parsedFrame['sellername']        = $frame_sellername;
+			$parsedFrame['description']       = $frame_description;
+			$parsedFrame['mime']              = $frame_mimetype;
+			$parsedFrame['logo']              = $frame_sellerlogo;
+			unset($parsedFrame['data']);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'ENCR')) { // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
+			//   There may be several 'ENCR' frames in a tag,
+			//   but only one containing the same symbol
+			//   and only one containing the same owner identifier
+			// <Header for 'Encryption method registration', ID: 'ENCR'>
+			// Owner identifier    <text string> $00
+			// Method symbol       $xx
+			// Encryption data     <binary data>
+
+			$frame_offset = 0;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_ownerid) === 0) {
+				$frame_ownerid = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$parsedFrame['ownerid']      = $frame_ownerid;
+			$parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['data']         = (string) substr($parsedFrame['data'], $frame_offset);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'GRID')) { // 4.26  GRID Group identification registration (ID3v2.3+ only)
+
+			//   There may be several 'GRID' frames in a tag,
+			//   but only one containing the same symbol
+			//   and only one containing the same owner identifier
+			// <Header for 'Group ID registration', ID: 'GRID'>
+			// Owner identifier      <text string> $00
+			// Group symbol          $xx
+			// Group dependent data  <binary data>
+
+			$frame_offset = 0;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_ownerid) === 0) {
+				$frame_ownerid = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$parsedFrame['ownerid']       = $frame_ownerid;
+			$parsedFrame['groupsymbol']   = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['data']          = (string) substr($parsedFrame['data'], $frame_offset);
+
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'PRIV')) { // 4.27  PRIV Private frame (ID3v2.3+ only)
+			//   The tag may contain more than one 'PRIV' frame
+			//   but only with different contents
+			// <Header for 'Private frame', ID: 'PRIV'>
+			// Owner identifier      <text string> $00
+			// The private data      <binary data>
+
+			$frame_offset = 0;
+			$frame_terminatorpos = @strpos($parsedFrame['data'], "\x00", $frame_offset);
+			$frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
+			if (ord($frame_ownerid) === 0) {
+				$frame_ownerid = '';
+			}
+			$frame_offset = $frame_terminatorpos + strlen("\x00");
+
+			$parsedFrame['ownerid'] = $frame_ownerid;
+			$parsedFrame['data']    = (string) substr($parsedFrame['data'], $frame_offset);
+
+
+		} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SIGN')) { // 4.28  SIGN Signature frame (ID3v2.4+ only)
+			//   There may be more than one 'signature frame' in a tag,
+			//   but no two may be identical
+			// <Header for 'Signature frame', ID: 'SIGN'>
+			// Group symbol      $xx
+			// Signature         <binary data>
+
+			$frame_offset = 0;
+			$parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$parsedFrame['data']        = (string) substr($parsedFrame['data'], $frame_offset);
+
+
+		} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'SEEK')) { // 4.29  SEEK Seek frame (ID3v2.4+ only)
+			//   There may only be one 'seek frame' in a tag
+			// <Header for 'Seek frame', ID: 'SEEK'>
+			// Minimum offset to next tag       $xx xx xx xx
+
+			$frame_offset = 0;
+			$parsedFrame['data']          = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
+
+
+		} elseif (($id3v2_majorversion >= 4) && ($parsedFrame['frame_name'] == 'ASPI')) { // 4.30  ASPI Audio seek point index (ID3v2.4+ only)
+			//   There may only be one 'audio seek point index' frame in a tag
+			// <Header for 'Seek Point Index', ID: 'ASPI'>
+			// Indexed data start (S)         $xx xx xx xx
+			// Indexed data length (L)        $xx xx xx xx
+			// Number of index points (N)     $xx xx
+			// Bits per index point (b)       $xx
+			//   Then for every index point the following data is included:
+			// Fraction at index (Fi)          $xx (xx)
+
+			$frame_offset = 0;
+			$parsedFrame['datastart'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
+			$frame_offset += 4;
+			$parsedFrame['indexeddatalength'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
+			$frame_offset += 4;
+			$parsedFrame['indexpoints'] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
+			$frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
+			for ($i = 0; $i < $frame_indexpoints; $i++) {
+				$parsedFrame['indexes'][$i] = getid3_lib::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
+				$frame_offset += $frame_bytesperpoint;
+			}
+			unset($parsedFrame['data']);
+
+		} elseif (($id3v2_majorversion >= 3) && ($parsedFrame['frame_name'] == 'RGAD')) { // Replay Gain Adjustment
+			// http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
+			//   There may only be one 'RGAD' frame in a tag
+			// <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
+			// Peak Amplitude                      $xx $xx $xx $xx
+			// Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
+			// Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
+			//   a - name code
+			//   b - originator code
+			//   c - sign bit
+			//   d - replay gain adjustment
+
+			$frame_offset = 0;
+			$parsedFrame['peakamplitude'] = getid3_lib::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
+			$frame_offset += 4;
+			$rg_track_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$rg_album_adjustment = getid3_lib::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
+			$frame_offset += 2;
+			$parsedFrame['raw']['track']['name']       = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 0, 3));
+			$parsedFrame['raw']['track']['originator'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 3, 3));
+			$parsedFrame['raw']['track']['signbit']    = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 6, 1));
+			$parsedFrame['raw']['track']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_track_adjustment, 7, 9));
+			$parsedFrame['raw']['album']['name']       = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 0, 3));
+			$parsedFrame['raw']['album']['originator'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 3, 3));
+			$parsedFrame['raw']['album']['signbit']    = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 6, 1));
+			$parsedFrame['raw']['album']['adjustment'] = getid3_lib::Bin2Dec(substr($rg_album_adjustment, 7, 9));
+			$parsedFrame['track']['name']       = getid3_lib::RGADnameLookup($parsedFrame['raw']['track']['name']);
+			$parsedFrame['track']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
+			$parsedFrame['track']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
+			$parsedFrame['album']['name']       = getid3_lib::RGADnameLookup($parsedFrame['raw']['album']['name']);
+			$parsedFrame['album']['originator'] = getid3_lib::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
+			$parsedFrame['album']['adjustment'] = getid3_lib::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
+
+			$ThisFileInfo['replay_gain']['track']['peak']       = $parsedFrame['peakamplitude'];
+			$ThisFileInfo['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
+			$ThisFileInfo['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
+			$ThisFileInfo['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
+			$ThisFileInfo['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
+
+			unset($parsedFrame['data']);
+
+		}
+
+		return true;
+	}
+
+
+	function DeUnsynchronise($data) {
+		return str_replace("\xFF\x00", "\xFF", $data);
+	}
+
+	function LookupCurrencyUnits($currencyid) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+
+			AED	Dirhams
+			AFA	Afghanis
+			ALL	Leke
+			AMD	Drams
+			ANG	Guilders
+			AOA	Kwanza
+			ARS	Pesos
+			ATS	Schillings
+			AUD	Dollars
+			AWG	Guilders
+			AZM	Manats
+			BAM	Convertible Marka
+			BBD	Dollars
+			BDT	Taka
+			BEF	Francs
+			BGL	Leva
+			BHD	Dinars
+			BIF	Francs
+			BMD	Dollars
+			BND	Dollars
+			BOB	Bolivianos
+			BRL	Brazil Real
+			BSD	Dollars
+			BTN	Ngultrum
+			BWP	Pulas
+			BYR	Rubles
+			BZD	Dollars
+			CAD	Dollars
+			CDF	Congolese Francs
+			CHF	Francs
+			CLP	Pesos
+			CNY	Yuan Renminbi
+			COP	Pesos
+			CRC	Colones
+			CUP	Pesos
+			CVE	Escudos
+			CYP	Pounds
+			CZK	Koruny
+			DEM	Deutsche Marks
+			DJF	Francs
+			DKK	Kroner
+			DOP	Pesos
+			DZD	Algeria Dinars
+			EEK	Krooni
+			EGP	Pounds
+			ERN	Nakfa
+			ESP	Pesetas
+			ETB	Birr
+			EUR	Euro
+			FIM	Markkaa
+			FJD	Dollars
+			FKP	Pounds
+			FRF	Francs
+			GBP	Pounds
+			GEL	Lari
+			GGP	Pounds
+			GHC	Cedis
+			GIP	Pounds
+			GMD	Dalasi
+			GNF	Francs
+			GRD	Drachmae
+			GTQ	Quetzales
+			GYD	Dollars
+			HKD	Dollars
+			HNL	Lempiras
+			HRK	Kuna
+			HTG	Gourdes
+			HUF	Forints
+			IDR	Rupiahs
+			IEP	Pounds
+			ILS	New Shekels
+			IMP	Pounds
+			INR	Rupees
+			IQD	Dinars
+			IRR	Rials
+			ISK	Kronur
+			ITL	Lire
+			JEP	Pounds
+			JMD	Dollars
+			JOD	Dinars
+			JPY	Yen
+			KES	Shillings
+			KGS	Soms
+			KHR	Riels
+			KMF	Francs
+			KPW	Won
+			KWD	Dinars
+			KYD	Dollars
+			KZT	Tenge
+			LAK	Kips
+			LBP	Pounds
+			LKR	Rupees
+			LRD	Dollars
+			LSL	Maloti
+			LTL	Litai
+			LUF	Francs
+			LVL	Lati
+			LYD	Dinars
+			MAD	Dirhams
+			MDL	Lei
+			MGF	Malagasy Francs
+			MKD	Denars
+			MMK	Kyats
+			MNT	Tugriks
+			MOP	Patacas
+			MRO	Ouguiyas
+			MTL	Liri
+			MUR	Rupees
+			MVR	Rufiyaa
+			MWK	Kwachas
+			MXN	Pesos
+			MYR	Ringgits
+			MZM	Meticais
+			NAD	Dollars
+			NGN	Nairas
+			NIO	Gold Cordobas
+			NLG	Guilders
+			NOK	Krone
+			NPR	Nepal Rupees
+			NZD	Dollars
+			OMR	Rials
+			PAB	Balboa
+			PEN	Nuevos Soles
+			PGK	Kina
+			PHP	Pesos
+			PKR	Rupees
+			PLN	Zlotych
+			PTE	Escudos
+			PYG	Guarani
+			QAR	Rials
+			ROL	Lei
+			RUR	Rubles
+			RWF	Rwanda Francs
+			SAR	Riyals
+			SBD	Dollars
+			SCR	Rupees
+			SDD	Dinars
+			SEK	Kronor
+			SGD	Dollars
+			SHP	Pounds
+			SIT	Tolars
+			SKK	Koruny
+			SLL	Leones
+			SOS	Shillings
+			SPL	Luigini
+			SRG	Guilders
+			STD	Dobras
+			SVC	Colones
+			SYP	Pounds
+			SZL	Emalangeni
+			THB	Baht
+			TJR	Rubles
+			TMM	Manats
+			TND	Dinars
+			TOP	Pa'anga
+			TRL	Liras
+			TTD	Dollars
+			TVD	Tuvalu Dollars
+			TWD	New Dollars
+			TZS	Shillings
+			UAH	Hryvnia
+			UGX	Shillings
+			USD	Dollars
+			UYU	Pesos
+			UZS	Sums
+			VAL	Lire
+			VEB	Bolivares
+			VND	Dong
+			VUV	Vatu
+			WST	Tala
+			XAF	Francs
+			XAG	Ounces
+			XAU	Ounces
+			XCD	Dollars
+			XDR	Special Drawing Rights
+			XPD	Ounces
+			XPF	Francs
+			XPT	Ounces
+			YER	Rials
+			YUM	New Dinars
+			ZAR	Rand
+			ZMK	Kwacha
+			ZWD	Zimbabwe Dollars
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-units');
+	}
+
+
+	function LookupCurrencyCountry($currencyid) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			AED	United Arab Emirates
+			AFA	Afghanistan
+			ALL	Albania
+			AMD	Armenia
+			ANG	Netherlands Antilles
+			AOA	Angola
+			ARS	Argentina
+			ATS	Austria
+			AUD	Australia
+			AWG	Aruba
+			AZM	Azerbaijan
+			BAM	Bosnia and Herzegovina
+			BBD	Barbados
+			BDT	Bangladesh
+			BEF	Belgium
+			BGL	Bulgaria
+			BHD	Bahrain
+			BIF	Burundi
+			BMD	Bermuda
+			BND	Brunei Darussalam
+			BOB	Bolivia
+			BRL	Brazil
+			BSD	Bahamas
+			BTN	Bhutan
+			BWP	Botswana
+			BYR	Belarus
+			BZD	Belize
+			CAD	Canada
+			CDF	Congo/Kinshasa
+			CHF	Switzerland
+			CLP	Chile
+			CNY	China
+			COP	Colombia
+			CRC	Costa Rica
+			CUP	Cuba
+			CVE	Cape Verde
+			CYP	Cyprus
+			CZK	Czech Republic
+			DEM	Germany
+			DJF	Djibouti
+			DKK	Denmark
+			DOP	Dominican Republic
+			DZD	Algeria
+			EEK	Estonia
+			EGP	Egypt
+			ERN	Eritrea
+			ESP	Spain
+			ETB	Ethiopia
+			EUR	Euro Member Countries
+			FIM	Finland
+			FJD	Fiji
+			FKP	Falkland Islands (Malvinas)
+			FRF	France
+			GBP	United Kingdom
+			GEL	Georgia
+			GGP	Guernsey
+			GHC	Ghana
+			GIP	Gibraltar
+			GMD	Gambia
+			GNF	Guinea
+			GRD	Greece
+			GTQ	Guatemala
+			GYD	Guyana
+			HKD	Hong Kong
+			HNL	Honduras
+			HRK	Croatia
+			HTG	Haiti
+			HUF	Hungary
+			IDR	Indonesia
+			IEP	Ireland (Eire)
+			ILS	Israel
+			IMP	Isle of Man
+			INR	India
+			IQD	Iraq
+			IRR	Iran
+			ISK	Iceland
+			ITL	Italy
+			JEP	Jersey
+			JMD	Jamaica
+			JOD	Jordan
+			JPY	Japan
+			KES	Kenya
+			KGS	Kyrgyzstan
+			KHR	Cambodia
+			KMF	Comoros
+			KPW	Korea
+			KWD	Kuwait
+			KYD	Cayman Islands
+			KZT	Kazakstan
+			LAK	Laos
+			LBP	Lebanon
+			LKR	Sri Lanka
+			LRD	Liberia
+			LSL	Lesotho
+			LTL	Lithuania
+			LUF	Luxembourg
+			LVL	Latvia
+			LYD	Libya
+			MAD	Morocco
+			MDL	Moldova
+			MGF	Madagascar
+			MKD	Macedonia
+			MMK	Myanmar (Burma)
+			MNT	Mongolia
+			MOP	Macau
+			MRO	Mauritania
+			MTL	Malta
+			MUR	Mauritius
+			MVR	Maldives (Maldive Islands)
+			MWK	Malawi
+			MXN	Mexico
+			MYR	Malaysia
+			MZM	Mozambique
+			NAD	Namibia
+			NGN	Nigeria
+			NIO	Nicaragua
+			NLG	Netherlands (Holland)
+			NOK	Norway
+			NPR	Nepal
+			NZD	New Zealand
+			OMR	Oman
+			PAB	Panama
+			PEN	Peru
+			PGK	Papua New Guinea
+			PHP	Philippines
+			PKR	Pakistan
+			PLN	Poland
+			PTE	Portugal
+			PYG	Paraguay
+			QAR	Qatar
+			ROL	Romania
+			RUR	Russia
+			RWF	Rwanda
+			SAR	Saudi Arabia
+			SBD	Solomon Islands
+			SCR	Seychelles
+			SDD	Sudan
+			SEK	Sweden
+			SGD	Singapore
+			SHP	Saint Helena
+			SIT	Slovenia
+			SKK	Slovakia
+			SLL	Sierra Leone
+			SOS	Somalia
+			SPL	Seborga
+			SRG	Suriname
+			STD	São Tome and Principe
+			SVC	El Salvador
+			SYP	Syria
+			SZL	Swaziland
+			THB	Thailand
+			TJR	Tajikistan
+			TMM	Turkmenistan
+			TND	Tunisia
+			TOP	Tonga
+			TRL	Turkey
+			TTD	Trinidad and Tobago
+			TVD	Tuvalu
+			TWD	Taiwan
+			TZS	Tanzania
+			UAH	Ukraine
+			UGX	Uganda
+			USD	United States of America
+			UYU	Uruguay
+			UZS	Uzbekistan
+			VAL	Vatican City
+			VEB	Venezuela
+			VND	Viet Nam
+			VUV	Vanuatu
+			WST	Samoa
+			XAF	Communauté Financière Africaine
+			XAG	Silver
+			XAU	Gold
+			XCD	East Caribbean
+			XDR	International Monetary Fund
+			XPD	Palladium
+			XPF	Comptoirs Français du Pacifique
+			XPT	Platinum
+			YER	Yemen
+			YUM	Yugoslavia
+			ZAR	South Africa
+			ZMK	Zambia
+			ZWD	Zimbabwe
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($currencyid, $begin, __LINE__, __FILE__, 'id3v2-currency-country');
+	}
+
+
+
+	function LanguageLookup($languagecode, $casesensitive=false) {
+
+		if (!$casesensitive) {
+			$languagecode = strtolower($languagecode);
+		}
+
+		// http://www.id3.org/id3v2.4.0-structure.txt
+		// [4.   ID3v2 frame overview]
+		// The three byte language field, present in several frames, is used to
+		// describe the language of the frame's content, according to ISO-639-2
+		// [ISO-639-2]. The language should be represented in lower case. If the
+		// language is not known the string "XXX" should be used.
+
+
+		// ISO 639-2 - http://www.id3.org/iso639-2.html
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			XXX	unknown
+			xxx	unknown
+			aar	Afar
+			abk	Abkhazian
+			ace	Achinese
+			ach	Acoli
+			ada	Adangme
+			afa	Afro-Asiatic (Other)
+			afh	Afrihili
+			afr	Afrikaans
+			aka	Akan
+			akk	Akkadian
+			alb	Albanian
+			ale	Aleut
+			alg	Algonquian Languages
+			amh	Amharic
+			ang	English, Old (ca. 450-1100)
+			apa	Apache Languages
+			ara	Arabic
+			arc	Aramaic
+			arm	Armenian
+			arn	Araucanian
+			arp	Arapaho
+			art	Artificial (Other)
+			arw	Arawak
+			asm	Assamese
+			ath	Athapascan Languages
+			ava	Avaric
+			ave	Avestan
+			awa	Awadhi
+			aym	Aymara
+			aze	Azerbaijani
+			bad	Banda
+			bai	Bamileke Languages
+			bak	Bashkir
+			bal	Baluchi
+			bam	Bambara
+			ban	Balinese
+			baq	Basque
+			bas	Basa
+			bat	Baltic (Other)
+			bej	Beja
+			bel	Byelorussian
+			bem	Bemba
+			ben	Bengali
+			ber	Berber (Other)
+			bho	Bhojpuri
+			bih	Bihari
+			bik	Bikol
+			bin	Bini
+			bis	Bislama
+			bla	Siksika
+			bnt	Bantu (Other)
+			bod	Tibetan
+			bra	Braj
+			bre	Breton
+			bua	Buriat
+			bug	Buginese
+			bul	Bulgarian
+			bur	Burmese
+			cad	Caddo
+			cai	Central American Indian (Other)
+			car	Carib
+			cat	Catalan
+			cau	Caucasian (Other)
+			ceb	Cebuano
+			cel	Celtic (Other)
+			ces	Czech
+			cha	Chamorro
+			chb	Chibcha
+			che	Chechen
+			chg	Chagatai
+			chi	Chinese
+			chm	Mari
+			chn	Chinook jargon
+			cho	Choctaw
+			chr	Cherokee
+			chu	Church Slavic
+			chv	Chuvash
+			chy	Cheyenne
+			cop	Coptic
+			cor	Cornish
+			cos	Corsican
+			cpe	Creoles and Pidgins, English-based (Other)
+			cpf	Creoles and Pidgins, French-based (Other)
+			cpp	Creoles and Pidgins, Portuguese-based (Other)
+			cre	Cree
+			crp	Creoles and Pidgins (Other)
+			cus	Cushitic (Other)
+			cym	Welsh
+			cze	Czech
+			dak	Dakota
+			dan	Danish
+			del	Delaware
+			deu	German
+			din	Dinka
+			div	Divehi
+			doi	Dogri
+			dra	Dravidian (Other)
+			dua	Duala
+			dum	Dutch, Middle (ca. 1050-1350)
+			dut	Dutch
+			dyu	Dyula
+			dzo	Dzongkha
+			efi	Efik
+			egy	Egyptian (Ancient)
+			eka	Ekajuk
+			ell	Greek, Modern (1453-)
+			elx	Elamite
+			eng	English
+			enm	English, Middle (ca. 1100-1500)
+			epo	Esperanto
+			esk	Eskimo (Other)
+			esl	Spanish
+			est	Estonian
+			eus	Basque
+			ewe	Ewe
+			ewo	Ewondo
+			fan	Fang
+			fao	Faroese
+			fas	Persian
+			fat	Fanti
+			fij	Fijian
+			fin	Finnish
+			fiu	Finno-Ugrian (Other)
+			fon	Fon
+			fra	French
+			fre	French
+			frm	French, Middle (ca. 1400-1600)
+			fro	French, Old (842- ca. 1400)
+			fry	Frisian
+			ful	Fulah
+			gaa	Ga
+			gae	Gaelic (Scots)
+			gai	Irish
+			gay	Gayo
+			gdh	Gaelic (Scots)
+			gem	Germanic (Other)
+			geo	Georgian
+			ger	German
+			gez	Geez
+			gil	Gilbertese
+			glg	Gallegan
+			gmh	German, Middle High (ca. 1050-1500)
+			goh	German, Old High (ca. 750-1050)
+			gon	Gondi
+			got	Gothic
+			grb	Grebo
+			grc	Greek, Ancient (to 1453)
+			gre	Greek, Modern (1453-)
+			grn	Guarani
+			guj	Gujarati
+			hai	Haida
+			hau	Hausa
+			haw	Hawaiian
+			heb	Hebrew
+			her	Herero
+			hil	Hiligaynon
+			him	Himachali
+			hin	Hindi
+			hmo	Hiri Motu
+			hun	Hungarian
+			hup	Hupa
+			hye	Armenian
+			iba	Iban
+			ibo	Igbo
+			ice	Icelandic
+			ijo	Ijo
+			iku	Inuktitut
+			ilo	Iloko
+			ina	Interlingua (International Auxiliary language Association)
+			inc	Indic (Other)
+			ind	Indonesian
+			ine	Indo-European (Other)
+			ine	Interlingue
+			ipk	Inupiak
+			ira	Iranian (Other)
+			iri	Irish
+			iro	Iroquoian uages
+			isl	Icelandic
+			ita	Italian
+			jav	Javanese
+			jaw	Javanese
+			jpn	Japanese
+			jpr	Judeo-Persian
+			jrb	Judeo-Arabic
+			kaa	Kara-Kalpak
+			kab	Kabyle
+			kac	Kachin
+			kal	Greenlandic
+			kam	Kamba
+			kan	Kannada
+			kar	Karen
+			kas	Kashmiri
+			kat	Georgian
+			kau	Kanuri
+			kaw	Kawi
+			kaz	Kazakh
+			kha	Khasi
+			khi	Khoisan (Other)
+			khm	Khmer
+			kho	Khotanese
+			kik	Kikuyu
+			kin	Kinyarwanda
+			kir	Kirghiz
+			kok	Konkani
+			kom	Komi
+			kon	Kongo
+			kor	Korean
+			kpe	Kpelle
+			kro	Kru
+			kru	Kurukh
+			kua	Kuanyama
+			kum	Kumyk
+			kur	Kurdish
+			kus	Kusaie
+			kut	Kutenai
+			lad	Ladino
+			lah	Lahnda
+			lam	Lamba
+			lao	Lao
+			lat	Latin
+			lav	Latvian
+			lez	Lezghian
+			lin	Lingala
+			lit	Lithuanian
+			lol	Mongo
+			loz	Lozi
+			ltz	Letzeburgesch
+			lub	Luba-Katanga
+			lug	Ganda
+			lui	Luiseno
+			lun	Lunda
+			luo	Luo (Kenya and Tanzania)
+			mac	Macedonian
+			mad	Madurese
+			mag	Magahi
+			mah	Marshall
+			mai	Maithili
+			mak	Macedonian
+			mak	Makasar
+			mal	Malayalam
+			man	Mandingo
+			mao	Maori
+			map	Austronesian (Other)
+			mar	Marathi
+			mas	Masai
+			max	Manx
+			may	Malay
+			men	Mende
+			mga	Irish, Middle (900 - 1200)
+			mic	Micmac
+			min	Minangkabau
+			mis	Miscellaneous (Other)
+			mkh	Mon-Kmer (Other)
+			mlg	Malagasy
+			mlt	Maltese
+			mni	Manipuri
+			mno	Manobo Languages
+			moh	Mohawk
+			mol	Moldavian
+			mon	Mongolian
+			mos	Mossi
+			mri	Maori
+			msa	Malay
+			mul	Multiple Languages
+			mun	Munda Languages
+			mus	Creek
+			mwr	Marwari
+			mya	Burmese
+			myn	Mayan Languages
+			nah	Aztec
+			nai	North American Indian (Other)
+			nau	Nauru
+			nav	Navajo
+			nbl	Ndebele, South
+			nde	Ndebele, North
+			ndo	Ndongo
+			nep	Nepali
+			new	Newari
+			nic	Niger-Kordofanian (Other)
+			niu	Niuean
+			nla	Dutch
+			nno	Norwegian (Nynorsk)
+			non	Norse, Old
+			nor	Norwegian
+			nso	Sotho, Northern
+			nub	Nubian Languages
+			nya	Nyanja
+			nym	Nyamwezi
+			nyn	Nyankole
+			nyo	Nyoro
+			nzi	Nzima
+			oci	Langue d'Oc (post 1500)
+			oji	Ojibwa
+			ori	Oriya
+			orm	Oromo
+			osa	Osage
+			oss	Ossetic
+			ota	Turkish, Ottoman (1500 - 1928)
+			oto	Otomian Languages
+			paa	Papuan-Australian (Other)
+			pag	Pangasinan
+			pal	Pahlavi
+			pam	Pampanga
+			pan	Panjabi
+			pap	Papiamento
+			pau	Palauan
+			peo	Persian, Old (ca 600 - 400 B.C.)
+			per	Persian
+			phn	Phoenician
+			pli	Pali
+			pol	Polish
+			pon	Ponape
+			por	Portuguese
+			pra	Prakrit uages
+			pro	Provencal, Old (to 1500)
+			pus	Pushto
+			que	Quechua
+			raj	Rajasthani
+			rar	Rarotongan
+			roa	Romance (Other)
+			roh	Rhaeto-Romance
+			rom	Romany
+			ron	Romanian
+			rum	Romanian
+			run	Rundi
+			rus	Russian
+			sad	Sandawe
+			sag	Sango
+			sah	Yakut
+			sai	South American Indian (Other)
+			sal	Salishan Languages
+			sam	Samaritan Aramaic
+			san	Sanskrit
+			sco	Scots
+			scr	Serbo-Croatian
+			sel	Selkup
+			sem	Semitic (Other)
+			sga	Irish, Old (to 900)
+			shn	Shan
+			sid	Sidamo
+			sin	Singhalese
+			sio	Siouan Languages
+			sit	Sino-Tibetan (Other)
+			sla	Slavic (Other)
+			slk	Slovak
+			slo	Slovak
+			slv	Slovenian
+			smi	Sami Languages
+			smo	Samoan
+			sna	Shona
+			snd	Sindhi
+			sog	Sogdian
+			som	Somali
+			son	Songhai
+			sot	Sotho, Southern
+			spa	Spanish
+			sqi	Albanian
+			srd	Sardinian
+			srr	Serer
+			ssa	Nilo-Saharan (Other)
+			ssw	Siswant
+			ssw	Swazi
+			suk	Sukuma
+			sun	Sudanese
+			sus	Susu
+			sux	Sumerian
+			sve	Swedish
+			swa	Swahili
+			swe	Swedish
+			syr	Syriac
+			tah	Tahitian
+			tam	Tamil
+			tat	Tatar
+			tel	Telugu
+			tem	Timne
+			ter	Tereno
+			tgk	Tajik
+			tgl	Tagalog
+			tha	Thai
+			tib	Tibetan
+			tig	Tigre
+			tir	Tigrinya
+			tiv	Tivi
+			tli	Tlingit
+			tmh	Tamashek
+			tog	Tonga (Nyasa)
+			ton	Tonga (Tonga Islands)
+			tru	Truk
+			tsi	Tsimshian
+			tsn	Tswana
+			tso	Tsonga
+			tuk	Turkmen
+			tum	Tumbuka
+			tur	Turkish
+			tut	Altaic (Other)
+			twi	Twi
+			tyv	Tuvinian
+			uga	Ugaritic
+			uig	Uighur
+			ukr	Ukrainian
+			umb	Umbundu
+			und	Undetermined
+			urd	Urdu
+			uzb	Uzbek
+			vai	Vai
+			ven	Venda
+			vie	Vietnamese
+			vol	Volapük
+			vot	Votic
+			wak	Wakashan Languages
+			wal	Walamo
+			war	Waray
+			was	Washo
+			wel	Welsh
+			wen	Sorbian Languages
+			wol	Wolof
+			xho	Xhosa
+			yao	Yao
+			yap	Yap
+			yid	Yiddish
+			yor	Yoruba
+			zap	Zapotec
+			zen	Zenaga
+			zha	Zhuang
+			zho	Chinese
+			zul	Zulu
+			zun	Zuni
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($languagecode, $begin, __LINE__, __FILE__, 'id3v2-languagecode');
+	}
+
+
+	function ETCOEventLookup($index) {
+        if (($index >= 0x17) && ($index <= 0xDF)) {
+		    return 'reserved for future use';
+	    }
+		if (($index >= 0xE0) && ($index <= 0xEF)) {
+			return 'not predefined synch 0-F';
+		}
+		if (($index >= 0xF0) && ($index <= 0xFC)) {
+			return 'reserved for future use';
+		}
+
+		static $EventLookup = array(
+			0x00 => 'padding (has no meaning)',
+			0x01 => 'end of initial silence',
+			0x02 => 'intro start',
+			0x03 => 'main part start',
+			0x04 => 'outro start',
+			0x05 => 'outro end',
+			0x06 => 'verse start',
+			0x07 => 'refrain start',
+			0x08 => 'interlude start',
+			0x09 => 'theme start',
+			0x0A => 'variation start',
+			0x0B => 'key change',
+			0x0C => 'time change',
+			0x0D => 'momentary unwanted noise (Snap, Crackle & Pop)',
+			0x0E => 'sustained noise',
+			0x0F => 'sustained noise end',
+			0x10 => 'intro end',
+			0x11 => 'main part end',
+			0x12 => 'verse end',
+			0x13 => 'refrain end',
+			0x14 => 'theme end',
+			0x15 => 'profanity',
+			0x16 => 'profanity end',
+			0xFD => 'audio end (start of silence)',
+			0xFE => 'audio file ends',
+			0xFF => 'one more byte of events follows'
+		);
+
+		return (isset($EventLookup[$index]) ? $EventLookup[$index] : '');
+	}
+
+	function SYTLContentTypeLookup($index) {
+		static $SYTLContentTypeLookup = array(
+			0x00 => 'other',
+			0x01 => 'lyrics',
+			0x02 => 'text transcription',
+			0x03 => 'movement/part name', // (e.g. 'Adagio')
+			0x04 => 'events',             // (e.g. 'Don Quijote enters the stage')
+			0x05 => 'chord',              // (e.g. 'Bb F Fsus')
+			0x06 => 'trivia/\'pop up\' information',
+			0x07 => 'URLs to webpages',
+			0x08 => 'URLs to images'
+		);
+
+		return (isset($SYTLContentTypeLookup[$index]) ? $SYTLContentTypeLookup[$index] : '');
+	}
+
+	function APICPictureTypeLookup($index, $returnarray=false) {
+		static $APICPictureTypeLookup = array(
+			0x00 => 'Other',
+			0x01 => '32x32 pixels \'file icon\' (PNG only)',
+			0x02 => 'Other file icon',
+			0x03 => 'Cover (front)',
+			0x04 => 'Cover (back)',
+			0x05 => 'Leaflet page',
+			0x06 => 'Media (e.g. label side of CD)',
+			0x07 => 'Lead artist/lead performer/soloist',
+			0x08 => 'Artist/performer',
+			0x09 => 'Conductor',
+			0x0A => 'Band/Orchestra',
+			0x0B => 'Composer',
+			0x0C => 'Lyricist/text writer',
+			0x0D => 'Recording Location',
+			0x0E => 'During recording',
+			0x0F => 'During performance',
+			0x10 => 'Movie/video screen capture',
+			0x11 => 'A bright coloured fish',
+			0x12 => 'Illustration',
+			0x13 => 'Band/artist logotype',
+			0x14 => 'Publisher/Studio logotype'
+		);
+		if ($returnarray) {
+			return $APICPictureTypeLookup;
+		}
+		return (isset($APICPictureTypeLookup[$index]) ? $APICPictureTypeLookup[$index] : '');
+	}
+
+	function COMRReceivedAsLookup($index) {
+		static $COMRReceivedAsLookup = array(
+			0x00 => 'Other',
+			0x01 => 'Standard CD album with other songs',
+			0x02 => 'Compressed audio on CD',
+			0x03 => 'File over the Internet',
+			0x04 => 'Stream over the Internet',
+			0x05 => 'As note sheets',
+			0x06 => 'As note sheets in a book with other sheets',
+			0x07 => 'Music on other media',
+			0x08 => 'Non-musical merchandise'
+		);
+
+		return (isset($COMRReceivedAsLookup[$index]) ? $COMRReceivedAsLookup[$index] : '');
+	}
+
+	function RVA2ChannelTypeLookup($index) {
+		static $RVA2ChannelTypeLookup = array(
+			0x00 => 'Other',
+			0x01 => 'Master volume',
+			0x02 => 'Front right',
+			0x03 => 'Front left',
+			0x04 => 'Back right',
+			0x05 => 'Back left',
+			0x06 => 'Front centre',
+			0x07 => 'Back centre',
+			0x08 => 'Subwoofer'
+		);
+
+		return (isset($RVA2ChannelTypeLookup[$index]) ? $RVA2ChannelTypeLookup[$index] : '');
+	}
+
+	function FrameNameLongLookup($framename) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			AENC	Audio encryption
+			APIC	Attached picture
+			ASPI	Audio seek point index
+			BUF	Recommended buffer size
+			CNT	Play counter
+			COM	Comments
+			COMM	Comments
+			COMR	Commercial frame
+			CRA	Audio encryption
+			CRM	Encrypted meta frame
+			ENCR	Encryption method registration
+			EQU	Equalisation
+			EQU2	Equalisation (2)
+			EQUA	Equalisation
+			ETC	Event timing codes
+			ETCO	Event timing codes
+			GEO	General encapsulated object
+			GEOB	General encapsulated object
+			GRID	Group identification registration
+			IPL	Involved people list
+			IPLS	Involved people list
+			LINK	Linked information
+			LNK	Linked information
+			MCDI	Music CD identifier
+			MCI	Music CD Identifier
+			MLL	MPEG location lookup table
+			MLLT	MPEG location lookup table
+			OWNE	Ownership frame
+			PCNT	Play counter
+			PIC	Attached picture
+			POP	Popularimeter
+			POPM	Popularimeter
+			POSS	Position synchronisation frame
+			PRIV	Private frame
+			RBUF	Recommended buffer size
+			REV	Reverb
+			RVA	Relative volume adjustment
+			RVA2	Relative volume adjustment (2)
+			RVAD	Relative volume adjustment
+			RVRB	Reverb
+			SEEK	Seek frame
+			SIGN	Signature frame
+			SLT	Synchronised lyric/text
+			STC	Synced tempo codes
+			SYLT	Synchronised lyric/text
+			SYTC	Synchronised tempo codes
+			TAL	Album/Movie/Show title
+			TALB	Album/Movie/Show title
+			TBP	BPM (Beats Per Minute)
+			TBPM	BPM (beats per minute)
+			TCM	Composer
+			TCMP	Part of a compilation
+			TCO	Content type
+			TCOM	Composer
+			TCON	Content type
+			TCOP	Copyright message
+			TCP	Part of a compilation
+			TCR	Copyright message
+			TDA	Date
+			TDAT	Date
+			TDEN	Encoding time
+			TDLY	Playlist delay
+			TDOR	Original release time
+			TDRC	Recording time
+			TDRL	Release time
+			TDTG	Tagging time
+			TDY	Playlist delay
+			TEN	Encoded by
+			TENC	Encoded by
+			TEXT	Lyricist/Text writer
+			TFLT	File type
+			TFT	File type
+			TIM	Time
+			TIME	Time
+			TIPL	Involved people list
+			TIT1	Content group description
+			TIT2	Title/songname/content description
+			TIT3	Subtitle/Description refinement
+			TKE	Initial key
+			TKEY	Initial key
+			TLA	Language(s)
+			TLAN	Language(s)
+			TLE	Length
+			TLEN	Length
+			TMCL	Musician credits list
+			TMED	Media type
+			TMOO	Mood
+			TMT	Media type
+			TOA	Original artist(s)/performer(s)
+			TOAL	Original album/movie/show title
+			TOF	Original filename
+			TOFN	Original filename
+			TOL	Original Lyricist(s)/text writer(s)
+			TOLY	Original lyricist(s)/text writer(s)
+			TOPE	Original artist(s)/performer(s)
+			TOR	Original release year
+			TORY	Original release year
+			TOT	Original album/Movie/Show title
+			TOWN	File owner/licensee
+			TP1	Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group
+			TP2	Band/Orchestra/Accompaniment
+			TP3	Conductor/Performer refinement
+			TP4	Interpreted, remixed, or otherwise modified by
+			TPA	Part of a set
+			TPB	Publisher
+			TPE1	Lead performer(s)/Soloist(s)
+			TPE2	Band/orchestra/accompaniment
+			TPE3	Conductor/performer refinement
+			TPE4	Interpreted, remixed, or otherwise modified by
+			TPOS	Part of a set
+			TPRO	Produced notice
+			TPUB	Publisher
+			TRC	ISRC (International Standard Recording Code)
+			TRCK	Track number/Position in set
+			TRD	Recording dates
+			TRDA	Recording dates
+			TRK	Track number/Position in set
+			TRSN	Internet radio station name
+			TRSO	Internet radio station owner
+			TS2	Album-Artist sort order
+			TSA	Album sort order
+			TSC	Composer sort order
+			TSI	Size
+			TSIZ	Size
+			TSO2	Album-Artist sort order
+			TSOA	Album sort order
+			TSOC	Composer sort order
+			TSOP	Performer sort order
+			TSOT	Title sort order
+			TSP	Performer sort order
+			TSRC	ISRC (international standard recording code)
+			TSS	Software/hardware and settings used for encoding
+			TSSE	Software/Hardware and settings used for encoding
+			TSST	Set subtitle
+			TST	Title sort order
+			TT1	Content group description
+			TT2	Title/Songname/Content description
+			TT3	Subtitle/Description refinement
+			TXT	Lyricist/text writer
+			TXX	User defined text information frame
+			TXXX	User defined text information frame
+			TYE	Year
+			TYER	Year
+			UFI	Unique file identifier
+			UFID	Unique file identifier
+			ULT	Unsychronised lyric/text transcription
+			USER	Terms of use
+			USLT	Unsynchronised lyric/text transcription
+			WAF	Official audio file webpage
+			WAR	Official artist/performer webpage
+			WAS	Official audio source webpage
+			WCM	Commercial information
+			WCOM	Commercial information
+			WCOP	Copyright/Legal information
+			WCP	Copyright/Legal information
+			WOAF	Official audio file webpage
+			WOAR	Official artist/performer webpage
+			WOAS	Official audio source webpage
+			WORS	Official Internet radio station homepage
+			WPAY	Payment
+			WPB	Publishers official webpage
+			WPUB	Publishers official webpage
+			WXX	User defined URL link frame
+			WXXX	User defined URL link frame
+			TFEA	Featured Artist
+			TSTU	Recording Studio
+			rgad	Replay Gain Adjustment
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_long');
+
+		// Last three:
+		// from Helium2 [www.helium2.com]
+		// from http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
+	}
+
+
+	function FrameNameShortLookup($framename) {
+
+		$begin = __LINE__;
+
+		/** This is not a comment!
+
+			AENC	audio_encryption
+			APIC	attached_picture
+			ASPI	audio_seek_point_index
+			BUF	recommended_buffer_size
+			CNT	play_counter
+			COM	comments
+			COMM	comments
+			COMR	commercial_frame
+			CRA	audio_encryption
+			CRM	encrypted_meta_frame
+			ENCR	encryption_method_registration
+			EQU	equalisation
+			EQU2	equalisation
+			EQUA	equalisation
+			ETC	event_timing_codes
+			ETCO	event_timing_codes
+			GEO	general_encapsulated_object
+			GEOB	general_encapsulated_object
+			GRID	group_identification_registration
+			IPL	involved_people_list
+			IPLS	involved_people_list
+			LINK	linked_information
+			LNK	linked_information
+			MCDI	music_cd_identifier
+			MCI	music_cd_identifier
+			MLL	mpeg_location_lookup_table
+			MLLT	mpeg_location_lookup_table
+			OWNE	ownership_frame
+			PCNT	play_counter
+			PIC	attached_picture
+			POP	popularimeter
+			POPM	popularimeter
+			POSS	position_synchronisation_frame
+			PRIV	private_frame
+			RBUF	recommended_buffer_size
+			REV	reverb
+			RVA	relative_volume_adjustment
+			RVA2	relative_volume_adjustment
+			RVAD	relative_volume_adjustment
+			RVRB	reverb
+			SEEK	seek_frame
+			SIGN	signature_frame
+			SLT	synchronised_lyric
+			STC	synced_tempo_codes
+			SYLT	synchronised_lyric
+			SYTC	synchronised_tempo_codes
+			TAL	album
+			TALB	album
+			TBP	bpm
+			TBPM	bpm
+			TCM	composer
+			TCMP	part_of_a_compilation
+			TCO	genre
+			TCOM	composer
+			TCON	genre
+			TCOP	copyright_message
+			TCP	part_of_a_compilation
+			TCR	copyright_message
+			TDA	date
+			TDAT	date
+			TDEN	encoding_time
+			TDLY	playlist_delay
+			TDOR	original_release_time
+			TDRC	recording_time
+			TDRL	release_time
+			TDTG	tagging_time
+			TDY	playlist_delay
+			TEN	encoded_by
+			TENC	encoded_by
+			TEXT	lyricist
+			TFLT	file_type
+			TFT	file_type
+			TIM	time
+			TIME	time
+			TIPL	involved_people_list
+			TIT1	content_group_description
+			TIT2	title
+			TIT3	subtitle
+			TKE	initial_key
+			TKEY	initial_key
+			TLA	language
+			TLAN	language
+			TLE	length
+			TLEN	length
+			TMCL	musician_credits_list
+			TMED	media_type
+			TMOO	mood
+			TMT	media_type
+			TOA	original_artist
+			TOAL	original_album
+			TOF	original_filename
+			TOFN	original_filename
+			TOL	original_lyricist
+			TOLY	original_lyricist
+			TOPE	original_artist
+			TOR	original_year
+			TORY	original_year
+			TOT	original_album
+			TOWN	file_owner
+			TP1	artist
+			TP2	band
+			TP3	conductor
+			TP4	remixer
+			TPA	part_of_a_set
+			TPB	publisher
+			TPE1	artist
+			TPE2	band
+			TPE3	conductor
+			TPE4	remixer
+			TPOS	part_of_a_set
+			TPRO	produced_notice
+			TPUB	publisher
+			TRC	isrc
+			TRCK	track_number
+			TRD	recording_dates
+			TRDA	recording_dates
+			TRK	track_number
+			TRSN	internet_radio_station_name
+			TRSO	internet_radio_station_owner
+			TS2	album_artist_sort_order
+			TSA	album_sort_order
+			TSC	composer_sort_order
+			TSI	size
+			TSIZ	size
+			TSO2	album_artist_sort_order
+			TSOA	album_sort_order
+			TSOC	composer_sort_order
+			TSOP	performer_sort_order
+			TSOT	title_sort_order
+			TSP	performer_sort_order
+			TSRC	isrc
+			TSS	encoder_settings
+			TSSE	encoder_settings
+			TSST	set_subtitle
+			TST	title_sort_order
+			TT1	description
+			TT2	title
+			TT3	subtitle
+			TXT	lyricist
+			TXX	text
+			TXXX	text
+			TYE	year
+			TYER	year
+			UFI	unique_file_identifier
+			UFID	unique_file_identifier
+			ULT	unsychronised_lyric
+			USER	terms_of_use
+			USLT	unsynchronised_lyric
+			WAF	url_file
+			WAR	url_artist
+			WAS	url_source
+			WCM	commercial_information
+			WCOM	commercial_information
+			WCOP	copyright
+			WCP	copyright
+			WOAF	url_file
+			WOAR	url_artist
+			WOAS	url_source
+			WORS	url_station
+			WPAY	url_payment
+			WPB	url_publisher
+			WPUB	url_publisher
+			WXX	url_user
+			WXXX	url_user
+			TFEA	featured_artist
+			TSTU	recording_studio
+			rgad	replay_gain_adjustment
+
+		*/
+
+		return getid3_lib::EmbeddedLookup($framename, $begin, __LINE__, __FILE__, 'id3v2-framename_short');
+	}
+
+	function TextEncodingTerminatorLookup($encoding) {
+		// http://www.id3.org/id3v2.4.0-structure.txt
+		// Frames that allow different types of text encoding contains a text encoding description byte. Possible encodings:
+		// $00  ISO-8859-1. Terminated with $00.
+		// $01  UTF-16 encoded Unicode with BOM. All strings in the same frame SHALL have the same byteorder. Terminated with $00 00.
+		// $02  UTF-16BE encoded Unicode without BOM. Terminated with $00 00.
+		// $03  UTF-8 encoded Unicode. Terminated with $00.
+
+		static $TextEncodingTerminatorLookup = array(0=>"\x00", 1=>"\x00\x00", 2=>"\x00\x00", 3=>"\x00", 255=>"\x00\x00");
+
+		return @$TextEncodingTerminatorLookup[$encoding];
+	}
+
+	function TextEncodingNameLookup($encoding) {
+		// http://www.id3.org/id3v2.4.0-structure.txt
+		static $TextEncodingNameLookup = array(0=>'ISO-8859-1', 1=>'UTF-16', 2=>'UTF-16BE', 3=>'UTF-8', 255=>'UTF-16BE');
+		return (isset($TextEncodingNameLookup[$encoding]) ? $TextEncodingNameLookup[$encoding] : 'ISO-8859-1');
+	}
+
+	function IsValidID3v2FrameName($framename, $id3v2majorversion) {
+		switch ($id3v2majorversion) {
+			case 2:
+				return ereg('[A-Z][A-Z0-9]{2}', $framename);
+				break;
+
+			case 3:
+			case 4:
+				return ereg('[A-Z][A-Z0-9]{3}', $framename);
+				break;
+		}
+		return false;
+	}
+
+	function IsANumber($numberstring, $allowdecimal=false, $allownegative=false) {
+		for ($i = 0; $i < strlen($numberstring); $i++) {
+			if ((chr($numberstring{$i}) < chr('0')) || (chr($numberstring{$i}) > chr('9'))) {
+				if (($numberstring{$i} == '.') && $allowdecimal) {
+					// allowed
+				} elseif (($numberstring{$i} == '-') && $allownegative && ($i == 0)) {
+					// allowed
+				} else {
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	function IsValidDateStampString($datestamp) {
+		if (strlen($datestamp) != 8) {
+			return false;
+		}
+		if (!$this->IsANumber($datestamp, false)) {
+			return false;
+		}
+		$year  = substr($datestamp, 0, 4);
+		$month = substr($datestamp, 4, 2);
+		$day   = substr($datestamp, 6, 2);
+		if (($year == 0) || ($month == 0) || ($day == 0)) {
+			return false;
+		}
+		if ($month > 12) {
+			return false;
+		}
+		if ($day > 31) {
+			return false;
+		}
+		if (($day > 30) && (($month == 4) || ($month == 6) || ($month == 9) || ($month == 11))) {
+			return false;
+		}
+		if (($day > 29) && ($month == 2)) {
+			return false;
+		}
+		return true;
+	}
+
+	function ID3v2HeaderLength($majorversion) {
+		return (($majorversion == 2) ? 6 : 10);
+	}
+
+}
+
+?>
diff --git a/apps/media/getID3/getid3/module.tag.lyrics3.php b/apps/media/getID3/getid3/module.tag.lyrics3.php
new file mode 100644
index 0000000000000000000000000000000000000000..67dba43eb5bb2a58451e4e8c6aacffa397169218
--- /dev/null
+++ b/apps/media/getID3/getid3/module.tag.lyrics3.php
@@ -0,0 +1,282 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+///                                                            //
+// module.tag.lyrics3.php                                      //
+// module for analyzing Lyrics3 tags                           //
+// dependencies: module.tag.apetag.php (optional)              //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_lyrics3
+{
+
+	function getid3_lyrics3(&$fd, &$ThisFileInfo) {
+		// http://www.volweb.cz/str/tags.htm
+
+		if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
+			$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
+			return false;
+		}
+
+		fseek($fd, (0 - 128 - 9 - 6), SEEK_END);          // end - ID3v1 - LYRICSEND - [Lyrics3size]
+		$lyrics3_id3v1 = fread($fd, 128 + 9 + 6);
+		$lyrics3lsz    = substr($lyrics3_id3v1,  0,   6); // Lyrics3size
+		$lyrics3end    = substr($lyrics3_id3v1,  6,   9); // LYRICSEND or LYRICS200
+		$id3v1tag      = substr($lyrics3_id3v1, 15, 128); // ID3v1
+
+		if ($lyrics3end == 'LYRICSEND') {
+			// Lyrics3v1, ID3v1, no APE
+
+			$lyrics3size    = 5100;
+			$lyrics3offset  = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
+			$lyrics3version = 1;
+
+		} elseif ($lyrics3end == 'LYRICS200') {
+			// Lyrics3v2, ID3v1, no APE
+
+			// LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
+			$lyrics3size    = $lyrics3lsz + 6 + strlen('LYRICS200');
+			$lyrics3offset  = $ThisFileInfo['filesize'] - 128 - $lyrics3size;
+			$lyrics3version = 2;
+
+		} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICSEND')) {
+			// Lyrics3v1, no ID3v1, no APE
+
+			$lyrics3size    = 5100;
+			$lyrics3offset  = $ThisFileInfo['filesize'] - $lyrics3size;
+			$lyrics3version = 1;
+			$lyrics3offset  = $ThisFileInfo['filesize'] - $lyrics3size;
+
+		} elseif (substr(strrev($lyrics3_id3v1), 0, 9) == strrev('LYRICS200')) {
+
+			// Lyrics3v2, no ID3v1, no APE
+
+			$lyrics3size    = strrev(substr(strrev($lyrics3_id3v1), 9, 6)) + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
+			$lyrics3offset  = $ThisFileInfo['filesize'] - $lyrics3size;
+			$lyrics3version = 2;
+
+		} else {
+
+			if (isset($ThisFileInfo['ape']['tag_offset_start']) && ($ThisFileInfo['ape']['tag_offset_start'] > 15)) {
+
+				fseek($fd, $ThisFileInfo['ape']['tag_offset_start'] - 15, SEEK_SET);
+				$lyrics3lsz = fread($fd, 6);
+				$lyrics3end = fread($fd, 9);
+
+				if ($lyrics3end == 'LYRICSEND') {
+					// Lyrics3v1, APE, maybe ID3v1
+
+					$lyrics3size    = 5100;
+					$lyrics3offset  = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
+					$ThisFileInfo['avdataend'] = $lyrics3offset;
+					$lyrics3version = 1;
+					$ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
+
+				} elseif ($lyrics3end == 'LYRICS200') {
+					// Lyrics3v2, APE, maybe ID3v1
+
+					$lyrics3size    = $lyrics3lsz + 6 + strlen('LYRICS200'); // LSZ = lyrics + 'LYRICSBEGIN'; add 6-byte size field; add 'LYRICS200'
+					$lyrics3offset  = $ThisFileInfo['ape']['tag_offset_start'] - $lyrics3size;
+					$lyrics3version = 2;
+					$ThisFileInfo['warning'][] = 'APE tag located after Lyrics3, will probably break Lyrics3 compatability';
+
+				}
+
+			}
+
+		}
+
+		if (isset($lyrics3offset)) {
+			$ThisFileInfo['avdataend'] = $lyrics3offset;
+			$this->getLyrics3Data($ThisFileInfo, $fd, $lyrics3offset, $lyrics3version, $lyrics3size);
+
+			if (!isset($ThisFileInfo['ape'])) {
+				$GETID3_ERRORARRAY = &$ThisFileInfo['warning'];
+				if (getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, false)) {
+					$tag = new getid3_apetag($fd, $ThisFileInfo, $ThisFileInfo['lyrics3']['tag_offset_start']);
+					unset($tag);
+				}
+			}
+
+		}
+
+		return true;
+	}
+
+	function getLyrics3Data(&$ThisFileInfo, &$fd, $endoffset, $version, $length) {
+		// http://www.volweb.cz/str/tags.htm
+
+		if ($endoffset >= pow(2, 31)) {
+			$ThisFileInfo['warning'][] = 'Unable to check for Lyrics3 because file is larger than 2GB';
+			return false;
+		}
+
+		fseek($fd, $endoffset, SEEK_SET);
+		if ($length <= 0) {
+			return false;
+		}
+		$rawdata = fread($fd, $length);
+
+		if (substr($rawdata, 0, 11) != 'LYRICSBEGIN') {
+			if (strpos($rawdata, 'LYRICSBEGIN') !== false) {
+
+				$ThisFileInfo['warning'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but actually found at '.($endoffset + strpos($rawdata, 'LYRICSBEGIN')).' - this is invalid for Lyrics3 v'.$version;
+				$ThisFileInfo['avdataend'] = $endoffset + strpos($rawdata, 'LYRICSBEGIN');
+				$ParsedLyrics3['tag_offset_start'] = $ThisFileInfo['avdataend'];
+				$rawdata = substr($rawdata, strpos($rawdata, 'LYRICSBEGIN'));
+				$length = strlen($rawdata);
+
+			} else {
+
+				$ThisFileInfo['error'][] = '"LYRICSBEGIN" expected at '.$endoffset.' but found "'.substr($rawdata, 0, 11).'" instead';
+				return false;
+
+			}
+
+		}
+
+		$ParsedLyrics3['raw']['lyrics3version'] = $version;
+		$ParsedLyrics3['raw']['lyrics3tagsize'] = $length;
+		$ParsedLyrics3['tag_offset_start']      = $endoffset;
+		$ParsedLyrics3['tag_offset_end']        = $endoffset + $length;
+
+		switch ($version) {
+
+			case 1:
+				if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICSEND') {
+					$ParsedLyrics3['raw']['LYR'] = trim(substr($rawdata, 11, strlen($rawdata) - 11 - 9));
+					$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
+				} else {
+					$ThisFileInfo['error'][] = '"LYRICSEND" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
+					return false;
+				}
+				break;
+
+			case 2:
+				if (substr($rawdata, strlen($rawdata) - 9, 9) == 'LYRICS200') {
+					$ParsedLyrics3['raw']['unparsed'] = substr($rawdata, 11, strlen($rawdata) - 11 - 9 - 6); // LYRICSBEGIN + LYRICS200 + LSZ
+					$rawdata = $ParsedLyrics3['raw']['unparsed'];
+					while (strlen($rawdata) > 0) {
+						$fieldname = substr($rawdata, 0, 3);
+						$fieldsize = (int) substr($rawdata, 3, 5);
+						$ParsedLyrics3['raw'][$fieldname] = substr($rawdata, 8, $fieldsize);
+						$rawdata = substr($rawdata, 3 + 5 + $fieldsize);
+					}
+
+					if (isset($ParsedLyrics3['raw']['IND'])) {
+						$i = 0;
+						$flagnames = array('lyrics', 'timestamps', 'inhibitrandom');
+						foreach ($flagnames as $flagname) {
+							if (strlen($ParsedLyrics3['raw']['IND']) > $i++) {
+								$ParsedLyrics3['flags'][$flagname] = $this->IntString2Bool(substr($ParsedLyrics3['raw']['IND'], $i, 1 - 1));
+							}
+						}
+					}
+
+					$fieldnametranslation = array('ETT'=>'title', 'EAR'=>'artist', 'EAL'=>'album', 'INF'=>'comment', 'AUT'=>'author');
+					foreach ($fieldnametranslation as $key => $value) {
+						if (isset($ParsedLyrics3['raw'][$key])) {
+							$ParsedLyrics3['comments'][$value][] = trim($ParsedLyrics3['raw'][$key]);
+						}
+					}
+
+					if (isset($ParsedLyrics3['raw']['IMG'])) {
+						$imagestrings = explode("\r\n", $ParsedLyrics3['raw']['IMG']);
+						foreach ($imagestrings as $key => $imagestring) {
+							if (strpos($imagestring, '||') !== false) {
+								$imagearray = explode('||', $imagestring);
+								$ParsedLyrics3['images'][$key]['filename']     = @$imagearray[0];
+								$ParsedLyrics3['images'][$key]['description']  = @$imagearray[1];
+								$ParsedLyrics3['images'][$key]['timestamp']    = $this->Lyrics3Timestamp2Seconds(@$imagearray[2]);
+							}
+						}
+					}
+					if (isset($ParsedLyrics3['raw']['LYR'])) {
+						$this->Lyrics3LyricsTimestampParse($ParsedLyrics3);
+					}
+				} else {
+					$ThisFileInfo['error'][] = '"LYRICS200" expected at '.(ftell($fd) - 11 + $length - 9).' but found "'.substr($rawdata, strlen($rawdata) - 9, 9).'" instead';
+					return false;
+				}
+				break;
+
+			default:
+				$ThisFileInfo['error'][] = 'Cannot process Lyrics3 version '.$version.' (only v1 and v2)';
+				return false;
+				break;
+		}
+
+
+		if (isset($ThisFileInfo['id3v1']['tag_offset_start']) && ($ThisFileInfo['id3v1']['tag_offset_start'] < $ParsedLyrics3['tag_offset_end'])) {
+			$ThisFileInfo['warning'][] = 'ID3v1 tag information ignored since it appears to be a false synch in Lyrics3 tag data';
+			unset($ThisFileInfo['id3v1']);
+			foreach ($ThisFileInfo['warning'] as $key => $value) {
+				if ($value == 'Some ID3v1 fields do not use NULL characters for padding') {
+					unset($ThisFileInfo['warning'][$key]);
+					sort($ThisFileInfo['warning']);
+					break;
+				}
+			}
+		}
+
+		$ThisFileInfo['lyrics3'] = $ParsedLyrics3;
+
+		return true;
+	}
+
+	function Lyrics3Timestamp2Seconds($rawtimestamp) {
+		if (ereg('^\\[([0-9]{2}):([0-9]{2})\\]$', $rawtimestamp, $regs)) {
+			return (int) (($regs[1] * 60) + $regs[2]);
+		}
+		return false;
+	}
+
+	function Lyrics3LyricsTimestampParse(&$Lyrics3data) {
+		$lyricsarray = explode("\r\n", $Lyrics3data['raw']['LYR']);
+		foreach ($lyricsarray as $key => $lyricline) {
+			$regs = array();
+			unset($thislinetimestamps);
+			while (ereg('^(\\[[0-9]{2}:[0-9]{2}\\])', $lyricline, $regs)) {
+				$thislinetimestamps[] = $this->Lyrics3Timestamp2Seconds($regs[0]);
+				$lyricline = str_replace($regs[0], '', $lyricline);
+			}
+			$notimestamplyricsarray[$key] = $lyricline;
+			if (isset($thislinetimestamps) && is_array($thislinetimestamps)) {
+				sort($thislinetimestamps);
+				foreach ($thislinetimestamps as $timestampkey => $timestamp) {
+					if (isset($Lyrics3data['synchedlyrics'][$timestamp])) {
+						// timestamps only have a 1-second resolution, it's possible that multiple lines
+						// could have the same timestamp, if so, append
+						$Lyrics3data['synchedlyrics'][$timestamp] .= "\r\n".$lyricline;
+					} else {
+						$Lyrics3data['synchedlyrics'][$timestamp] = $lyricline;
+					}
+				}
+			}
+		}
+		$Lyrics3data['unsynchedlyrics'] = implode("\r\n", $notimestamplyricsarray);
+		if (isset($Lyrics3data['synchedlyrics']) && is_array($Lyrics3data['synchedlyrics'])) {
+			ksort($Lyrics3data['synchedlyrics']);
+		}
+		return true;
+	}
+
+	function IntString2Bool($char) {
+		if ($char == '1') {
+			return true;
+		} elseif ($char == '0') {
+			return false;
+		}
+		return null;
+	}
+}
+
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.apetag.php b/apps/media/getID3/getid3/write.apetag.php
new file mode 100644
index 0000000000000000000000000000000000000000..189160aff842779449250de0247072a5c44dba70
--- /dev/null
+++ b/apps/media/getID3/getid3/write.apetag.php
@@ -0,0 +1,228 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.apetag.php                                            //
+// module for writing APE tags                                 //
+// dependencies: module.tag.apetag.php                         //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.apetag.php', __FILE__, true);
+
+class getid3_write_apetag
+{
+
+	var $filename;
+	var $tag_data;
+	var $always_preserve_replaygain = true;  // ReplayGain / MP3gain tags will be copied from old tag even if not passed in data
+	var $warnings = array();                 // any non-critical errors will be stored here
+	var $errors   = array();                 // any critical errors will be stored here
+
+	function getid3_write_apetag() {
+		return true;
+	}
+
+	function WriteAPEtag() {
+		// NOTE: All data passed to this function must be UTF-8 format
+
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+
+		if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
+			if ($ThisFileInfo['ape']['tag_offset_start'] >= $ThisFileInfo['lyrics3']['tag_offset_end']) {
+				// Current APE tag between Lyrics3 and ID3v1/EOF
+				// This break Lyrics3 functionality
+				if (!$this->DeleteAPEtag()) {
+					return false;
+				}
+				$ThisFileInfo = $getID3->analyze($this->filename);
+			}
+		}
+
+		if ($this->always_preserve_replaygain) {
+			$ReplayGainTagsToPreserve = array('mp3gain_minmax', 'mp3gain_album_minmax', 'mp3gain_undo', 'replaygain_track_peak', 'replaygain_track_gain', 'replaygain_album_peak', 'replaygain_album_gain');
+			foreach ($ReplayGainTagsToPreserve as $rg_key) {
+				if (isset($ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0]) && !isset($this->tag_data[strtoupper($rg_key)][0])) {
+					$this->tag_data[strtoupper($rg_key)][0] = $ThisFileInfo['ape']['items'][strtolower($rg_key)]['data'][0];
+				}
+			}
+		}
+
+		if ($APEtag = $this->GenerateAPEtag()) {
+			if ($fp = @fopen($this->filename, 'a+b')) {
+				$oldignoreuserabort = ignore_user_abort(true);
+				flock($fp, LOCK_EX);
+
+				$PostAPEdataOffset = $ThisFileInfo['avdataend'];
+				if (isset($ThisFileInfo['ape']['tag_offset_end'])) {
+					$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['ape']['tag_offset_end']);
+				}
+				if (isset($ThisFileInfo['lyrics3']['tag_offset_start'])) {
+					$PostAPEdataOffset = max($PostAPEdataOffset, $ThisFileInfo['lyrics3']['tag_offset_start']);
+				}
+				fseek($fp, $PostAPEdataOffset, SEEK_SET);
+				$PostAPEdata = '';
+				if ($ThisFileInfo['filesize'] > $PostAPEdataOffset) {
+					$PostAPEdata = fread($fp, $ThisFileInfo['filesize'] - $PostAPEdataOffset);
+				}
+
+				fseek($fp, $PostAPEdataOffset, SEEK_SET);
+				if (isset($ThisFileInfo['ape']['tag_offset_start'])) {
+					fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
+				}
+				ftruncate($fp, ftell($fp));
+				fwrite($fp, $APEtag, strlen($APEtag));
+				if (!empty($PostAPEdata)) {
+					fwrite($fp, $PostAPEdata, strlen($PostAPEdata));
+				}
+				flock($fp, LOCK_UN);
+				fclose($fp);
+				ignore_user_abort($oldignoreuserabort);
+				return true;
+
+			}
+			return false;
+		}
+		return false;
+	}
+
+	function DeleteAPEtag() {
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+		if (isset($ThisFileInfo['ape']['tag_offset_start']) && isset($ThisFileInfo['ape']['tag_offset_end'])) {
+			if ($fp = @fopen($this->filename, 'a+b')) {
+
+				flock($fp, LOCK_EX);
+				$oldignoreuserabort = ignore_user_abort(true);
+
+				fseek($fp, $ThisFileInfo['ape']['tag_offset_end'], SEEK_SET);
+				$DataAfterAPE = '';
+				if ($ThisFileInfo['filesize'] > $ThisFileInfo['ape']['tag_offset_end']) {
+					$DataAfterAPE = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['ape']['tag_offset_end']);
+				}
+
+				ftruncate($fp, $ThisFileInfo['ape']['tag_offset_start']);
+				fseek($fp, $ThisFileInfo['ape']['tag_offset_start'], SEEK_SET);
+
+				if (!empty($DataAfterAPE)) {
+					fwrite($fp, $DataAfterAPE, strlen($DataAfterAPE));
+				}
+
+				flock($fp, LOCK_UN);
+				fclose($fp);
+				ignore_user_abort($oldignoreuserabort);
+
+				return true;
+
+			}
+			return false;
+		}
+		return true;
+	}
+
+
+	function GenerateAPEtag() {
+		// NOTE: All data passed to this function must be UTF-8 format
+
+		$items = array();
+		if (!is_array($this->tag_data)) {
+			return false;
+		}
+		foreach ($this->tag_data as $key => $arrayofvalues) {
+			if (!is_array($arrayofvalues)) {
+				return false;
+			}
+
+			$valuestring = '';
+			foreach ($arrayofvalues as $value) {
+				$valuestring .= str_replace("\x00", '', $value)."\x00";
+			}
+			$valuestring = rtrim($valuestring, "\x00");
+
+			// Length of the assigned value in bytes
+			$tagitem  = getid3_lib::LittleEndian2String(strlen($valuestring), 4);
+
+			//$tagitem .= $this->GenerateAPEtagFlags(true, true, false, 0, false);
+			$tagitem .= "\x00\x00\x00\x00";
+
+			$tagitem .= $this->CleanAPEtagItemKey($key)."\x00";
+			$tagitem .= $valuestring;
+
+			$items[] = $tagitem;
+
+		}
+
+		return $this->GenerateAPEtagHeaderFooter($items, true).implode('', $items).$this->GenerateAPEtagHeaderFooter($items, false);
+	}
+
+	function GenerateAPEtagHeaderFooter(&$items, $isheader=false) {
+		$tagdatalength = 0;
+		foreach ($items as $itemdata) {
+			$tagdatalength += strlen($itemdata);
+		}
+
+		$APEheader  = 'APETAGEX';
+		$APEheader .= getid3_lib::LittleEndian2String(2000, 4);
+		$APEheader .= getid3_lib::LittleEndian2String(32 + $tagdatalength, 4);
+		$APEheader .= getid3_lib::LittleEndian2String(count($items), 4);
+		$APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
+		$APEheader .= str_repeat("\x00", 8);
+
+		return $APEheader;
+	}
+
+	function GenerateAPEtagFlags($header=true, $footer=true, $isheader=false, $encodingid=0, $readonly=false) {
+		$APEtagFlags = array_fill(0, 4, 0);
+		if ($header) {
+			$APEtagFlags[0] |= 0x80; // Tag contains a header
+		}
+		if (!$footer) {
+			$APEtagFlags[0] |= 0x40; // Tag contains no footer
+		}
+		if ($isheader) {
+			$APEtagFlags[0] |= 0x20; // This is the header, not the footer
+		}
+
+		// 0: Item contains text information coded in UTF-8
+		// 1: Item contains binary information °)
+		// 2: Item is a locator of external stored information °°)
+		// 3: reserved
+		$APEtagFlags[3] |= ($encodingid << 1);
+
+		if ($readonly) {
+			$APEtagFlags[3] |= 0x01; // Tag or Item is Read Only
+		}
+
+		return chr($APEtagFlags[3]).chr($APEtagFlags[2]).chr($APEtagFlags[1]).chr($APEtagFlags[0]);
+	}
+
+	function CleanAPEtagItemKey($itemkey) {
+		$itemkey = eregi_replace("[^\x20-\x7E]", '', $itemkey);
+
+		// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
+		switch (strtoupper($itemkey)) {
+			case 'EAN/UPC':
+			case 'ISBN':
+			case 'LC':
+			case 'ISRC':
+				$itemkey = strtoupper($itemkey);
+				break;
+
+			default:
+				$itemkey = ucwords($itemkey);
+				break;
+		}
+		return $itemkey;
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.id3v1.php b/apps/media/getID3/getid3/write.id3v1.php
new file mode 100644
index 0000000000000000000000000000000000000000..3c2b7a402caa857ed4db157b99a6414756306dd6
--- /dev/null
+++ b/apps/media/getID3/getid3/write.id3v1.php
@@ -0,0 +1,119 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.id3v1.php                                             //
+// module for writing ID3v1 tags                               //
+// dependencies: module.tag.id3v1.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v1.php', __FILE__, true);
+
+class getid3_write_id3v1
+{
+	var $filename;
+	var $tag_data;
+	var $warnings = array(); // any non-critical errors will be stored here
+	var $errors   = array(); // any critical errors will be stored here
+
+	function getid3_write_id3v1() {
+		return true;
+	}
+
+	function WriteID3v1() {
+		if ((filesize($this->filename) >= (pow(2, 31) - 128)) || (filesize($this->filename) < 0)) {
+			$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
+			return false;
+		}
+
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				fseek($fp_source, -128, SEEK_END);
+				if (fread($fp_source, 3) == 'TAG') {
+					fseek($fp_source, -128, SEEK_END); // overwrite existing ID3v1 tag
+				} else {
+					fseek($fp_source, 0, SEEK_END);    // append new ID3v1 tag
+				}
+				$this->tag_data['track'] = (isset($this->tag_data['track']) ? $this->tag_data['track'] : (isset($this->tag_data['track_number']) ? $this->tag_data['track_number'] : (isset($this->tag_data['tracknumber']) ? $this->tag_data['tracknumber'] : '')));
+
+				$new_id3v1_tag_data = getid3_id3v1::GenerateID3v1Tag(
+														@$this->tag_data['title'],
+														@$this->tag_data['artist'],
+														@$this->tag_data['album'],
+														@$this->tag_data['year'],
+														@$this->tag_data['genreid'],
+														@$this->tag_data['comment'],
+														@$this->tag_data['track']);
+				fwrite($fp_source, $new_id3v1_tag_data, 128);
+				fclose($fp_source);
+				return true;
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+				return false;
+			}
+		}
+		$this->errors[] = 'File is not writeable: '.$this->filename;
+		return false;
+	}
+
+	function FixID3v1Padding() {
+		// ID3v1 data is supposed to be padded with NULL characters, but some taggers incorrectly use spaces
+		// This function rewrites the ID3v1 tag with correct padding
+
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+		if ($ThisFileInfo['filesize'] >= (pow(2, 31) - 128)) {
+			// cannot write tags on files > 2GB
+			return false;
+		}
+		if (isset($ThisFileInfo['tags']['id3v1'])) {
+			foreach ($ThisFileInfo['tags']['id3v1'] as $key => $value) {
+				$id3v1data[$key] = implode(',', $value);
+			}
+			$this->tag_data = $id3v1data;
+			return $this->WriteID3v1();
+		}
+		return false;
+	}
+
+	function RemoveID3v1() {
+		if ($ThisFileInfo['filesize'] >= pow(2, 31)) {
+			$this->errors[] = 'Unable to write ID3v1 because file is larger than 2GB';
+			return false;
+		}
+
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				fseek($fp_source, -128, SEEK_END);
+				if (fread($fp_source, 3) == 'TAG') {
+					ftruncate($fp_source, filesize($this->filename) - 128);
+				} else {
+					// no ID3v1 tag to begin with - do nothing
+				}
+				fclose($fp_source);
+				return true;
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+			}
+		} else {
+			$this->errors[] = $this->filename.' is not writeable';
+		}
+		return false;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.id3v2.php b/apps/media/getID3/getid3/write.id3v2.php
new file mode 100644
index 0000000000000000000000000000000000000000..9447486e8452465596da08e601fc0a802a754d3f
--- /dev/null
+++ b/apps/media/getID3/getid3/write.id3v2.php
@@ -0,0 +1,2054 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+///                                                            //
+// write.id3v2.php                                             //
+// module for writing ID3v2 tags                               //
+// dependencies: module.tag.id3v2.php                          //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true);
+
+class getid3_write_id3v2
+{
+	var $filename;
+	var $tag_data;
+	var $paddedlength                = 4096;     // minimum length of ID3v2 tag in bytes
+	var $majorversion                = 3;        // ID3v2 major version (2, 3 (recommended), 4)
+	var $minorversion                = 0;        // ID3v2 minor version - always 0
+	var $merge_existing_data         = false;    // if true, merge new data with existing tags; if false, delete old tag data and only write new tags
+	var $id3v2_default_encodingid    = 0;        // default text encoding (ISO-8859-1) if not explicitly passed
+	var $id3v2_use_unsynchronisation = false;    // the specs say it should be TRUE, but most other ID3v2-aware programs are broken if unsynchronization is used, so by default don't use it.
+	var $warnings                    = array();  // any non-critical errors will be stored here
+	var $errors                      = array();  // any critical errors will be stored here
+
+	function getid3_write_id3v2() {
+		return true;
+	}
+
+	function WriteID3v2() {
+		// File MUST be writeable - CHMOD(646) at least. It's best if the
+		// directory is also writeable, because that method is both faster and less susceptible to errors.
+
+		if (is_writeable($this->filename) || (!file_exists($this->filename) && is_writeable(dirname($this->filename)))) {
+			// Initialize getID3 engine
+			$getID3 = new getID3;
+			$OldThisFileInfo = $getID3->analyze($this->filename);
+			if ($OldThisFileInfo['filesize'] >= pow(2, 31)) {
+				$this->errors[] = 'Unable to write ID3v2 because file is larger than 2GB';
+				fclose($fp_source);
+				return false;
+			}
+			if ($this->merge_existing_data) {
+				// merge with existing data
+				if (!empty($OldThisFileInfo['id3v2'])) {
+					$this->tag_data = $this->array_join_merge($OldThisFileInfo['id3v2'], $this->tag_data);
+				}
+			}
+			$this->paddedlength = max(@$OldThisFileInfo['id3v2']['headerlength'], $this->paddedlength);
+
+			if ($NewID3v2Tag = $this->GenerateID3v2Tag()) {
+
+				if (file_exists($this->filename) && is_writeable($this->filename) && isset($OldThisFileInfo['id3v2']['headerlength']) && ($OldThisFileInfo['id3v2']['headerlength'] == strlen($NewID3v2Tag))) {
+
+					// best and fastest method - insert-overwrite existing tag (padded to length of old tag if neccesary)
+					if (file_exists($this->filename)) {
+
+						ob_start();
+						if ($fp = fopen($this->filename, 'r+b')) {
+							rewind($fp);
+							fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
+							fclose($fp);
+						} else {
+							$this->errors[] = 'Could not open '.$this->filename.' mode "r+b" - '.strip_tags(ob_get_contents());
+						}
+						ob_end_clean();
+
+					} else {
+
+						ob_start();
+						if ($fp = fopen($this->filename, 'wb')) {
+							rewind($fp);
+							fwrite($fp, $NewID3v2Tag, strlen($NewID3v2Tag));
+							fclose($fp);
+						} else {
+							$this->errors[] = 'Could not open '.$this->filename.' mode "wb" - '.strip_tags(ob_get_contents());
+						}
+						ob_end_clean();
+
+					}
+
+				} else {
+
+					if ($tempfilename = tempnam('*', 'getID3')) {
+						ob_start();
+						if ($fp_source = fopen($this->filename, 'rb')) {
+							if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+								fwrite($fp_temp, $NewID3v2Tag, strlen($NewID3v2Tag));
+
+								rewind($fp_source);
+								if (!empty($OldThisFileInfo['avdataoffset'])) {
+									fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
+								}
+
+								while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+									fwrite($fp_temp, $buffer, strlen($buffer));
+								}
+
+								fclose($fp_temp);
+								fclose($fp_source);
+								copy($tempfilename, $this->filename);
+								unlink($tempfilename);
+								ob_end_clean();
+								return true;
+
+							} else {
+
+								$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+							}
+							fclose($fp_source);
+
+						} else {
+
+							$this->errors[] = 'Could not open '.$this->filename.' mode "rb" - '.strip_tags(ob_get_contents());
+
+						}
+						ob_end_clean();
+					}
+					return false;
+
+				}
+
+			} else {
+
+				$this->errors[] = '$this->GenerateID3v2Tag() failed';
+
+			}
+
+			if (!empty($this->errors)) {
+				return false;
+			}
+			return true;
+		} else {
+			$this->errors[] = '!is_writeable('.$this->filename.')';
+		}
+		return false;
+	}
+
+	function RemoveID3v2() {
+		// File MUST be writeable - CHMOD(646) at least. It's best if the
+		// directory is also writeable, because that method is both faster and less susceptible to errors.
+		if (is_writeable(dirname($this->filename))) {
+
+			// preferred method - only one copying operation, minimal chance of corrupting
+			// original file if script is interrupted, but required directory to be writeable
+			if ($fp_source = @fopen($this->filename, 'rb')) {
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				if ($OldThisFileInfo['filesize'] >= pow(2, 31)) {
+					$this->errors[] = 'Unable to remove ID3v2 because file is larger than 2GB';
+					fclose($fp_source);
+					return false;
+				}
+				rewind($fp_source);
+				if ($OldThisFileInfo['avdataoffset'] !== false) {
+					fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
+				}
+				if ($fp_temp = @fopen($this->filename.'getid3tmp', 'w+b')) {
+					while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+						fwrite($fp_temp, $buffer, strlen($buffer));
+					}
+					fclose($fp_temp);
+				} else {
+					$this->errors[] = 'Could not open '.$this->filename.'getid3tmp mode "w+b"';
+				}
+				fclose($fp_source);
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
+			}
+			if (file_exists($this->filename)) {
+				unlink($this->filename);
+			}
+			rename($this->filename.'getid3tmp', $this->filename);
+
+		} elseif (is_writable($this->filename)) {
+
+			// less desirable alternate method - double-copies the file, overwrites original file
+			// and could corrupt source file if the script is interrupted or an error occurs.
+			if ($fp_source = @fopen($this->filename, 'rb')) {
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				if ($OldThisFileInfo['filesize'] >= pow(2, 31)) {
+					$this->errors[] = 'Unable to remove ID3v2 because file is larger than 2GB';
+					fclose($fp_source);
+					return false;
+				}
+				rewind($fp_source);
+				if ($OldThisFileInfo['avdataoffset'] !== false) {
+					fseek($fp_source, $OldThisFileInfo['avdataoffset'], SEEK_SET);
+				}
+				if ($fp_temp = tmpfile()) {
+					while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+						fwrite($fp_temp, $buffer, strlen($buffer));
+					}
+					fclose($fp_source);
+					if ($fp_source = @fopen($this->filename, 'wb')) {
+						rewind($fp_temp);
+						while ($buffer = fread($fp_temp, GETID3_FREAD_BUFFER_SIZE)) {
+							fwrite($fp_source, $buffer, strlen($buffer));
+						}
+						fseek($fp_temp, -128, SEEK_END);
+						fclose($fp_source);
+					} else {
+						$this->errors[] = 'Could not open '.$this->filename.' mode "wb"';
+					}
+					fclose($fp_temp);
+				} else {
+					$this->errors[] = 'Could not create tmpfile()';
+				}
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "rb"';
+			}
+
+		} else {
+
+			$this->errors[] = 'Directory and file both not writeable';
+
+		}
+
+		if (!empty($this->errors)) {
+			return false;
+		}
+		return true;
+	}
+
+
+	function GenerateID3v2TagFlags($flags) {
+		switch ($this->majorversion) {
+			case 4:
+				// %abcd0000
+				$flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
+				$flag .= (@$flags['extendedheader']    ? '1' : '0'); // b - Extended header
+				$flag .= (@$flags['experimental']      ? '1' : '0'); // c - Experimental indicator
+				$flag .= (@$flags['footer']            ? '1' : '0'); // d - Footer present
+				$flag .= '0000';
+				break;
+
+			case 3:
+				// %abc00000
+				$flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
+				$flag .= (@$flags['extendedheader']    ? '1' : '0'); // b - Extended header
+				$flag .= (@$flags['experimental']      ? '1' : '0'); // c - Experimental indicator
+				$flag .= '00000';
+				break;
+
+			case 2:
+				// %ab000000
+				$flag  = (@$flags['unsynchronisation'] ? '1' : '0'); // a - Unsynchronisation
+				$flag .= (@$flags['compression']       ? '1' : '0'); // b - Compression
+				$flag .= '000000';
+				break;
+
+			default:
+				return false;
+				break;
+		}
+		return chr(bindec($flag));
+	}
+
+
+	function GenerateID3v2FrameFlags($TagAlter=false, $FileAlter=false, $ReadOnly=false, $Compression=false, $Encryption=false, $GroupingIdentity=false, $Unsynchronisation=false, $DataLengthIndicator=false) {
+		switch ($this->majorversion) {
+			case 4:
+				// %0abc0000 %0h00kmnp
+				$flag1  = '0';
+				$flag1 .= $TagAlter  ? '1' : '0'; // a - Tag alter preservation (true == discard)
+				$flag1 .= $FileAlter ? '1' : '0'; // b - File alter preservation (true == discard)
+				$flag1 .= $ReadOnly  ? '1' : '0'; // c - Read only (true == read only)
+				$flag1 .= '0000';
+
+				$flag2  = '0';
+				$flag2 .= $GroupingIdentity    ? '1' : '0'; // h - Grouping identity (true == contains group information)
+				$flag2 .= '00';
+				$flag2 .= $Compression         ? '1' : '0'; // k - Compression (true == compressed)
+				$flag2 .= $Encryption          ? '1' : '0'; // m - Encryption (true == encrypted)
+				$flag2 .= $Unsynchronisation   ? '1' : '0'; // n - Unsynchronisation (true == unsynchronised)
+				$flag2 .= $DataLengthIndicator ? '1' : '0'; // p - Data length indicator (true == data length indicator added)
+				break;
+
+			case 3:
+				// %abc00000 %ijk00000
+				$flag1  = $TagAlter  ? '1' : '0';  // a - Tag alter preservation (true == discard)
+				$flag1 .= $FileAlter ? '1' : '0';  // b - File alter preservation (true == discard)
+				$flag1 .= $ReadOnly  ? '1' : '0';  // c - Read only (true == read only)
+				$flag1 .= '00000';
+
+				$flag2  = $Compression      ? '1' : '0';      // i - Compression (true == compressed)
+				$flag2 .= $Encryption       ? '1' : '0';      // j - Encryption (true == encrypted)
+				$flag2 .= $GroupingIdentity ? '1' : '0';      // k - Grouping identity (true == contains group information)
+				$flag2 .= '00000';
+				break;
+
+			default:
+				return false;
+				break;
+
+		}
+		return chr(bindec($flag1)).chr(bindec($flag2));
+	}
+
+	function GenerateID3v2FrameData($frame_name, $source_data_array) {
+		if (!getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
+			return false;
+		}
+		$framedata = '';
+
+		if (($this->majorversion < 3) || ($this->majorversion > 4)) {
+
+			$this->errors[] = 'Only ID3v2.3 and ID3v2.4 are supported in GenerateID3v2FrameData()';
+
+		} else { // $this->majorversion 3 or 4
+
+			switch ($frame_name) {
+				case 'UFID':
+					// 4.1   UFID Unique file identifier
+					// Owner identifier        <text string> $00
+					// Identifier              <up to 64 bytes binary data>
+					if (strlen($source_data_array['data']) > 64) {
+						$this->errors[] = 'Identifier not allowed to be longer than 64 bytes in '.$frame_name.' (supplied data was '.strlen($source_data_array['data']).' bytes long)';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= substr($source_data_array['data'], 0, 64); // max 64 bytes - truncate anything longer
+					}
+					break;
+
+				case 'TXXX':
+					// 4.2.2 TXXX User defined text information frame
+					// Text encoding     $xx
+					// Description       <text string according to encoding> $00 (00)
+					// Value             <text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'WXXX':
+					// 4.3.2 WXXX User defined URL link frame
+					// Text encoding     $xx
+					// Description       <text string according to encoding> $00 (00)
+					// URL               <text string>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (!isset($source_data_array['data']) || !$this->IsValidURL($source_data_array['data'], false, false)) {
+						//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+						$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'IPLS':
+					// 4.4  IPLS Involved people list (ID3v2.3 only)
+					// Text encoding     $xx
+					// People list strings    <textstrings>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'MCDI':
+					// 4.4   MCDI Music CD identifier
+					// CD TOC                <binary data>
+					$framedata .= $source_data_array['data'];
+					break;
+
+				case 'ETCO':
+					// 4.5   ETCO Event timing codes
+					// Time stamp format    $xx
+					//   Where time stamp format is:
+					// $01  (32-bit value) MPEG frames from beginning of file
+					// $02  (32-bit value) milliseconds from beginning of file
+					//   Followed by a list of key events in the following format:
+					// Type of event   $xx
+					// Time stamp      $xx (xx ...)
+					//   The 'Time stamp' is set to zero if directly at the beginning of the sound
+					//   or after the previous event. All events MUST be sorted in chronological order.
+					if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
+						$this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
+					} else {
+						$framedata .= chr($source_data_array['timestampformat']);
+						foreach ($source_data_array as $key => $val) {
+							if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
+								$this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
+							} elseif (($key != 'timestampformat') && ($key != 'flags')) {
+								if (($val['timestamp'] > 0) && ($previousETCOtimestamp >= $val['timestamp'])) {
+									//   The 'Time stamp' is set to zero if directly at the beginning of the sound
+									//   or after the previous event. All events MUST be sorted in chronological order.
+									$this->errors[] = 'Out-of-order timestamp in '.$frame_name.' ('.$val['timestamp'].') for Event Type ('.$val['typeid'].')';
+								} else {
+									$framedata .= chr($val['typeid']);
+									$framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
+								}
+							}
+						}
+					}
+					break;
+
+				case 'MLLT':
+					// 4.6   MLLT MPEG location lookup table
+					// MPEG frames between reference  $xx xx
+					// Bytes between reference        $xx xx xx
+					// Milliseconds between reference $xx xx xx
+					// Bits for bytes deviation       $xx
+					// Bits for milliseconds dev.     $xx
+					//   Then for every reference the following data is included;
+					// Deviation in bytes         %xxx....
+					// Deviation in milliseconds  %xxx....
+					if (($source_data_array['framesbetweenreferences'] > 0) && ($source_data_array['framesbetweenreferences'] <= 65535)) {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['framesbetweenreferences'], 2, false);
+					} else {
+						$this->errors[] = 'Invalid MPEG Frames Between References in '.$frame_name.' ('.$source_data_array['framesbetweenreferences'].')';
+					}
+					if (($source_data_array['bytesbetweenreferences'] > 0) && ($source_data_array['bytesbetweenreferences'] <= 16777215)) {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['bytesbetweenreferences'], 3, false);
+					} else {
+						$this->errors[] = 'Invalid bytes Between References in '.$frame_name.' ('.$source_data_array['bytesbetweenreferences'].')';
+					}
+					if (($source_data_array['msbetweenreferences'] > 0) && ($source_data_array['msbetweenreferences'] <= 16777215)) {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['msbetweenreferences'], 3, false);
+					} else {
+						$this->errors[] = 'Invalid Milliseconds Between References in '.$frame_name.' ('.$source_data_array['msbetweenreferences'].')';
+					}
+					if (!$this->IsWithinBitRange($source_data_array['bitsforbytesdeviation'], 8, false)) {
+						if (($source_data_array['bitsforbytesdeviation'] % 4) == 0) {
+							$framedata .= chr($source_data_array['bitsforbytesdeviation']);
+						} else {
+							$this->errors[] = 'Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
+						}
+					} else {
+						$this->errors[] = 'Invalid Bits For Bytes Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].')';
+					}
+					if (!$this->IsWithinBitRange($source_data_array['bitsformsdeviation'], 8, false)) {
+						if (($source_data_array['bitsformsdeviation'] % 4) == 0) {
+							$framedata .= chr($source_data_array['bitsformsdeviation']);
+						} else {
+							$this->errors[] = 'Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsforbytesdeviation'].') must be a multiple of 4.';
+						}
+					} else {
+						$this->errors[] = 'Invalid Bits For Milliseconds Deviation in '.$frame_name.' ('.$source_data_array['bitsformsdeviation'].')';
+					}
+					foreach ($source_data_array as $key => $val) {
+						if (($key != 'framesbetweenreferences') && ($key != 'bytesbetweenreferences') && ($key != 'msbetweenreferences') && ($key != 'bitsforbytesdeviation') && ($key != 'bitsformsdeviation') && ($key != 'flags')) {
+							$unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['bytedeviation']), $source_data_array['bitsforbytesdeviation'], '0', STR_PAD_LEFT);
+							$unwrittenbitstream .= str_pad(getid3_lib::Dec2Bin($val['msdeviation']),   $source_data_array['bitsformsdeviation'],    '0', STR_PAD_LEFT);
+						}
+					}
+					for ($i = 0; $i < strlen($unwrittenbitstream); $i += 8) {
+						$highnibble = bindec(substr($unwrittenbitstream, $i, 4)) << 4;
+						$lownibble  = bindec(substr($unwrittenbitstream, $i + 4, 4));
+						$framedata .= chr($highnibble & $lownibble);
+					}
+					break;
+
+				case 'SYTC':
+					// 4.7   SYTC Synchronised tempo codes
+					// Time stamp format   $xx
+					// Tempo data          <binary data>
+					//   Where time stamp format is:
+					// $01  (32-bit value) MPEG frames from beginning of file
+					// $02  (32-bit value) milliseconds from beginning of file
+					if (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
+						$this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
+					} else {
+						$framedata .= chr($source_data_array['timestampformat']);
+						foreach ($source_data_array as $key => $val) {
+							if (!$this->ID3v2IsValidETCOevent($val['typeid'])) {
+								$this->errors[] = 'Invalid Event Type byte in '.$frame_name.' ('.$val['typeid'].')';
+							} elseif (($key != 'timestampformat') && ($key != 'flags')) {
+								if (($val['tempo'] < 0) || ($val['tempo'] > 510)) {
+									$this->errors[] = 'Invalid Tempo (max = 510) in '.$frame_name.' ('.$val['tempo'].') at timestamp ('.$val['timestamp'].')';
+								} else {
+									if ($val['tempo'] > 255) {
+										$framedata .= chr(255);
+										$val['tempo'] -= 255;
+									}
+									$framedata .= chr($val['tempo']);
+									$framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
+								}
+							}
+						}
+					}
+					break;
+
+				case 'USLT':
+					// 4.8   USLT Unsynchronised lyric/text transcription
+					// Text encoding        $xx
+					// Language             $xx xx xx
+					// Content descriptor   <text string according to encoding> $00 (00)
+					// Lyrics/text          <full text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'SYLT':
+					// 4.9   SYLT Synchronised lyric/text
+					// Text encoding        $xx
+					// Language             $xx xx xx
+					// Time stamp format    $xx
+					//   $01  (32-bit value) MPEG frames from beginning of file
+					//   $02  (32-bit value) milliseconds from beginning of file
+					// Content type         $xx
+					// Content descriptor   <text string according to encoding> $00 (00)
+					//   Terminated text to be synced (typically a syllable)
+					//   Sync identifier (terminator to above string)   $00 (00)
+					//   Time stamp                                     $xx (xx ...)
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} elseif (($source_data_array['timestampformat'] > 2) || ($source_data_array['timestampformat'] < 1)) {
+						$this->errors[] = 'Invalid Time Stamp Format byte in '.$frame_name.' ('.$source_data_array['timestampformat'].')';
+					} elseif (!$this->ID3v2IsValidSYLTtype($source_data_array['contenttypeid'])) {
+						$this->errors[] = 'Invalid Content Type byte in '.$frame_name.' ('.$source_data_array['contenttypeid'].')';
+					} elseif (!is_array($source_data_array['data'])) {
+						$this->errors[] = 'Invalid Lyric/Timestamp data in '.$frame_name.' (must be an array)';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= chr($source_data_array['timestampformat']);
+						$framedata .= chr($source_data_array['contenttypeid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						ksort($source_data_array['data']);
+						foreach ($source_data_array['data'] as $key => $val) {
+							$framedata .= $val['data'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+							$framedata .= getid3_lib::BigEndian2String($val['timestamp'], 4, false);
+						}
+					}
+					break;
+
+				case 'COMM':
+					// 4.10  COMM Comments
+					// Text encoding          $xx
+					// Language               $xx xx xx
+					// Short content descrip. <text string according to encoding> $00 (00)
+					// The actual text        <full text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'RVA2':
+					// 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
+					// Identification          <text string> $00
+					//   The 'identification' string is used to identify the situation and/or
+					//   device where this adjustment should apply. The following is then
+					//   repeated for every channel:
+					// Type of channel         $xx
+					// Volume adjustment       $xx xx
+					// Bits representing peak  $xx
+					// Peak volume             $xx (xx ...)
+					$framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
+					foreach ($source_data_array as $key => $val) {
+						if ($key != 'description') {
+							$framedata .= chr($val['channeltypeid']);
+							$framedata .= getid3_lib::BigEndian2String($val['volumeadjust'], 2, false, true); // signed 16-bit
+							if (!$this->IsWithinBitRange($source_data_array['bitspeakvolume'], 8, false)) {
+								$framedata .= chr($val['bitspeakvolume']);
+								if ($val['bitspeakvolume'] > 0) {
+									$framedata .= getid3_lib::BigEndian2String($val['peakvolume'], ceil($val['bitspeakvolume'] / 8), false, false);
+								}
+							} else {
+								$this->errors[] = 'Invalid Bits Representing Peak Volume in '.$frame_name.' ('.$val['bitspeakvolume'].') (range = 0 to 255)';
+							}
+						}
+					}
+					break;
+
+				case 'RVAD':
+					// 4.12  RVAD Relative volume adjustment (ID3v2.3 only)
+					// Increment/decrement     %00fedcba
+					// Bits used for volume descr.        $xx
+					// Relative volume change, right      $xx xx (xx ...) // a
+					// Relative volume change, left       $xx xx (xx ...) // b
+					// Peak volume right                  $xx xx (xx ...)
+					// Peak volume left                   $xx xx (xx ...)
+					// Relative volume change, right back $xx xx (xx ...) // c
+					// Relative volume change, left back  $xx xx (xx ...) // d
+					// Peak volume right back             $xx xx (xx ...)
+					// Peak volume left back              $xx xx (xx ...)
+					// Relative volume change, center     $xx xx (xx ...) // e
+					// Peak volume center                 $xx xx (xx ...)
+					// Relative volume change, bass       $xx xx (xx ...) // f
+					// Peak volume bass                   $xx xx (xx ...)
+					if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
+						$this->errors[] = 'Invalid Bits For Volume Description byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
+					} else {
+						$incdecflag .= '00';
+						$incdecflag .= $source_data_array['incdec']['right']     ? '1' : '0';     // a - Relative volume change, right
+						$incdecflag .= $source_data_array['incdec']['left']      ? '1' : '0';      // b - Relative volume change, left
+						$incdecflag .= $source_data_array['incdec']['rightrear'] ? '1' : '0'; // c - Relative volume change, right back
+						$incdecflag .= $source_data_array['incdec']['leftrear']  ? '1' : '0';  // d - Relative volume change, left back
+						$incdecflag .= $source_data_array['incdec']['center']    ? '1' : '0';    // e - Relative volume change, center
+						$incdecflag .= $source_data_array['incdec']['bass']      ? '1' : '0';      // f - Relative volume change, bass
+						$framedata .= chr(bindec($incdecflag));
+						$framedata .= chr($source_data_array['bitsvolume']);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['left'],  ceil($source_data_array['bitsvolume'] / 8), false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['right'], ceil($source_data_array['bitsvolume'] / 8), false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['left'],  ceil($source_data_array['bitsvolume'] / 8), false);
+						if ($source_data_array['volumechange']['rightrear'] || $source_data_array['volumechange']['leftrear'] ||
+							$source_data_array['peakvolume']['rightrear'] || $source_data_array['peakvolume']['leftrear'] ||
+							$source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
+							$source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['leftrear'],  ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['rightrear'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['leftrear'],  ceil($source_data_array['bitsvolume']/8), false);
+						}
+						if ($source_data_array['volumechange']['center'] || $source_data_array['peakvolume']['center'] ||
+							$source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['center'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['center'], ceil($source_data_array['bitsvolume']/8), false);
+						}
+						if ($source_data_array['volumechange']['bass'] || $source_data_array['peakvolume']['bass']) {
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['volumechange']['bass'], ceil($source_data_array['bitsvolume']/8), false);
+								$framedata .= getid3_lib::BigEndian2String($source_data_array['peakvolume']['bass'], ceil($source_data_array['bitsvolume']/8), false);
+						}
+					}
+					break;
+
+				case 'EQU2':
+					// 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
+					// Interpolation method  $xx
+					//   $00  Band
+					//   $01  Linear
+					// Identification        <text string> $00
+					//   The following is then repeated for every adjustment point
+					// Frequency          $xx xx
+					// Volume adjustment  $xx xx
+					if (($source_data_array['interpolationmethod'] < 0) || ($source_data_array['interpolationmethod'] > 1)) {
+						$this->errors[] = 'Invalid Interpolation Method byte in '.$frame_name.' ('.$source_data_array['interpolationmethod'].') (valid = 0 or 1)';
+					} else {
+						$framedata .= chr($source_data_array['interpolationmethod']);
+						$framedata .= str_replace("\x00", '', $source_data_array['description'])."\x00";
+						foreach ($source_data_array['data'] as $key => $val) {
+							$framedata .= getid3_lib::BigEndian2String(intval(round($key * 2)), 2, false);
+							$framedata .= getid3_lib::BigEndian2String($val, 2, false, true); // signed 16-bit
+						}
+					}
+					break;
+
+				case 'EQUA':
+					// 4.12  EQUA Equalisation (ID3v2.3 only)
+					// Adjustment bits    $xx
+					//   This is followed by 2 bytes + ('adjustment bits' rounded up to the
+					//   nearest byte) for every equalisation band in the following format,
+					//   giving a frequency range of 0 - 32767Hz:
+					// Increment/decrement   %x (MSB of the Frequency)
+					// Frequency             (lower 15 bits)
+					// Adjustment            $xx (xx ...)
+					if (!$this->IsWithinBitRange($source_data_array['bitsvolume'], 8, false)) {
+						$this->errors[] = 'Invalid Adjustment Bits byte in '.$frame_name.' ('.$source_data_array['bitsvolume'].') (range = 1 to 255)';
+					} else {
+						$framedata .= chr($source_data_array['adjustmentbits']);
+						foreach ($source_data_array as $key => $val) {
+							if ($key != 'bitsvolume') {
+								if (($key > 32767) || ($key < 0)) {
+									$this->errors[] = 'Invalid Frequency in '.$frame_name.' ('.$key.') (range = 0 to 32767)';
+								} else {
+									if ($val >= 0) {
+										// put MSB of frequency to 1 if increment, 0 if decrement
+										$key |= 0x8000;
+									}
+									$framedata .= getid3_lib::BigEndian2String($key, 2, false);
+									$framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['adjustmentbits'] / 8), false);
+								}
+							}
+						}
+					}
+					break;
+
+				case 'RVRB':
+					// 4.13  RVRB Reverb
+					// Reverb left (ms)                 $xx xx
+					// Reverb right (ms)                $xx xx
+					// Reverb bounces, left             $xx
+					// Reverb bounces, right            $xx
+					// Reverb feedback, left to left    $xx
+					// Reverb feedback, left to right   $xx
+					// Reverb feedback, right to right  $xx
+					// Reverb feedback, right to left   $xx
+					// Premix left to right             $xx
+					// Premix right to left             $xx
+					if (!$this->IsWithinBitRange($source_data_array['left'], 16, false)) {
+						$this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['left'].') (range = 0 to 65535)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['right'], 16, false)) {
+						$this->errors[] = 'Invalid Reverb Left in '.$frame_name.' ('.$source_data_array['right'].') (range = 0 to 65535)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['bouncesL'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Bounces, Left in '.$frame_name.' ('.$source_data_array['bouncesL'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['bouncesR'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Bounces, Right in '.$frame_name.' ('.$source_data_array['bouncesR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackLL'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Left-To-Left in '.$frame_name.' ('.$source_data_array['feedbackLL'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackLR'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Left-To-Right in '.$frame_name.' ('.$source_data_array['feedbackLR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackRR'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Right-To-Right in '.$frame_name.' ('.$source_data_array['feedbackRR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['feedbackRL'], 8, false)) {
+						$this->errors[] = 'Invalid Reverb Feedback, Right-To-Left in '.$frame_name.' ('.$source_data_array['feedbackRL'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['premixLR'], 8, false)) {
+						$this->errors[] = 'Invalid Premix, Left-To-Right in '.$frame_name.' ('.$source_data_array['premixLR'].') (range = 0 to 255)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['premixRL'], 8, false)) {
+						$this->errors[] = 'Invalid Premix, Right-To-Left in '.$frame_name.' ('.$source_data_array['premixRL'].') (range = 0 to 255)';
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['left'], 2, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['right'], 2, false);
+						$framedata .= chr($source_data_array['bouncesL']);
+						$framedata .= chr($source_data_array['bouncesR']);
+						$framedata .= chr($source_data_array['feedbackLL']);
+						$framedata .= chr($source_data_array['feedbackLR']);
+						$framedata .= chr($source_data_array['feedbackRR']);
+						$framedata .= chr($source_data_array['feedbackRL']);
+						$framedata .= chr($source_data_array['premixLR']);
+						$framedata .= chr($source_data_array['premixRL']);
+					}
+					break;
+
+				case 'APIC':
+					// 4.14  APIC Attached picture
+					// Text encoding      $xx
+					// MIME type          <text string> $00
+					// Picture type       $xx
+					// Description        <text string according to encoding> $00 (00)
+					// Picture data       <binary data>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (!$this->ID3v2IsValidAPICpicturetype($source_data_array['picturetypeid'])) {
+						$this->errors[] = 'Invalid Picture Type byte in '.$frame_name.' ('.$source_data_array['picturetypeid'].') for ID3v2.'.$this->majorversion;
+					} elseif (($this->majorversion >= 3) && (!$this->ID3v2IsValidAPICimageformat($source_data_array['mime']))) {
+						$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].') for ID3v2.'.$this->majorversion;
+					} elseif (($source_data_array['mime'] == '-->') && (!$this->IsValidURL($source_data_array['data'], false, false))) {
+						//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+						$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
+						$framedata .= chr($source_data_array['picturetypeid']);
+						$framedata .= @$source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'GEOB':
+					// 4.15  GEOB General encapsulated object
+					// Text encoding          $xx
+					// MIME type              <text string> $00
+					// Filename               <text string according to encoding> $00 (00)
+					// Content description    <text string according to encoding> $00 (00)
+					// Encapsulated object    <binary data>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+					} elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
+						$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
+					} elseif (!$source_data_array['description']) {
+						$this->errors[] = 'Missing Description in '.$frame_name;
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= str_replace("\x00", '', $source_data_array['mime'])."\x00";
+						$framedata .= $source_data_array['filename'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'PCNT':
+					// 4.16  PCNT Play counter
+					//   When the counter reaches all one's, one byte is inserted in
+					//   front of the counter thus making the counter eight bits bigger
+					// Counter        $xx xx xx xx (xx ...)
+					$framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
+					break;
+
+				case 'POPM':
+					// 4.17  POPM Popularimeter
+					//   When the counter reaches all one's, one byte is inserted in
+					//   front of the counter thus making the counter eight bits bigger
+					// Email to user   <text string> $00
+					// Rating          $xx
+					// Counter         $xx xx xx xx (xx ...)
+					if (!$this->IsWithinBitRange($source_data_array['rating'], 8, false)) {
+						$this->errors[] = 'Invalid Rating byte in '.$frame_name.' ('.$source_data_array['rating'].') (range = 0 to 255)';
+					} elseif (!IsValidEmail($source_data_array['email'])) {
+						$this->errors[] = 'Invalid Email in '.$frame_name.' ('.$source_data_array['email'].')';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['email'])."\x00";
+						$framedata .= chr($source_data_array['rating']);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
+					}
+					break;
+
+				case 'RBUF':
+					// 4.18  RBUF Recommended buffer size
+					// Buffer size               $xx xx xx
+					// Embedded info flag        %0000000x
+					// Offset to next tag        $xx xx xx xx
+					if (!$this->IsWithinBitRange($source_data_array['buffersize'], 24, false)) {
+						$this->errors[] = 'Invalid Buffer Size in '.$frame_name;
+					} elseif (!$this->IsWithinBitRange($source_data_array['nexttagoffset'], 32, false)) {
+						$this->errors[] = 'Invalid Offset To Next Tag in '.$frame_name;
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['buffersize'], 3, false);
+						$flag .= '0000000';
+						$flag .= $source_data_array['flags']['embededinfo'] ? '1' : '0';
+						$framedata .= chr(bindec($flag));
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['nexttagoffset'], 4, false);
+					}
+					break;
+
+				case 'AENC':
+					// 4.19  AENC Audio encryption
+					// Owner identifier   <text string> $00
+					// Preview start      $xx xx
+					// Preview length     $xx xx
+					// Encryption info    <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['previewstart'], 16, false)) {
+						$this->errors[] = 'Invalid Preview Start in '.$frame_name.' ('.$source_data_array['previewstart'].')';
+					} elseif (!$this->IsWithinBitRange($source_data_array['previewlength'], 16, false)) {
+						$this->errors[] = 'Invalid Preview Length in '.$frame_name.' ('.$source_data_array['previewlength'].')';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['previewstart'], 2, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['previewlength'], 2, false);
+						$framedata .= $source_data_array['encryptioninfo'];
+					}
+					break;
+
+				case 'LINK':
+					// 4.20  LINK Linked information
+					// Frame identifier               $xx xx xx xx
+					// URL                            <text string> $00
+					// ID and additional data         <text string(s)>
+					if (!getid3_id3v2::IsValidID3v2FrameName($source_data_array['frameid'], $this->majorversion)) {
+						$this->errors[] = 'Invalid Frame Identifier in '.$frame_name.' ('.$source_data_array['frameid'].')';
+					} elseif (!$this->IsValidURL($source_data_array['data'], true, false)) {
+						//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+						$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+					} elseif ((($source_data_array['frameid'] == 'AENC') || ($source_data_array['frameid'] == 'APIC') || ($source_data_array['frameid'] == 'GEOB') || ($source_data_array['frameid'] == 'TXXX')) && ($source_data_array['additionaldata'] == '')) {
+						$this->errors[] = 'Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} elseif (($source_data_array['frameid'] == 'USER') && (getid3_id3v2::LanguageLookup($source_data_array['additionaldata'], true) == '')) {
+						$this->errors[] = 'Language must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} elseif (($source_data_array['frameid'] == 'PRIV') && ($source_data_array['additionaldata'] == '')) {
+						$this->errors[] = 'Owner Identifier must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} elseif ((($source_data_array['frameid'] == 'COMM') || ($source_data_array['frameid'] == 'SYLT') || ($source_data_array['frameid'] == 'USLT')) && ((getid3_id3v2::LanguageLookup(substr($source_data_array['additionaldata'], 0, 3), true) == '') || (substr($source_data_array['additionaldata'], 3) == ''))) {
+						$this->errors[] = 'Language followed by Content Descriptor must be specified as additional data for Frame Identifier of '.$source_data_array['frameid'].' in '.$frame_name;
+					} else {
+						$framedata .= $source_data_array['frameid'];
+						$framedata .= str_replace("\x00", '', $source_data_array['data'])."\x00";
+						switch ($source_data_array['frameid']) {
+							case 'COMM':
+							case 'SYLT':
+							case 'USLT':
+							case 'PRIV':
+							case 'USER':
+							case 'AENC':
+							case 'APIC':
+							case 'GEOB':
+							case 'TXXX':
+								$framedata .= $source_data_array['additionaldata'];
+								break;
+							case 'ASPI':
+							case 'ETCO':
+							case 'EQU2':
+							case 'MCID':
+							case 'MLLT':
+							case 'OWNE':
+							case 'RVA2':
+							case 'RVRB':
+							case 'SYTC':
+							case 'IPLS':
+							case 'RVAD':
+							case 'EQUA':
+								// no additional data required
+								break;
+							case 'RBUF':
+								if ($this->majorversion == 3) {
+									// no additional data required
+								} else {
+									$this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
+								}
+
+							default:
+								if ((substr($source_data_array['frameid'], 0, 1) == 'T') || (substr($source_data_array['frameid'], 0, 1) == 'W')) {
+									// no additional data required
+								} else {
+									$this->errors[] = $source_data_array['frameid'].' is not a valid Frame Identifier in '.$frame_name.' (in ID3v2.'.$this->majorversion.')';
+								}
+								break;
+						}
+					}
+					break;
+
+				case 'POSS':
+					// 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
+					// Time stamp format         $xx
+					// Position                  $xx (xx ...)
+					if (($source_data_array['timestampformat'] < 1) || ($source_data_array['timestampformat'] > 2)) {
+						$this->errors[] = 'Invalid Time Stamp Format in '.$frame_name.' ('.$source_data_array['timestampformat'].') (valid = 1 or 2)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['position'], 32, false)) {
+						$this->errors[] = 'Invalid Position in '.$frame_name.' ('.$source_data_array['position'].') (range = 0 to 4294967295)';
+					} else {
+						$framedata .= chr($source_data_array['timestampformat']);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['position'], 4, false);
+					}
+					break;
+
+				case 'USER':
+					// 4.22  USER Terms of use (ID3v2.3+ only)
+					// Text encoding        $xx
+					// Language             $xx xx xx
+					// The actual text      <text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
+					} elseif (getid3_id3v2::LanguageLookup($source_data_array['language'], true) == '') {
+						$this->errors[] = 'Invalid Language in '.$frame_name.' ('.$source_data_array['language'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= strtolower($source_data_array['language']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'OWNE':
+					// 4.23  OWNE Ownership frame (ID3v2.3+ only)
+					// Text encoding     $xx
+					// Price paid        <text string> $00
+					// Date of purch.    <text string>
+					// Seller            <text string according to encoding>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
+					} elseif (!$this->IsANumber($source_data_array['pricepaid']['value'], false)) {
+						$this->errors[] = 'Invalid Price Paid in '.$frame_name.' ('.$source_data_array['pricepaid']['value'].')';
+					} elseif (!$this->IsValidDateStampString($source_data_array['purchasedate'])) {
+						$this->errors[] = 'Invalid Date Of Purchase in '.$frame_name.' ('.$source_data_array['purchasedate'].') (format = YYYYMMDD)';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						$framedata .= str_replace("\x00", '', $source_data_array['pricepaid']['value'])."\x00";
+						$framedata .= $source_data_array['purchasedate'];
+						$framedata .= $source_data_array['seller'];
+					}
+					break;
+
+				case 'COMR':
+					// 4.24  COMR Commercial frame (ID3v2.3+ only)
+					// Text encoding      $xx
+					// Price string       <text string> $00
+					// Valid until        <text string>
+					// Contact URL        <text string> $00
+					// Received as        $xx
+					// Name of seller     <text string according to encoding> $00 (00)
+					// Description        <text string according to encoding> $00 (00)
+					// Picture MIME type  <string> $00
+					// Seller logo        <binary data>
+					$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+					if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+						$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].')';
+					} elseif (!$this->IsValidDateStampString($source_data_array['pricevaliduntil'])) {
+						$this->errors[] = 'Invalid Valid Until date in '.$frame_name.' ('.$source_data_array['pricevaliduntil'].') (format = YYYYMMDD)';
+					} elseif (!$this->IsValidURL($source_data_array['contacturl'], false, true)) {
+						$this->errors[] = 'Invalid Contact URL in '.$frame_name.' ('.$source_data_array['contacturl'].') (allowed schemes: http, https, ftp, mailto)';
+					} elseif (!$this->ID3v2IsValidCOMRreceivedAs($source_data_array['receivedasid'])) {
+						$this->errors[] = 'Invalid Received As byte in '.$frame_name.' ('.$source_data_array['contacturl'].') (range = 0 to 8)';
+					} elseif (!$this->IsValidMIMEstring($source_data_array['mime'])) {
+						$this->errors[] = 'Invalid MIME Type in '.$frame_name.' ('.$source_data_array['mime'].')';
+					} else {
+						$framedata .= chr($source_data_array['encodingid']);
+						unset($pricestring);
+						foreach ($source_data_array['price'] as $key => $val) {
+							if ($this->ID3v2IsValidPriceString($key.$val['value'])) {
+								$pricestrings[] = $key.$val['value'];
+							} else {
+								$this->errors[] = 'Invalid Price String in '.$frame_name.' ('.$key.$val['value'].')';
+							}
+						}
+						$framedata .= implode('/', $pricestrings);
+						$framedata .= $source_data_array['pricevaliduntil'];
+						$framedata .= str_replace("\x00", '', $source_data_array['contacturl'])."\x00";
+						$framedata .= chr($source_data_array['receivedasid']);
+						$framedata .= $source_data_array['sellername'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['description'].getid3_id3v2::TextEncodingTerminatorLookup($source_data_array['encodingid']);
+						$framedata .= $source_data_array['mime']."\x00";
+						$framedata .= $source_data_array['logo'];
+					}
+					break;
+
+				case 'ENCR':
+					// 4.25  ENCR Encryption method registration (ID3v2.3+ only)
+					// Owner identifier    <text string> $00
+					// Method symbol       $xx
+					// Encryption data     <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['methodsymbol'], 8, false)) {
+						$this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['methodsymbol'].') (range = 0 to 255)';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= ord($source_data_array['methodsymbol']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'GRID':
+					// 4.26  GRID Group identification registration (ID3v2.3+ only)
+					// Owner identifier      <text string> $00
+					// Group symbol          $xx
+					// Group dependent data  <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
+						$this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
+					} else {
+						$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+						$framedata .= ord($source_data_array['groupsymbol']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'PRIV':
+					// 4.27  PRIV Private frame (ID3v2.3+ only)
+					// Owner identifier      <text string> $00
+					// The private data      <binary data>
+					$framedata .= str_replace("\x00", '', $source_data_array['ownerid'])."\x00";
+					$framedata .= $source_data_array['data'];
+					break;
+
+				case 'SIGN':
+					// 4.28  SIGN Signature frame (ID3v2.4+ only)
+					// Group symbol      $xx
+					// Signature         <binary data>
+					if (!$this->IsWithinBitRange($source_data_array['groupsymbol'], 8, false)) {
+						$this->errors[] = 'Invalid Group Symbol in '.$frame_name.' ('.$source_data_array['groupsymbol'].') (range = 0 to 255)';
+					} else {
+						$framedata .= ord($source_data_array['groupsymbol']);
+						$framedata .= $source_data_array['data'];
+					}
+					break;
+
+				case 'SEEK':
+					// 4.29  SEEK Seek frame (ID3v2.4+ only)
+					// Minimum offset to next tag       $xx xx xx xx
+					if (!$this->IsWithinBitRange($source_data_array['data'], 32, false)) {
+						$this->errors[] = 'Invalid Minimum Offset in '.$frame_name.' ('.$source_data_array['data'].') (range = 0 to 4294967295)';
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['data'], 4, false);
+					}
+					break;
+
+				case 'ASPI':
+					// 4.30  ASPI Audio seek point index (ID3v2.4+ only)
+					// Indexed data start (S)         $xx xx xx xx
+					// Indexed data length (L)        $xx xx xx xx
+					// Number of index points (N)     $xx xx
+					// Bits per index point (b)       $xx
+					//   Then for every index point the following data is included:
+					// Fraction at index (Fi)          $xx (xx)
+					if (!$this->IsWithinBitRange($source_data_array['datastart'], 32, false)) {
+						$this->errors[] = 'Invalid Indexed Data Start in '.$frame_name.' ('.$source_data_array['datastart'].') (range = 0 to 4294967295)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['datalength'], 32, false)) {
+						$this->errors[] = 'Invalid Indexed Data Length in '.$frame_name.' ('.$source_data_array['datalength'].') (range = 0 to 4294967295)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['indexpoints'], 16, false)) {
+						$this->errors[] = 'Invalid Number Of Index Points in '.$frame_name.' ('.$source_data_array['indexpoints'].') (range = 0 to 65535)';
+					} elseif (!$this->IsWithinBitRange($source_data_array['bitsperpoint'], 8, false)) {
+						$this->errors[] = 'Invalid Bits Per Index Point in '.$frame_name.' ('.$source_data_array['bitsperpoint'].') (range = 0 to 255)';
+					} elseif ($source_data_array['indexpoints'] != count($source_data_array['indexes'])) {
+						$this->errors[] = 'Number Of Index Points does not match actual supplied data in '.$frame_name;
+					} else {
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['datastart'], 4, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['datalength'], 4, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['indexpoints'], 2, false);
+						$framedata .= getid3_lib::BigEndian2String($source_data_array['bitsperpoint'], 1, false);
+						foreach ($source_data_array['indexes'] as $key => $val) {
+							$framedata .= getid3_lib::BigEndian2String($val, ceil($source_data_array['bitsperpoint'] / 8), false);
+						}
+					}
+					break;
+
+				case 'RGAD':
+					//   RGAD Replay Gain Adjustment
+					//   http://privatewww.essex.ac.uk/~djmrob/replaygain/
+					// Peak Amplitude                     $xx $xx $xx $xx
+					// Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
+					// Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
+					//   a - name code
+					//   b - originator code
+					//   c - sign bit
+					//   d - replay gain adjustment
+
+					if (($source_data_array['track_adjustment'] > 51) || ($source_data_array['track_adjustment'] < -51)) {
+						$this->errors[] = 'Invalid Track Adjustment in '.$frame_name.' ('.$source_data_array['track_adjustment'].') (range = -51.0 to +51.0)';
+					} elseif (($source_data_array['album_adjustment'] > 51) || ($source_data_array['album_adjustment'] < -51)) {
+						$this->errors[] = 'Invalid Album Adjustment in '.$frame_name.' ('.$source_data_array['album_adjustment'].') (range = -51.0 to +51.0)';
+					} elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['track_name'])) {
+						$this->errors[] = 'Invalid Track Name Code in '.$frame_name.' ('.$source_data_array['raw']['track_name'].') (range = 0 to 2)';
+					} elseif (!$this->ID3v2IsValidRGADname($source_data_array['raw']['album_name'])) {
+						$this->errors[] = 'Invalid Album Name Code in '.$frame_name.' ('.$source_data_array['raw']['album_name'].') (range = 0 to 2)';
+					} elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['track_originator'])) {
+						$this->errors[] = 'Invalid Track Originator Code in '.$frame_name.' ('.$source_data_array['raw']['track_originator'].') (range = 0 to 3)';
+					} elseif (!$this->ID3v2IsValidRGADoriginator($source_data_array['raw']['album_originator'])) {
+						$this->errors[] = 'Invalid Album Originator Code in '.$frame_name.' ('.$source_data_array['raw']['album_originator'].') (range = 0 to 3)';
+					} else {
+						$framedata .= getid3_lib::Float2String($source_data_array['peakamplitude'], 32);
+						$framedata .= getid3_lib::RGADgainString($source_data_array['raw']['track_name'], $source_data_array['raw']['track_originator'], $source_data_array['track_adjustment']);
+						$framedata .= getid3_lib::RGADgainString($source_data_array['raw']['album_name'], $source_data_array['raw']['album_originator'], $source_data_array['album_adjustment']);
+					}
+					break;
+
+				default:
+					if ($frame_name{0} == 'T') {
+						// 4.2. T???  Text information frames
+						// Text encoding                $xx
+						// Information                  <text string(s) according to encoding>
+						$source_data_array['encodingid'] = (isset($source_data_array['encodingid']) ? $source_data_array['encodingid'] : $this->id3v2_default_encodingid);
+						if (!$this->ID3v2IsValidTextEncoding($source_data_array['encodingid'])) {
+							$this->errors[] = 'Invalid Text Encoding in '.$frame_name.' ('.$source_data_array['encodingid'].') for ID3v2.'.$this->majorversion;
+						} else {
+							$framedata .= chr($source_data_array['encodingid']);
+							$framedata .= $source_data_array['data'];
+						}
+					} elseif ($frame_name{0} == 'W') {
+						// 4.3. W???  URL link frames
+						// URL              <text string>
+						if (!$this->IsValidURL($source_data_array['data'], false, false)) {
+							//$this->errors[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+							// probably should be an error, need to rewrite IsValidURL() to handle other encodings
+							$this->warnings[] = 'Invalid URL in '.$frame_name.' ('.$source_data_array['data'].')';
+						} else {
+							$framedata .= $source_data_array['data'];
+						}
+					} else {
+						$this->errors[] = $frame_name.' not yet supported in $this->GenerateID3v2FrameData()';
+					}
+					break;
+			}
+		}
+		if (!empty($this->errors)) {
+			return false;
+		}
+		return $framedata;
+	}
+
+	function ID3v2FrameIsAllowed($frame_name, $source_data_array) {
+		static $PreviousFrames = array();
+
+		if ($frame_name === null) {
+			// if the writing functions are called multiple times, the static array needs to be
+			// cleared - this can be done by calling $this->ID3v2FrameIsAllowed(null, '')
+			$PreviousFrames = array();
+			return true;
+		}
+
+		if ($this->majorversion == 4) {
+			switch ($frame_name) {
+				case 'UFID':
+				case 'AENC':
+				case 'ENCR':
+				case 'GRID':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
+					}
+					break;
+
+				case 'TXXX':
+				case 'WXXX':
+				case 'RVA2':
+				case 'EQU2':
+				case 'APIC':
+				case 'GEOB':
+					if (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['description'];
+					}
+					break;
+
+				case 'USER':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'];
+					}
+					break;
+
+				case 'USLT':
+				case 'SYLT':
+				case 'COMM':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
+					}
+					break;
+
+				case 'POPM':
+					if (!isset($source_data_array['email'])) {
+						$this->errors[] = '[email] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['email'];
+					}
+					break;
+
+				case 'IPLS':
+				case 'MCDI':
+				case 'ETCO':
+				case 'MLLT':
+				case 'SYTC':
+				case 'RVRB':
+				case 'PCNT':
+				case 'RBUF':
+				case 'POSS':
+				case 'OWNE':
+				case 'SEEK':
+				case 'ASPI':
+				case 'RGAD':
+					if (in_array($frame_name, $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed';
+					} else {
+						$PreviousFrames[] = $frame_name;
+					}
+					break;
+
+				case 'LINK':
+					// this isn't implemented quite right (yet) - it should check the target frame data for compliance
+					// but right now it just allows one linked frame of each type, to be safe.
+					if (!isset($source_data_array['frameid'])) {
+						$this->errors[] = '[frameid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
+					} elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
+						// no links to singleton tags
+						$this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
+						$PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
+					}
+					break;
+
+				case 'COMR':
+					//   There may be more than one 'commercial frame' in a tag, but no two may be identical
+					// Checking isn't implemented at all (yet) - just assumes that it's OK.
+					break;
+
+				case 'PRIV':
+				case 'SIGN':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['data'])) {
+						$this->errors[] = '[data] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
+					}
+					break;
+
+				default:
+					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
+					}
+					break;
+			}
+
+		} elseif ($this->majorversion == 3) {
+
+			switch ($frame_name) {
+				case 'UFID':
+				case 'AENC':
+				case 'ENCR':
+				case 'GRID':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
+					}
+					break;
+
+				case 'TXXX':
+				case 'WXXX':
+				case 'APIC':
+				case 'GEOB':
+					if (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['description'];
+					}
+					break;
+
+				case 'USER':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language ('.$source_data_array['language'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'];
+					}
+					break;
+
+				case 'USLT':
+				case 'SYLT':
+				case 'COMM':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
+					}
+					break;
+
+				case 'POPM':
+					if (!isset($source_data_array['email'])) {
+						$this->errors[] = '[email] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['email'];
+					}
+					break;
+
+				case 'IPLS':
+				case 'MCDI':
+				case 'ETCO':
+				case 'MLLT':
+				case 'SYTC':
+				case 'RVAD':
+				case 'EQUA':
+				case 'RVRB':
+				case 'PCNT':
+				case 'RBUF':
+				case 'POSS':
+				case 'OWNE':
+				case 'RGAD':
+					if (in_array($frame_name, $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed';
+					} else {
+						$PreviousFrames[] = $frame_name;
+					}
+					break;
+
+				case 'LINK':
+					// this isn't implemented quite right (yet) - it should check the target frame data for compliance
+					// but right now it just allows one linked frame of each type, to be safe.
+					if (!isset($source_data_array['frameid'])) {
+						$this->errors[] = '[frameid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
+					} elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
+						// no links to singleton tags
+						$this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
+						$PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
+					}
+					break;
+
+				case 'COMR':
+					//   There may be more than one 'commercial frame' in a tag, but no two may be identical
+					// Checking isn't implemented at all (yet) - just assumes that it's OK.
+					break;
+
+				case 'PRIV':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['data'])) {
+						$this->errors[] = '[data] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'].$source_data_array['data'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID + Data ('.$source_data_array['ownerid'].' + '.$source_data_array['data'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'].$source_data_array['data'];
+					}
+					break;
+
+				default:
+					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
+					}
+					break;
+			}
+
+		} elseif ($this->majorversion == 2) {
+
+			switch ($frame_name) {
+				case 'UFI':
+				case 'CRM':
+				case 'CRA':
+					if (!isset($source_data_array['ownerid'])) {
+						$this->errors[] = '[ownerid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['ownerid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same OwnerID ('.$source_data_array['ownerid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['ownerid'];
+					}
+					break;
+
+				case 'TXX':
+				case 'WXX':
+				case 'PIC':
+				case 'GEO':
+					if (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Description ('.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['description'];
+					}
+					break;
+
+				case 'ULT':
+				case 'SLT':
+				case 'COM':
+					if (!isset($source_data_array['language'])) {
+						$this->errors[] = '[language] not specified for '.$frame_name;
+					} elseif (!isset($source_data_array['description'])) {
+						$this->errors[] = '[description] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['language'].$source_data_array['description'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Language + Description ('.$source_data_array['language'].' + '.$source_data_array['description'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['language'].$source_data_array['description'];
+					}
+					break;
+
+				case 'POP':
+					if (!isset($source_data_array['email'])) {
+						$this->errors[] = '[email] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['email'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same Email ('.$source_data_array['email'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['email'];
+					}
+					break;
+
+				case 'IPL':
+				case 'MCI':
+				case 'ETC':
+				case 'MLL':
+				case 'STC':
+				case 'RVA':
+				case 'EQU':
+				case 'REV':
+				case 'CNT':
+				case 'BUF':
+					if (in_array($frame_name, $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed';
+					} else {
+						$PreviousFrames[] = $frame_name;
+					}
+					break;
+
+				case 'LNK':
+					// this isn't implemented quite right (yet) - it should check the target frame data for compliance
+					// but right now it just allows one linked frame of each type, to be safe.
+					if (!isset($source_data_array['frameid'])) {
+						$this->errors[] = '[frameid] not specified for '.$frame_name;
+					} elseif (in_array($frame_name.$source_data_array['frameid'], $PreviousFrames)) {
+						$this->errors[] = 'Only one '.$frame_name.' tag allowed with the same FrameID ('.$source_data_array['frameid'].')';
+					} elseif (in_array($source_data_array['frameid'], $PreviousFrames)) {
+						// no links to singleton tags
+						$this->errors[] = 'Cannot specify a '.$frame_name.' tag to a singleton tag that already exists ('.$source_data_array['frameid'].')';
+					} else {
+						$PreviousFrames[] = $frame_name.$source_data_array['frameid']; // only one linked tag of this type
+						$PreviousFrames[] = $source_data_array['frameid'];             // no non-linked singleton tags of this type
+					}
+					break;
+
+				default:
+					if (($frame_name{0} != 'T') && ($frame_name{0} != 'W')) {
+						$this->errors[] = 'Frame not allowed in ID3v2.'.$this->majorversion.': '.$frame_name;
+					}
+					break;
+			}
+		}
+
+		if (!empty($this->errors)) {
+			return false;
+		}
+		return true;
+	}
+
+	function GenerateID3v2Tag($noerrorsonly=true) {
+		$this->ID3v2FrameIsAllowed(null, ''); // clear static array in case this isn't the first call to $this->GenerateID3v2Tag()
+
+		$tagstring = '';
+		if (is_array($this->tag_data)) {
+			foreach ($this->tag_data as $frame_name => $frame_rawinputdata) {
+				foreach ($frame_rawinputdata as $irrelevantindex => $source_data_array) {
+					if (getid3_id3v2::IsValidID3v2FrameName($frame_name, $this->majorversion)) {
+						unset($frame_length);
+						unset($frame_flags);
+						$frame_data = false;
+						if ($this->ID3v2FrameIsAllowed($frame_name, $source_data_array)) {
+							if ($frame_data = $this->GenerateID3v2FrameData($frame_name, $source_data_array)) {
+								$FrameUnsynchronisation = false;
+								if ($this->majorversion >= 4) {
+									// frame-level unsynchronisation
+									$unsynchdata = $frame_data;
+									if ($this->id3v2_use_unsynchronisation) {
+										$unsynchdata = $this->Unsynchronise($frame_data);
+									}
+									if (strlen($unsynchdata) != strlen($frame_data)) {
+										// unsynchronisation needed
+										$FrameUnsynchronisation = true;
+										$frame_data = $unsynchdata;
+										if (isset($TagUnsynchronisation) && $TagUnsynchronisation === false) {
+											// only set to true if ALL frames are unsynchronised
+										} else {
+											$TagUnsynchronisation = true;
+										}
+									} else {
+										if (isset($TagUnsynchronisation)) {
+											$TagUnsynchronisation = false;
+										}
+									}
+									unset($unsynchdata);
+
+									$frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, true);
+								} else {
+									$frame_length = getid3_lib::BigEndian2String(strlen($frame_data), 4, false);
+								}
+								$frame_flags  = $this->GenerateID3v2FrameFlags($this->ID3v2FrameFlagsLookupTagAlter($frame_name), $this->ID3v2FrameFlagsLookupFileAlter($frame_name), false, false, false, false, $FrameUnsynchronisation, false);
+							}
+						} else {
+							$this->errors[] = 'Frame "'.$frame_name.'" is NOT allowed';
+						}
+						if ($frame_data === false) {
+							$this->errors[] = '$this->GenerateID3v2FrameData() failed for "'.$frame_name.'"';
+							if ($noerrorsonly) {
+								return false;
+							} else {
+								unset($frame_name);
+							}
+						}
+					} else {
+						// ignore any invalid frame names, including 'title', 'header', etc
+						$this->warnings[] = 'Ignoring invalid ID3v2 frame type: "'.$frame_name.'"';
+						unset($frame_name);
+						unset($frame_length);
+						unset($frame_flags);
+						unset($frame_data);
+					}
+					if (isset($frame_name) && isset($frame_length) && isset($frame_flags) && isset($frame_data)) {
+						$tagstring .= $frame_name.$frame_length.$frame_flags.$frame_data;
+					}
+				}
+			}
+
+			if (!isset($TagUnsynchronisation)) {
+				$TagUnsynchronisation = false;
+			}
+			if (($this->majorversion <= 3) && $this->id3v2_use_unsynchronisation) {
+				// tag-level unsynchronisation
+				$unsynchdata = $this->Unsynchronise($tagstring);
+				if (strlen($unsynchdata) != strlen($tagstring)) {
+					// unsynchronisation needed
+					$TagUnsynchronisation = true;
+					$tagstring = $unsynchdata;
+				}
+			}
+
+			while ($this->paddedlength < (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion))) {
+				$this->paddedlength += 1024;
+			}
+
+			$footer = false; // ID3v2 footers not yet supported in getID3()
+			if (!$footer && ($this->paddedlength > (strlen($tagstring) + getid3_id3v2::ID3v2HeaderLength($this->majorversion)))) {
+				// pad up to $paddedlength bytes if unpadded tag is shorter than $paddedlength
+				// "Furthermore it MUST NOT have any padding when a tag footer is added to the tag."
+				$tagstring .= @str_repeat("\x00", $this->paddedlength - strlen($tagstring) - getid3_id3v2::ID3v2HeaderLength($this->majorversion));
+			}
+			if ($this->id3v2_use_unsynchronisation && (substr($tagstring, strlen($tagstring) - 1, 1) == "\xFF")) {
+				// special unsynchronisation case:
+				// if last byte == $FF then appended a $00
+				$TagUnsynchronisation = true;
+				$tagstring .= "\x00";
+			}
+
+			$tagheader  = 'ID3';
+			$tagheader .= chr($this->majorversion);
+			$tagheader .= chr($this->minorversion);
+			$tagheader .= $this->GenerateID3v2TagFlags(array('unsynchronisation'=>$TagUnsynchronisation));
+			$tagheader .= getid3_lib::BigEndian2String(strlen($tagstring), 4, true);
+
+			return $tagheader.$tagstring;
+		}
+		$this->errors[] = 'tag_data is not an array in GenerateID3v2Tag()';
+		return false;
+	}
+
+	function ID3v2IsValidPriceString($pricestring) {
+		if (getid3_id3v2::LanguageLookup(substr($pricestring, 0, 3), true) == '') {
+			return false;
+		} elseif (!$this->IsANumber(substr($pricestring, 3), true)) {
+			return false;
+		}
+		return true;
+	}
+
+	function ID3v2FrameFlagsLookupTagAlter($framename) {
+		// unfinished
+		switch ($framename) {
+			case 'RGAD':
+				$allow = true;
+			default:
+				$allow = false;
+				break;
+		}
+		return $allow;
+	}
+
+	function ID3v2FrameFlagsLookupFileAlter($framename) {
+		// unfinished
+		switch ($framename) {
+			case 'RGAD':
+				return false;
+				break;
+
+			default:
+				return false;
+				break;
+		}
+	}
+
+	function ID3v2IsValidETCOevent($eventid) {
+		if (($eventid < 0) || ($eventid > 0xFF)) {
+			// outside range of 1 byte
+			return false;
+		} elseif (($eventid >= 0xF0) && ($eventid <= 0xFC)) {
+			// reserved for future use
+			return false;
+		} elseif (($eventid >= 0x17) && ($eventid <= 0xDF)) {
+			// reserved for future use
+			return false;
+		} elseif (($eventid >= 0x0E) && ($eventid <= 0x16) && ($this->majorversion == 2)) {
+			// not defined in ID3v2.2
+			return false;
+		} elseif (($eventid >= 0x15) && ($eventid <= 0x16) && ($this->majorversion == 3)) {
+			// not defined in ID3v2.3
+			return false;
+		}
+		return true;
+	}
+
+	function ID3v2IsValidSYLTtype($contenttype) {
+		if (($contenttype >= 0) && ($contenttype <= 8) && ($this->majorversion == 4)) {
+			return true;
+		} elseif (($contenttype >= 0) && ($contenttype <= 6) && ($this->majorversion == 3)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidRVA2channeltype($channeltype) {
+		if (($channeltype >= 0) && ($channeltype <= 8) && ($this->majorversion == 4)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidAPICpicturetype($picturetype) {
+		if (($picturetype >= 0) && ($picturetype <= 0x14) && ($this->majorversion >= 2) && ($this->majorversion <= 4)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidAPICimageformat($imageformat) {
+		if ($imageformat == '-->') {
+			return true;
+		} elseif ($this->majorversion == 2) {
+			if ((strlen($imageformat) == 3) && ($imageformat == strtoupper($imageformat))) {
+				return true;
+			}
+		} elseif (($this->majorversion == 3) || ($this->majorversion == 4)) {
+			if ($this->IsValidMIMEstring($imageformat)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function ID3v2IsValidCOMRreceivedAs($receivedas) {
+		if (($this->majorversion >= 3) && ($receivedas >= 0) && ($receivedas <= 8)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidRGADname($RGADname) {
+		if (($RGADname >= 0) && ($RGADname <= 2)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidRGADoriginator($RGADoriginator) {
+		if (($RGADoriginator >= 0) && ($RGADoriginator <= 3)) {
+			return true;
+		}
+		return false;
+	}
+
+	function ID3v2IsValidTextEncoding($textencodingbyte) {
+		static $ID3v2IsValidTextEncoding_cache = array(
+			2 => array(true, true),
+			3 => array(true, true),
+			4 => array(true, true, true, true));
+		return isset($ID3v2IsValidTextEncoding_cache[$this->majorversion][$textencodingbyte]);
+	}
+
+	function Unsynchronise($data) {
+		// Whenever a false synchronisation is found within the tag, one zeroed
+		// byte is inserted after the first false synchronisation byte. The
+		// format of a correct sync that should be altered by ID3 encoders is as
+		// follows:
+		//      %11111111 111xxxxx
+		// And should be replaced with:
+		//      %11111111 00000000 111xxxxx
+		// This has the side effect that all $FF 00 combinations have to be
+		// altered, so they won't be affected by the decoding process. Therefore
+		// all the $FF 00 combinations have to be replaced with the $FF 00 00
+		// combination during the unsynchronisation.
+
+		$data = str_replace("\xFF\x00", "\xFF\x00\x00", $data);
+		$unsyncheddata = '';
+		$datalength = strlen($data);
+		for ($i = 0; $i < $datalength; $i++) {
+			$thischar = $data{$i};
+			$unsyncheddata .= $thischar;
+			if ($thischar == "\xFF") {
+				$nextchar = ord($data{$i + 1});
+				if (($nextchar & 0xE0) == 0xE0) {
+					// previous byte = 11111111, this byte = 111?????
+					$unsyncheddata .= "\x00";
+				}
+			}
+		}
+		return $unsyncheddata;
+	}
+
+	function is_hash($var) {
+		// written by dev-nullØchristophe*vg
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (is_array($var)) {
+			$keys = array_keys($var);
+			$all_num = true;
+			for ($i = 0; $i < count($keys); $i++) {
+				if (is_string($keys[$i])) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+	function array_join_merge($arr1, $arr2) {
+		// written by dev-nullØchristophe*vg
+		// taken from http://www.php.net/manual/en/function.array-merge-recursive.php
+		if (is_array($arr1) && is_array($arr2)) {
+			// the same -> merge
+			$new_array = array();
+
+			if ($this->is_hash($arr1) && $this->is_hash($arr2)) {
+				// hashes -> merge based on keys
+				$keys = array_merge(array_keys($arr1), array_keys($arr2));
+				foreach ($keys as $key) {
+					$new_array[$key] = $this->array_join_merge(@$arr1[$key], @$arr2[$key]);
+				}
+			} else {
+				// two real arrays -> merge
+				$new_array = array_reverse(array_unique(array_reverse(array_merge($arr1, $arr2))));
+			}
+			return $new_array;
+		} else {
+			// not the same ... take new one if defined, else the old one stays
+			return $arr2 ? $arr2 : $arr1;
+		}
+	}
+
+	function IsValidMIMEstring($mimestring) {
+		if ((strlen($mimestring) >= 3) && (strpos($mimestring, '/') > 0) && (strpos($mimestring, '/') < (strlen($mimestring) - 1))) {
+			return true;
+		}
+		return false;
+	}
+
+	function IsWithinBitRange($number, $maxbits, $signed=false) {
+		if ($signed) {
+			if (($number > (0 - pow(2, $maxbits - 1))) && ($number <= pow(2, $maxbits - 1))) {
+				return true;
+			}
+		} else {
+			if (($number >= 0) && ($number <= pow(2, $maxbits))) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function safe_parse_url($url) {
+		$parts = @parse_url($url);
+		$parts['scheme'] = (isset($parts['scheme']) ? $parts['scheme'] : '');
+		$parts['host']   = (isset($parts['host'])   ? $parts['host']   : '');
+		$parts['user']   = (isset($parts['user'])   ? $parts['user']   : '');
+		$parts['pass']   = (isset($parts['pass'])   ? $parts['pass']   : '');
+		$parts['path']   = (isset($parts['path'])   ? $parts['path']   : '');
+		$parts['query']  = (isset($parts['query'])  ? $parts['query']  : '');
+		return $parts;
+	}
+
+	function IsValidURL($url, $allowUserPass=false) {
+		if ($url == '') {
+			return false;
+		}
+		if ($allowUserPass !== true) {
+			if (strstr($url, '@')) {
+				// in the format http://user:pass@example.com  or http://user@example.com
+				// but could easily be somebody incorrectly entering an email address in place of a URL
+				return false;
+			}
+		}
+		if ($parts = $this->safe_parse_url($url)) {
+			if (($parts['scheme'] != 'http') && ($parts['scheme'] != 'https') && ($parts['scheme'] != 'ftp') && ($parts['scheme'] != 'gopher')) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]]([-.]?[0-9a-z])*\.[a-z]{2,3}$", $parts['host'], $regs) && !IsValidDottedIP($parts['host'])) {
+				return false;
+			} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['user'], $regs)) {
+				return false;
+			} elseif (!eregi("^([[:alnum:]-]|[\_])*$", $parts['pass'], $regs)) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]/_\.@~-]*$", $parts['path'], $regs)) {
+				return false;
+			} elseif (!eregi("^[[:alnum:]?&=+:;_()%#/,\.-]*$", $parts['query'], $regs)) {
+				return false;
+			} else {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	function ID3v2ShortFrameNameLookup($majorversion, $long_description) {
+		$long_description = str_replace(' ', '_', strtolower(trim($long_description)));
+		static $ID3v2ShortFrameNameLookup = array();
+		if (empty($ID3v2ShortFrameNameLookup)) {
+
+			// The following are unique to ID3v2.2
+			$ID3v2ShortFrameNameLookup[2]['comment']                                          = 'COM';
+			$ID3v2ShortFrameNameLookup[2]['album']                                            = 'TAL';
+			$ID3v2ShortFrameNameLookup[2]['beats_per_minute']                                 = 'TBP';
+			$ID3v2ShortFrameNameLookup[2]['composer']                                         = 'TCM';
+			$ID3v2ShortFrameNameLookup[2]['genre']                                            = 'TCO';
+			$ID3v2ShortFrameNameLookup[2]['itunescompilation']                                = 'TCP';
+			$ID3v2ShortFrameNameLookup[2]['copyright']                                        = 'TCR';
+			$ID3v2ShortFrameNameLookup[2]['encoded_by']                                       = 'TEN';
+			$ID3v2ShortFrameNameLookup[2]['language']                                         = 'TLA';
+			$ID3v2ShortFrameNameLookup[2]['length']                                           = 'TLE';
+			$ID3v2ShortFrameNameLookup[2]['original_artist']                                  = 'TOA';
+			$ID3v2ShortFrameNameLookup[2]['original_filename']                                = 'TOF';
+			$ID3v2ShortFrameNameLookup[2]['original_lyricist']                                = 'TOL';
+			$ID3v2ShortFrameNameLookup[2]['original_album_title']                             = 'TOT';
+			$ID3v2ShortFrameNameLookup[2]['artist']                                           = 'TP1';
+			$ID3v2ShortFrameNameLookup[2]['band']                                             = 'TP2';
+			$ID3v2ShortFrameNameLookup[2]['conductor']                                        = 'TP3';
+			$ID3v2ShortFrameNameLookup[2]['remixer']                                          = 'TP4';
+			$ID3v2ShortFrameNameLookup[2]['publisher']                                        = 'TPB';
+			$ID3v2ShortFrameNameLookup[2]['isrc']                                             = 'TRC';
+			$ID3v2ShortFrameNameLookup[2]['tracknumber']                                      = 'TRK';
+			$ID3v2ShortFrameNameLookup[2]['size']                                             = 'TSI';
+			$ID3v2ShortFrameNameLookup[2]['encoder_settings']                                 = 'TSS';
+			$ID3v2ShortFrameNameLookup[2]['description']                                      = 'TT1';
+			$ID3v2ShortFrameNameLookup[2]['title']                                            = 'TT2';
+			$ID3v2ShortFrameNameLookup[2]['subtitle']                                         = 'TT3';
+			$ID3v2ShortFrameNameLookup[2]['lyricist']                                         = 'TXT';
+			$ID3v2ShortFrameNameLookup[2]['user_text']                                        = 'TXX';
+			$ID3v2ShortFrameNameLookup[2]['year']                                             = 'TYE';
+			$ID3v2ShortFrameNameLookup[2]['unique_file_identifier']                           = 'UFI';
+			$ID3v2ShortFrameNameLookup[2]['unsynchronised_lyrics']                            = 'ULT';
+			$ID3v2ShortFrameNameLookup[2]['url_file']                                         = 'WAF';
+			$ID3v2ShortFrameNameLookup[2]['url_artist']                                       = 'WAR';
+			$ID3v2ShortFrameNameLookup[2]['url_source']                                       = 'WAS';
+			$ID3v2ShortFrameNameLookup[2]['copyright_information']                            = 'WCP';
+			$ID3v2ShortFrameNameLookup[2]['url_publisher']                                    = 'WPB';
+			$ID3v2ShortFrameNameLookup[2]['url_user']                                         = 'WXX';
+
+			// The following are common to ID3v2.3 and ID3v2.4
+			$ID3v2ShortFrameNameLookup[3]['audio_encryption']                                 = 'AENC';
+			$ID3v2ShortFrameNameLookup[3]['attached_picture']                                 = 'APIC';
+			$ID3v2ShortFrameNameLookup[3]['comment']                                          = 'COMM';
+			$ID3v2ShortFrameNameLookup[3]['commercial']                                       = 'COMR';
+			$ID3v2ShortFrameNameLookup[3]['encryption_method_registration']                   = 'ENCR';
+			$ID3v2ShortFrameNameLookup[3]['event_timing_codes']                               = 'ETCO';
+			$ID3v2ShortFrameNameLookup[3]['general_encapsulated_object']                      = 'GEOB';
+			$ID3v2ShortFrameNameLookup[3]['group_identification_registration']                = 'GRID';
+			$ID3v2ShortFrameNameLookup[3]['linked_information']                               = 'LINK';
+			$ID3v2ShortFrameNameLookup[3]['music_cd_identifier']                              = 'MCDI';
+			$ID3v2ShortFrameNameLookup[3]['mpeg_location_lookup_table']                       = 'MLLT';
+			$ID3v2ShortFrameNameLookup[3]['ownership']                                        = 'OWNE';
+			$ID3v2ShortFrameNameLookup[3]['play_counter']                                     = 'PCNT';
+			$ID3v2ShortFrameNameLookup[3]['popularimeter']                                    = 'POPM';
+			$ID3v2ShortFrameNameLookup[3]['position_synchronisation']                         = 'POSS';
+			$ID3v2ShortFrameNameLookup[3]['private']                                          = 'PRIV';
+			$ID3v2ShortFrameNameLookup[3]['recommended_buffer_size']                          = 'RBUF';
+			$ID3v2ShortFrameNameLookup[3]['reverb']                                           = 'RVRB';
+			$ID3v2ShortFrameNameLookup[3]['synchronised_lyrics']                              = 'SYLT';
+			$ID3v2ShortFrameNameLookup[3]['synchronised_tempo_codes']                         = 'SYTC';
+			$ID3v2ShortFrameNameLookup[3]['album']                                            = 'TALB';
+			$ID3v2ShortFrameNameLookup[3]['beats_per_minute']                                 = 'TBPM';
+			$ID3v2ShortFrameNameLookup[3]['itunescompilation']                                = 'TCMP';
+			$ID3v2ShortFrameNameLookup[3]['composer']                                         = 'TCOM';
+			$ID3v2ShortFrameNameLookup[3]['genre']                                            = 'TCON';
+			$ID3v2ShortFrameNameLookup[3]['copyright']                                        = 'TCOP';
+			$ID3v2ShortFrameNameLookup[3]['playlist_delay']                                   = 'TDLY';
+			$ID3v2ShortFrameNameLookup[3]['encoded_by']                                       = 'TENC';
+			$ID3v2ShortFrameNameLookup[3]['lyricist']                                         = 'TEXT';
+			$ID3v2ShortFrameNameLookup[3]['file_type']                                        = 'TFLT';
+			$ID3v2ShortFrameNameLookup[3]['content_group_description']                        = 'TIT1';
+			$ID3v2ShortFrameNameLookup[3]['title']                                            = 'TIT2';
+			$ID3v2ShortFrameNameLookup[3]['subtitle']                                         = 'TIT3';
+			$ID3v2ShortFrameNameLookup[3]['initial_key']                                      = 'TKEY';
+			$ID3v2ShortFrameNameLookup[3]['language']                                         = 'TLAN';
+			$ID3v2ShortFrameNameLookup[3]['length']                                           = 'TLEN';
+			$ID3v2ShortFrameNameLookup[3]['media_type']                                       = 'TMED';
+			$ID3v2ShortFrameNameLookup[3]['original_album_title']                             = 'TOAL';
+			$ID3v2ShortFrameNameLookup[3]['original_filename']                                = 'TOFN';
+			$ID3v2ShortFrameNameLookup[3]['original_lyricist']                                = 'TOLY';
+			$ID3v2ShortFrameNameLookup[3]['original_artist']                                  = 'TOPE';
+			$ID3v2ShortFrameNameLookup[3]['file_owner']                                       = 'TOWN';
+			$ID3v2ShortFrameNameLookup[3]['artist']                                           = 'TPE1';
+			$ID3v2ShortFrameNameLookup[3]['band']                                             = 'TPE2';
+			$ID3v2ShortFrameNameLookup[3]['conductor']                                        = 'TPE3';
+			$ID3v2ShortFrameNameLookup[3]['remixer']                                          = 'TPE4';
+			$ID3v2ShortFrameNameLookup[3]['part_of_a_set']                                    = 'TPOS';
+			$ID3v2ShortFrameNameLookup[3]['publisher']                                        = 'TPUB';
+			$ID3v2ShortFrameNameLookup[3]['tracknumber']                                      = 'TRCK';
+			$ID3v2ShortFrameNameLookup[3]['internet_radio_station_name']                      = 'TRSN';
+			$ID3v2ShortFrameNameLookup[3]['internet_radio_station_owner']                     = 'TRSO';
+			$ID3v2ShortFrameNameLookup[3]['isrc']                                             = 'TSRC';
+			$ID3v2ShortFrameNameLookup[3]['encoder_settings']                                 = 'TSSE';
+			$ID3v2ShortFrameNameLookup[3]['user_text']                                        = 'TXXX';
+			$ID3v2ShortFrameNameLookup[3]['unique_file_identifier']                           = 'UFID';
+			$ID3v2ShortFrameNameLookup[3]['terms_of_use']                                     = 'USER';
+			$ID3v2ShortFrameNameLookup[3]['unsynchronised_lyrics']                            = 'USLT';
+			$ID3v2ShortFrameNameLookup[3]['commercial']                                       = 'WCOM';
+			$ID3v2ShortFrameNameLookup[3]['copyright_information']                            = 'WCOP';
+			$ID3v2ShortFrameNameLookup[3]['url_file']                                         = 'WOAF';
+			$ID3v2ShortFrameNameLookup[3]['url_artist']                                       = 'WOAR';
+			$ID3v2ShortFrameNameLookup[3]['url_source']                                       = 'WOAS';
+			$ID3v2ShortFrameNameLookup[3]['url_station']                                      = 'WORS';
+			$ID3v2ShortFrameNameLookup[3]['payment']                                          = 'WPAY';
+			$ID3v2ShortFrameNameLookup[3]['url_publisher']                                    = 'WPUB';
+			$ID3v2ShortFrameNameLookup[3]['url_user']                                         = 'WXXX';
+
+			// The above are common to ID3v2.3 and ID3v2.4
+			// so copy them to ID3v2.4 before adding specifics for 2.3 and 2.4
+			$ID3v2ShortFrameNameLookup[4] = $ID3v2ShortFrameNameLookup[3];
+
+			// The following are unique to ID3v2.3
+			$ID3v2ShortFrameNameLookup[3]['equalisation']                                     = 'EQUA';
+			$ID3v2ShortFrameNameLookup[3]['involved_people_list']                             = 'IPLS';
+			$ID3v2ShortFrameNameLookup[3]['relative_volume_adjustment']                       = 'RVAD';
+			$ID3v2ShortFrameNameLookup[3]['date']                                             = 'TDAT';
+			$ID3v2ShortFrameNameLookup[3]['time']                                             = 'TIME';
+			$ID3v2ShortFrameNameLookup[3]['original_release_year']                            = 'TORY';
+			$ID3v2ShortFrameNameLookup[3]['recording_dates']                                  = 'TRDA';
+			$ID3v2ShortFrameNameLookup[3]['size']                                             = 'TSIZ';
+			$ID3v2ShortFrameNameLookup[3]['year']                                             = 'TYER';
+
+
+			// The following are unique to ID3v2.4
+			$ID3v2ShortFrameNameLookup[4]['audio_seek_point_index']                           = 'ASPI';
+			$ID3v2ShortFrameNameLookup[4]['equalisation']                                     = 'EQU2';
+			$ID3v2ShortFrameNameLookup[4]['relative_volume_adjustment']                       = 'RVA2';
+			$ID3v2ShortFrameNameLookup[4]['seek']                                             = 'SEEK';
+			$ID3v2ShortFrameNameLookup[4]['signature']                                        = 'SIGN';
+			$ID3v2ShortFrameNameLookup[4]['encoding_time']                                    = 'TDEN';
+			$ID3v2ShortFrameNameLookup[4]['original_release_time']                            = 'TDOR';
+			$ID3v2ShortFrameNameLookup[4]['recording_time']                                   = 'TDRC';
+			$ID3v2ShortFrameNameLookup[4]['release_time']                                     = 'TDRL';
+			$ID3v2ShortFrameNameLookup[4]['tagging_time']                                     = 'TDTG';
+			$ID3v2ShortFrameNameLookup[4]['involved_people_list']                             = 'TIPL';
+			$ID3v2ShortFrameNameLookup[4]['musician_credits_list']                            = 'TMCL';
+			$ID3v2ShortFrameNameLookup[4]['mood']                                             = 'TMOO';
+			$ID3v2ShortFrameNameLookup[4]['produced_notice']                                  = 'TPRO';
+			$ID3v2ShortFrameNameLookup[4]['album_sort_order']                                 = 'TSOA';
+			$ID3v2ShortFrameNameLookup[4]['performer_sort_order']                             = 'TSOP';
+			$ID3v2ShortFrameNameLookup[4]['title_sort_order']                                 = 'TSOT';
+			$ID3v2ShortFrameNameLookup[4]['set_subtitle']                                     = 'TSST';
+		}
+		return @$ID3v2ShortFrameNameLookup[$majorversion][strtolower($long_description)];
+
+	}
+
+}
+
+?>
diff --git a/apps/media/getID3/getid3/write.lyrics3.php b/apps/media/getID3/getid3/write.lyrics3.php
new file mode 100644
index 0000000000000000000000000000000000000000..6b8a47d6a1951a7f05ef6de06c0d632a8f733925
--- /dev/null
+++ b/apps/media/getID3/getid3/write.lyrics3.php
@@ -0,0 +1,78 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.lyrics3.php                                           //
+// module for writing Lyrics3 tags                             //
+// dependencies: module.tag.lyrics3.php                        //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_write_lyrics3
+{
+	var $filename;
+	var $tag_data;
+	//var $lyrics3_version = 2;       // 1 or 2
+	var $warnings        = array(); // any non-critical errors will be stored here
+	var $errors          = array(); // any critical errors will be stored here
+
+	function getid3_write_lyrics3() {
+		return true;
+	}
+
+	function WriteLyrics3() {
+		$this->errors[] = 'WriteLyrics3() not yet functional - cannot write Lyrics3';
+		return false;
+	}
+
+	function DeleteLyrics3() {
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+		$ThisFileInfo = $getID3->analyze($this->filename);
+		if (isset($ThisFileInfo['lyrics3']['tag_offset_start']) && isset($ThisFileInfo['lyrics3']['tag_offset_end'])) {
+			if ($fp = @fopen($this->filename, 'a+b')) {
+
+				flock($fp, LOCK_EX);
+				$oldignoreuserabort = ignore_user_abort(true);
+
+				fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_end'], SEEK_SET);
+				$DataAfterLyrics3 = '';
+				if ($ThisFileInfo['filesize'] > $ThisFileInfo['lyrics3']['tag_offset_end']) {
+					$DataAfterLyrics3 = fread($fp, $ThisFileInfo['filesize'] - $ThisFileInfo['lyrics3']['tag_offset_end']);
+				}
+
+				ftruncate($fp, $ThisFileInfo['lyrics3']['tag_offset_start']);
+
+				if (!empty($DataAfterLyrics3)) {
+					fseek($fp, $ThisFileInfo['lyrics3']['tag_offset_start'], SEEK_SET);
+					fwrite($fp, $DataAfterLyrics3, strlen($DataAfterLyrics3));
+				}
+
+				flock($fp, LOCK_UN);
+				fclose($fp);
+				ignore_user_abort($oldignoreuserabort);
+
+				return true;
+
+			} else {
+
+				$this->errors[] = 'Cannot open "'.$this->filename.'" in "a+b" mode';
+				return false;
+
+			}
+		}
+		// no Lyrics3 present
+		return true;
+	}
+
+
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.metaflac.php b/apps/media/getID3/getid3/write.metaflac.php
new file mode 100644
index 0000000000000000000000000000000000000000..c9521c838629ca6c2af078b095cbb12d2f6674a1
--- /dev/null
+++ b/apps/media/getID3/getid3/write.metaflac.php
@@ -0,0 +1,167 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.metaflac.php                                          //
+// module for writing metaflac tags                            //
+// dependencies: /helperapps/metaflac.exe                      //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_write_metaflac
+{
+
+	var $filename;
+	var $tag_data;
+	var $warnings = array(); // any non-critical errors will be stored here
+	var $errors   = array(); // any critical errors will be stored here
+
+	function getid3_write_metaflac() {
+		return true;
+	}
+
+	function WriteMetaFLAC() {
+
+		if (!ini_get('safe_mode')) {
+
+			// Create file with new comments
+			$tempcommentsfilename = tempnam('*', 'getID3');
+			if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) {
+
+				foreach ($this->tag_data as $key => $value) {
+					foreach ($value as $commentdata) {
+						fwrite($fpcomments, $this->CleanmetaflacName($key).'='.$commentdata."\n");
+					}
+				}
+				fclose($fpcomments);
+
+			} else {
+
+				$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
+				return false;
+
+			}
+
+			$oldignoreuserabort = ignore_user_abort(true);
+			if (GETID3_OS_ISWINDOWS) {
+
+				if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
+					//$commandline = '"'.GETID3_HELPERAPPSDIR.'metaflac.exe" --no-utf8-convert --remove-all-tags --import-tags-from="'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
+					//  metaflac works fine if you copy-paste the above commandline into a command prompt,
+					//  but refuses to work with `backtick` if there are "doublequotes" present around BOTH
+					//  the metaflac pathname and the target filename. For whatever reason...??
+					//  The solution is simply ensure that the metaflac pathname has no spaces,
+					//  and therefore does not need to be quoted
+
+					// On top of that, if error messages are not always captured properly under Windows
+					// To at least see if there was a problem, compare file modification timestamps before and after writing
+					clearstatcache();
+					$timestampbeforewriting = filemtime($this->filename);
+
+					$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --no-utf8-convert --remove-all-tags --import-tags-from="'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
+					$metaflacError = `$commandline`;
+
+					if (empty($metaflacError)) {
+						clearstatcache();
+						if ($timestampbeforewriting == filemtime($this->filename)) {
+							$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not written';
+						}
+					}
+				} else {
+					$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
+				}
+
+			} else {
+
+				// It's simpler on *nix
+				$commandline = 'metaflac --no-utf8-convert --remove-all-tags --import-tags-from='.$tempcommentsfilename.' "'.$this->filename.'" 2>&1';
+				$metaflacError = `$commandline`;
+
+			}
+
+			// Remove temporary comments file
+			unlink($tempcommentsfilename);
+			ignore_user_abort($oldignoreuserabort);
+
+			if (!empty($metaflacError)) {
+
+				$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
+				return false;
+
+			}
+
+			return true;
+		}
+
+		$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not written';
+		return false;
+	}
+
+
+	function DeleteMetaFLAC() {
+
+		if (!ini_get('safe_mode')) {
+
+			$oldignoreuserabort = ignore_user_abort(true);
+			if (GETID3_OS_ISWINDOWS) {
+
+				if (file_exists(GETID3_HELPERAPPSDIR.'metaflac.exe')) {
+					// To at least see if there was a problem, compare file modification timestamps before and after writing
+					clearstatcache();
+					$timestampbeforewriting = filemtime($this->filename);
+
+					$commandline = GETID3_HELPERAPPSDIR.'metaflac.exe --remove-all-tags "'.$this->filename.'" 2>&1';
+					$metaflacError = `$commandline`;
+
+					if (empty($metaflacError)) {
+						clearstatcache();
+						if ($timestampbeforewriting == filemtime($this->filename)) {
+							$metaflacError = 'File modification timestamp has not changed - it looks like the tags were not deleted';
+						}
+					}
+				} else {
+					$metaflacError = 'metaflac.exe not found in '.GETID3_HELPERAPPSDIR;
+				}
+
+			} else {
+
+				// It's simpler on *nix
+				$commandline = 'metaflac --remove-all-tags "'.$this->filename.'" 2>&1';
+				$metaflacError = `$commandline`;
+
+			}
+
+			ignore_user_abort($oldignoreuserabort);
+
+			if (!empty($metaflacError)) {
+				$this->errors[] = 'System call to metaflac failed with this message returned: '."\n\n".$metaflacError;
+				return false;
+			}
+			return true;
+		}
+		$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call metaflac, tags not deleted';
+		return false;
+	}
+
+
+	function CleanmetaflacName($originalcommentname) {
+		// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
+		// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
+		// 0x7A inclusive (a-z).
+
+		// replace invalid chars with a space, return uppercase text
+		// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
+		// note: ereg_replace() replaces nulls with empty string (not space)
+		return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.php b/apps/media/getID3/getid3/write.php
new file mode 100644
index 0000000000000000000000000000000000000000..73e261036f288f7ea1de49dc80abde50b5304b09
--- /dev/null
+++ b/apps/media/getID3/getid3/write.php
@@ -0,0 +1,592 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+///                                                            //
+// write.php                                                   //
+// module for writing tags (APEv2, ID3v1, ID3v2)               //
+// dependencies: getid3.lib.php                                //
+//               write.apetag.php (optional)                   //
+//               write.id3v1.php (optional)                    //
+//               write.id3v2.php (optional)                    //
+//               write.vorbiscomment.php (optional)            //
+//               write.metaflac.php (optional)                 //
+//               write.lyrics3.php (optional)                  //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+if (!defined('GETID3_INCLUDEPATH')) {
+	die('getid3.php MUST be included before calling getid3_writetags');
+}
+if (!include_once(GETID3_INCLUDEPATH.'getid3.lib.php')) {
+	die('write.php depends on getid3.lib.php, which is missing.');
+}
+
+
+// NOTES:
+//
+// You should pass data here with standard field names as follows:
+// * TITLE
+// * ARTIST
+// * ALBUM
+// * TRACKNUMBER
+// * COMMENT
+// * GENRE
+// * YEAR
+// * ATTACHED_PICTURE (ID3v2 only)
+//
+// http://www.personal.uni-jena.de/~pfk/mpp/sv8/apekey.html
+// The APEv2 Tag Items Keys definition says "TRACK" is correct but foobar2000 uses "TRACKNUMBER" instead
+// Pass data here as "TRACKNUMBER" for compatability with all formats
+
+
+class getid3_writetags
+{
+	// public
+	var $filename;                            // absolute filename of file to write tags to
+	var $tagformats         = array();        // array of tag formats to write ('id3v1', 'id3v2.2', 'id2v2.3', 'id3v2.4', 'ape', 'vorbiscomment', 'metaflac', 'real')
+	var $tag_data           = array(array()); // 2-dimensional array of tag data (ex: $data['ARTIST'][0] = 'Elvis')
+	var $tag_encoding       = 'ISO-8859-1';   // text encoding used for tag data ('ISO-8859-1', 'UTF-8', 'UTF-16', 'UTF-16LE', 'UTF-16BE', )
+	var $overwrite_tags     = true;           // if true will erase existing tag data and write only passed data; if false will merge passed data with existing tag data
+	var $remove_other_tags  = false;          // if true will erase remove all existing tags and only write those passed in $tagformats; if false will ignore any tags not mentioned in $tagformats
+
+	var $id3v2_tag_language = 'eng';          // ISO-639-2 3-character language code needed for some ID3v2 frames (http://www.id3.org/iso639-2.html)
+	var $id3v2_paddedlength = 4096;           // minimum length of ID3v2 tags (will be padded to this length if tag data is shorter)
+
+	var $warnings           = array();        // any non-critical errors will be stored here
+	var $errors             = array();        // any critical errors will be stored here
+
+	// private
+	var $ThisFileInfo; // analysis of file before writing
+
+	function getid3_writetags() {
+		return true;
+	}
+
+
+	function WriteTags() {
+
+		if (empty($this->filename)) {
+			$this->errors[] = 'filename is undefined in getid3_writetags';
+			return false;
+		} elseif (!file_exists($this->filename)) {
+			$this->errors[] = 'filename set to non-existant file "'.$this->filename.'" in getid3_writetags';
+			return false;
+		}
+
+		if (!is_array($this->tagformats)) {
+			$this->errors[] = 'tagformats must be an array in getid3_writetags';
+			return false;
+		}
+
+		$TagFormatsToRemove = array();
+		if (filesize($this->filename) == 0) {
+
+			// empty file special case - allow any tag format, don't check existing format
+			// could be useful if you want to generate tag data for a non-existant file
+			$this->ThisFileInfo = array('fileformat'=>'');
+			$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
+
+		} else {
+
+			$getID3 = new getID3;
+			$getID3->encoding = $this->tag_encoding;
+			$this->ThisFileInfo = $getID3->analyze($this->filename);
+
+			// check for what file types are allowed on this fileformat
+			switch (@$this->ThisFileInfo['fileformat']) {
+				case 'mp3':
+				case 'mp2':
+				case 'mp1':
+				case 'riff': // maybe not officially, but people do it anyway
+					$AllowedTagFormats = array('id3v1', 'id3v2.2', 'id3v2.3', 'id3v2.4', 'ape', 'lyrics3');
+					break;
+
+				case 'mpc':
+					$AllowedTagFormats = array('ape');
+					break;
+
+				case 'flac':
+					$AllowedTagFormats = array('metaflac');
+					break;
+
+				case 'real':
+					$AllowedTagFormats = array('real');
+					break;
+
+				case 'ogg':
+					switch (@$this->ThisFileInfo['audio']['dataformat']) {
+						case 'flac':
+							//$AllowedTagFormats = array('metaflac');
+							$this->errors[] = 'metaflac is not (yet) compatible with OggFLAC files';
+							return false;
+							break;
+						case 'vorbis':
+							$AllowedTagFormats = array('vorbiscomment');
+							break;
+						default:
+							$this->errors[] = 'metaflac is not (yet) compatible with Ogg files other than OggVorbis';
+							return false;
+							break;
+					}
+					break;
+
+				default:
+					$AllowedTagFormats = array();
+					break;
+			}
+			foreach ($this->tagformats as $requested_tag_format) {
+				if (!in_array($requested_tag_format, $AllowedTagFormats)) {
+					$errormessage = 'Tag format "'.$requested_tag_format.'" is not allowed on "'.@$this->ThisFileInfo['fileformat'];
+					if (@$this->ThisFileInfo['fileformat'] != @$this->ThisFileInfo['audio']['dataformat']) {
+						$errormessage .= '.'.@$this->ThisFileInfo['audio']['dataformat'];
+					}
+					$errormessage .= '" files';
+					$this->errors[] = $errormessage;
+					return false;
+				}
+			}
+
+			// List of other tag formats, removed if requested
+			if ($this->remove_other_tags) {
+				foreach ($AllowedTagFormats as $AllowedTagFormat) {
+					switch ($AllowedTagFormat) {
+						case 'id3v2.2':
+						case 'id3v2.3':
+						case 'id3v2.4':
+							if (!in_array('id3v2', $TagFormatsToRemove) && !in_array('id3v2.2', $this->tagformats) && !in_array('id3v2.3', $this->tagformats) && !in_array('id3v2.4', $this->tagformats)) {
+								$TagFormatsToRemove[] = 'id3v2';
+							}
+							break;
+
+						default:
+							if (!in_array($AllowedTagFormat, $this->tagformats)) {
+								$TagFormatsToRemove[] = $AllowedTagFormat;
+							}
+							break;
+					}
+				}
+			}
+		}
+
+		$WritingFilesToInclude = array_merge($this->tagformats, $TagFormatsToRemove);
+
+		// Check for required include files and include them
+		foreach ($WritingFilesToInclude as $tagformat) {
+			switch ($tagformat) {
+				case 'ape':
+					$GETID3_ERRORARRAY = &$this->errors;
+					if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.apetag.php', __FILE__, false)) {
+						return false;
+					}
+					break;
+
+				case 'id3v1':
+				case 'lyrics3':
+				case 'vorbiscomment':
+				case 'metaflac':
+				case 'real':
+					$GETID3_ERRORARRAY = &$this->errors;
+					if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.'.$tagformat.'.php', __FILE__, false)) {
+						return false;
+					}
+					break;
+
+				case 'id3v2.2':
+				case 'id3v2.3':
+				case 'id3v2.4':
+				case 'id3v2':
+					$GETID3_ERRORARRAY = &$this->errors;
+					if (!getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'write.id3v2.php', __FILE__, false)) {
+						return false;
+					}
+					break;
+
+				default:
+					$this->errors[] = 'unknown tag format "'.$tagformat.'" in $tagformats in WriteTags()';
+					return false;
+					break;
+			}
+
+		}
+
+		// Validation of supplied data
+		if (!is_array($this->tag_data)) {
+			$this->errors[] = '$tag_data is not an array in WriteTags()';
+			return false;
+		}
+		// convert supplied data array keys to upper case, if they're not already
+		foreach ($this->tag_data as $tag_key => $tag_array) {
+			if (strtoupper($tag_key) !== $tag_key) {
+				$this->tag_data[strtoupper($tag_key)] = $this->tag_data[$tag_key];
+				unset($this->tag_data[$tag_key]);
+			}
+		}
+		// convert source data array keys to upper case, if they're not already
+		if (!empty($this->ThisFileInfo['tags'])) {
+			foreach ($this->ThisFileInfo['tags'] as $tag_format => $tag_data_array) {
+				foreach ($tag_data_array as $tag_key => $tag_array) {
+					if (strtoupper($tag_key) !== $tag_key) {
+						$this->ThisFileInfo['tags'][$tag_format][strtoupper($tag_key)] = $this->ThisFileInfo['tags'][$tag_format][$tag_key];
+						unset($this->ThisFileInfo['tags'][$tag_format][$tag_key]);
+					}
+				}
+			}
+		}
+
+		// Convert "TRACK" to "TRACKNUMBER" (if needed) for compatability with all formats
+		if (isset($this->tag_data['TRACK']) && !isset($this->tag_data['TRACKNUMBER'])) {
+			$this->tag_data['TRACKNUMBER'] = $this->tag_data['TRACK'];
+			unset($this->tag_data['TRACK']);
+		}
+
+		// Remove all other tag formats, if requested
+		if ($this->remove_other_tags) {
+			$this->DeleteTags($TagFormatsToRemove);
+		}
+
+		// Write data for each tag format
+		foreach ($this->tagformats as $tagformat) {
+			$success = false; // overridden if tag writing is successful
+			switch ($tagformat) {
+				case 'ape':
+					$ape_writer = new getid3_write_apetag;
+					if (($ape_writer->tag_data = $this->FormatDataForAPE()) !== false) {
+						$ape_writer->filename = $this->filename;
+						if (($success = $ape_writer->WriteAPEtag()) === false) {
+							$this->errors[] = 'WriteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForAPE() failed';
+					}
+					break;
+
+				case 'id3v1':
+					$id3v1_writer = new getid3_write_id3v1;
+					if (($id3v1_writer->tag_data = $this->FormatDataForID3v1()) !== false) {
+						$id3v1_writer->filename = $this->filename;
+						if (($success = $id3v1_writer->WriteID3v1()) === false) {
+							$this->errors[] = 'WriteID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForID3v1() failed';
+					}
+					break;
+
+				case 'id3v2.2':
+				case 'id3v2.3':
+				case 'id3v2.4':
+					$id3v2_writer = new getid3_write_id3v2;
+					$id3v2_writer->majorversion = intval(substr($tagformat, -1));
+					$id3v2_writer->paddedlength = $this->id3v2_paddedlength;
+					if (($id3v2_writer->tag_data = $this->FormatDataForID3v2($id3v2_writer->majorversion)) !== false) {
+						$id3v2_writer->filename = $this->filename;
+						if (($success = $id3v2_writer->WriteID3v2()) === false) {
+							$this->errors[] = 'WriteID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForID3v2() failed';
+					}
+					break;
+
+				case 'vorbiscomment':
+					$vorbiscomment_writer = new getid3_write_vorbiscomment;
+					if (($vorbiscomment_writer->tag_data = $this->FormatDataForVorbisComment()) !== false) {
+						$vorbiscomment_writer->filename = $this->filename;
+						if (($success = $vorbiscomment_writer->WriteVorbisComment()) === false) {
+							$this->errors[] = 'WriteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForVorbisComment() failed';
+					}
+					break;
+
+				case 'metaflac':
+					$metaflac_writer = new getid3_write_metaflac;
+					if (($metaflac_writer->tag_data = $this->FormatDataForMetaFLAC()) !== false) {
+						$metaflac_writer->filename = $this->filename;
+						if (($success = $metaflac_writer->WriteMetaFLAC()) === false) {
+							$this->errors[] = 'WriteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForMetaFLAC() failed';
+					}
+					break;
+
+				case 'real':
+					$real_writer = new getid3_write_real;
+					if (($real_writer->tag_data = $this->FormatDataForReal()) !== false) {
+						$real_writer->filename = $this->filename;
+						if (($success = $real_writer->WriteReal()) === false) {
+							$this->errors[] = 'WriteReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
+						}
+					} else {
+						$this->errors[] = 'FormatDataForReal() failed';
+					}
+					break;
+
+				default:
+					$this->errors[] = 'Invalid tag format to write: "'.$tagformat.'"';
+					return false;
+					break;
+			}
+			if (!$success) {
+				return false;
+			}
+		}
+		return true;
+
+	}
+
+
+	function DeleteTags($TagFormatsToDelete) {
+		foreach ($TagFormatsToDelete as $DeleteTagFormat) {
+			$success = false; // overridden if tag deletion is successful
+			switch ($DeleteTagFormat) {
+				case 'id3v1':
+					$id3v1_writer = new getid3_write_id3v1;
+					$id3v1_writer->filename = $this->filename;
+					if (($success = $id3v1_writer->RemoveID3v1()) === false) {
+						$this->errors[] = 'RemoveID3v1() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v1_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'id3v2':
+					$id3v2_writer = new getid3_write_id3v2;
+					$id3v2_writer->filename = $this->filename;
+					if (($success = $id3v2_writer->RemoveID3v2()) === false) {
+						$this->errors[] = 'RemoveID3v2() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $id3v2_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'ape':
+					$ape_writer = new getid3_write_apetag;
+					$ape_writer->filename = $this->filename;
+					if (($success = $ape_writer->DeleteAPEtag()) === false) {
+						$this->errors[] = 'DeleteAPEtag() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $ape_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'vorbiscomment':
+					$vorbiscomment_writer = new getid3_write_vorbiscomment;
+					$vorbiscomment_writer->filename = $this->filename;
+					if (($success = $vorbiscomment_writer->DeleteVorbisComment()) === false) {
+						$this->errors[] = 'DeleteVorbisComment() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $vorbiscomment_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'metaflac':
+					$metaflac_writer = new getid3_write_metaflac;
+					$metaflac_writer->filename = $this->filename;
+					if (($success = $metaflac_writer->DeleteMetaFLAC()) === false) {
+						$this->errors[] = 'DeleteMetaFLAC() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $metaflac_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'lyrics3':
+					$lyrics3_writer = new getid3_write_lyrics3;
+					$lyrics3_writer->filename = $this->filename;
+					if (($success = $lyrics3_writer->DeleteLyrics3()) === false) {
+						$this->errors[] = 'DeleteLyrics3() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $lyrics3_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				case 'real':
+					$real_writer = new getid3_write_real;
+					$real_writer->filename = $this->filename;
+					if (($success = $real_writer->RemoveReal()) === false) {
+						$this->errors[] = 'RemoveReal() failed with message(s):<PRE><UL><LI>'.trim(implode('</LI><LI>', $real_writer->errors)).'</LI></UL></PRE>';
+					}
+					break;
+
+				default:
+					$this->errors[] = 'Invalid tag format to delete: "'.$tagformat.'"';
+					return false;
+					break;
+			}
+			if (!$success) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+
+	function MergeExistingTagData($TagFormat, &$tag_data) {
+		// Merge supplied data with existing data, if requested
+		if ($this->overwrite_tags) {
+			// do nothing - ignore previous data
+		} else {
+			if (!isset($this->ThisFileInfo['tags'][$TagFormat])) {
+				return false;
+			}
+			$tag_data = array_merge_recursive($tag_data, $this->ThisFileInfo['tags'][$TagFormat]);
+		}
+		return true;
+	}
+
+	function FormatDataForAPE() {
+		$ape_tag_data = array();
+		foreach ($this->tag_data as $tag_key => $valuearray) {
+			switch ($tag_key) {
+				case 'ATTACHED_PICTURE':
+					// ATTACHED_PICTURE is ID3v2 only - ignore
+					$this->warnings[] = '$data['.$tag_key.'] is assumed to be ID3v2 APIC data - NOT written to APE tag';
+					break;
+
+				default:
+					foreach ($valuearray as $key => $value) {
+						if (is_string($value) || is_numeric($value)) {
+							$ape_tag_data[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
+						} else {
+							$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to APE tag';
+							unset($ape_tag_data[$tag_key]);
+							break;
+						}
+					}
+					break;
+			}
+		}
+		$this->MergeExistingTagData('ape', $ape_tag_data);
+		return $ape_tag_data;
+	}
+
+
+	function FormatDataForID3v1() {
+		$tag_data_id3v1['genreid'] = 255;
+		if (!empty($this->tag_data['GENRE'])) {
+			foreach ($this->tag_data['GENRE'] as $key => $value) {
+				if (getid3_id3v1::LookupGenreID($value) !== false) {
+					$tag_data_id3v1['genreid'] = getid3_id3v1::LookupGenreID($value);
+					break;
+				}
+			}
+		}
+
+		$tag_data_id3v1['title']   = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
+		$tag_data_id3v1['artist']  = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
+		$tag_data_id3v1['album']   = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ALBUM']));
+		$tag_data_id3v1['year']    = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['YEAR']));
+		$tag_data_id3v1['comment'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
+
+		$tag_data_id3v1['track']   = intval(getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TRACKNUMBER'])));
+		if ($tag_data_id3v1['track'] <= 0) {
+			$tag_data_id3v1['track'] = '';
+		}
+
+		$this->MergeExistingTagData('id3v1', $tag_data_id3v1);
+		return $tag_data_id3v1;
+	}
+
+	function FormatDataForID3v2($id3v2_majorversion) {
+		$tag_data_id3v2 = array();
+
+		$ID3v2_text_encoding_lookup[2] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
+		$ID3v2_text_encoding_lookup[3] = array('ISO-8859-1'=>0, 'UTF-16'=>1);
+		$ID3v2_text_encoding_lookup[4] = array('ISO-8859-1'=>0, 'UTF-16'=>1, 'UTF-16BE'=>2, 'UTF-8'=>3);
+		foreach ($this->tag_data as $tag_key => $valuearray) {
+			$ID3v2_framename = getid3_write_id3v2::ID3v2ShortFrameNameLookup($id3v2_majorversion, $tag_key);
+			switch ($ID3v2_framename) {
+				case 'APIC':
+					foreach ($valuearray as $key => $apic_data_array) {
+						if (isset($apic_data_array['data']) &&
+							isset($apic_data_array['picturetypeid']) &&
+							isset($apic_data_array['description']) &&
+							isset($apic_data_array['mime'])) {
+								$tag_data_id3v2['APIC'][] = $apic_data_array;
+						} else {
+							$this->errors[] = 'ID3v2 APIC data is not properly structured';
+							return false;
+						}
+					}
+					break;
+
+				case '':
+					$this->errors[] = 'ID3v2: Skipping "'.$tag_key.'" because cannot match it to a known ID3v2 frame type';
+					// some other data type, don't know how to handle it, ignore it
+					break;
+
+				default:
+					// most other (text) frames can be copied over as-is
+					foreach ($valuearray as $key => $value) {
+						if (isset($ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding])) {
+							// source encoding is valid in ID3v2 - use it with no conversion
+							$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = $ID3v2_text_encoding_lookup[$id3v2_majorversion][$this->tag_encoding];
+							$tag_data_id3v2[$ID3v2_framename][$key]['data']       = $value;
+						} else {
+							// source encoding is NOT valid in ID3v2 - convert it to an ID3v2-valid encoding first
+							if ($id3v2_majorversion < 4) {
+								// convert data from other encoding to UTF-16
+								$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 1;
+								$tag_data_id3v2[$ID3v2_framename][$key]['data']       = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-16', $value);
+
+							} else {
+								// convert data from other encoding to UTF-8
+								$tag_data_id3v2[$ID3v2_framename][$key]['encodingid'] = 3;
+								$tag_data_id3v2[$ID3v2_framename][$key]['data']       = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
+							}
+						}
+
+						// These values are not needed for all frame types, but if they're not used no matter
+						$tag_data_id3v2[$ID3v2_framename][$key]['description'] = '';
+						$tag_data_id3v2[$ID3v2_framename][$key]['language']    = $this->id3v2_tag_language;
+					}
+					break;
+			}
+		}
+		$this->MergeExistingTagData('id3v2', $tag_data_id3v2);
+		return $tag_data_id3v2;
+	}
+
+	function FormatDataForVorbisComment() {
+		$tag_data_vorbiscomment = $this->tag_data;
+
+		// check for multi-line comment values - split out to multiple comments if neccesary
+		// and convert data to UTF-8 strings
+		foreach ($tag_data_vorbiscomment as $tag_key => $valuearray) {
+			foreach ($valuearray as $key => $value) {
+				str_replace("\r", "\n", $value);
+				if (strstr($value, "\n")) {
+					unset($tag_data_vorbiscomment[$tag_key][$key]);
+					$multilineexploded = explode("\n", $value);
+					foreach ($multilineexploded as $newcomment) {
+						if (strlen(trim($newcomment)) > 0) {
+							$tag_data_vorbiscomment[$tag_key][] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $newcomment);
+						}
+					}
+				} elseif (is_string($value) || is_numeric($value)) {
+					$tag_data_vorbiscomment[$tag_key][$key] = getid3_lib::iconv_fallback($this->tag_encoding, 'UTF-8', $value);
+				} else {
+					$this->warnings[] = '$data['.$tag_key.']['.$key.'] is not a string value - all of $data['.$tag_key.'] NOT written to VorbisComment tag';
+					unset($tag_data_vorbiscomment[$tag_key]);
+					break;
+				}
+			}
+		}
+		$this->MergeExistingTagData('vorbiscomment', $tag_data_vorbiscomment);
+		return $tag_data_vorbiscomment;
+	}
+
+	function FormatDataForMetaFLAC() {
+		// FLAC & OggFLAC use VorbisComments same as OggVorbis
+		// but require metaflac to do the writing rather than vorbiscomment
+		return $this->FormatDataForVorbisComment();
+	}
+
+	function FormatDataForReal() {
+		$tag_data_real['title']     = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['TITLE']));
+		$tag_data_real['artist']    = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['ARTIST']));
+		$tag_data_real['copyright'] = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COPYRIGHT']));
+		$tag_data_real['comment']   = getid3_lib::iconv_fallback($this->tag_encoding, 'ISO-8859-1', @implode(' ', @$this->tag_data['COMMENT']));
+
+		$this->MergeExistingTagData('real', $tag_data_real);
+		return $tag_data_real;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.real.php b/apps/media/getID3/getid3/write.real.php
new file mode 100644
index 0000000000000000000000000000000000000000..1e0240ccf329a7ea6b003e010b6ffa71c9c4f475
--- /dev/null
+++ b/apps/media/getID3/getid3/write.real.php
@@ -0,0 +1,295 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.real.php                                              //
+// module for writing RealAudio/RealVideo tags                 //
+// dependencies: module.tag.real.php                           //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+class getid3_write_real
+{
+	var $filename;
+	var $tag_data     = array();
+	var $warnings     = array(); // any non-critical errors will be stored here
+	var $errors       = array(); // any critical errors will be stored here
+	var $paddedlength = 512;     // minimum length of CONT tag in bytes
+
+	function getid3_write_real() {
+		return true;
+	}
+
+	function WriteReal() {
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
+					$this->errors[] = 'Cannot write Real tags on old-style file format';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (empty($OldThisFileInfo['real']['chunks'])) {
+					$this->errors[] = 'Cannot write Real tags because cannot find DATA chunk in file';
+					fclose($fp_source);
+					return false;
+				}
+				foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+					$oldChunkInfo[$chunkarray['name']] = $chunkarray;
+				}
+				if (!empty($oldChunkInfo['CONT']['length'])) {
+					$this->paddedlength = max($oldChunkInfo['CONT']['length'], $this->paddedlength);
+				}
+
+				$new_CONT_tag_data = $this->GenerateCONTchunk();
+				$new_PROP_tag_data = $this->GeneratePROPchunk($OldThisFileInfo['real']['chunks'], $new_CONT_tag_data);
+				$new__RMF_tag_data = $this->GenerateRMFchunk($OldThisFileInfo['real']['chunks']);
+
+				if (@$oldChunkInfo['.RMF']['length'] == strlen($new__RMF_tag_data)) {
+					fseek($fp_source, $oldChunkInfo['.RMF']['offset'], SEEK_SET);
+					fwrite($fp_source, $new__RMF_tag_data);
+				} else {
+					$this->errors[] = 'new .RMF tag ('.strlen($new__RMF_tag_data).' bytes) different length than old .RMF tag ('.$oldChunkInfo['.RMF']['length'].' bytes)';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (@$oldChunkInfo['PROP']['length'] == strlen($new_PROP_tag_data)) {
+					fseek($fp_source, $oldChunkInfo['PROP']['offset'], SEEK_SET);
+					fwrite($fp_source, $new_PROP_tag_data);
+				} else {
+					$this->errors[] = 'new PROP tag ('.strlen($new_PROP_tag_data).' bytes) different length than old PROP tag ('.$oldChunkInfo['PROP']['length'].' bytes)';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (@$oldChunkInfo['CONT']['length'] == strlen($new_CONT_tag_data)) {
+
+					// new data length is same as old data length - just overwrite
+					fseek($fp_source, $oldChunkInfo['CONT']['offset'], SEEK_SET);
+					fwrite($fp_source, $new_CONT_tag_data);
+					fclose($fp_source);
+					return true;
+
+				} else {
+
+					if (empty($oldChunkInfo['CONT'])) {
+						// no existing CONT chunk
+						$BeforeOffset = $oldChunkInfo['DATA']['offset'];
+						$AfterOffset  = $oldChunkInfo['DATA']['offset'];
+					} else {
+						// new data is longer than old data
+						$BeforeOffset = $oldChunkInfo['CONT']['offset'];
+						$AfterOffset  = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
+					}
+					if ($tempfilename = tempnam('*', 'getID3')) {
+						ob_start();
+						if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+							rewind($fp_source);
+							fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+							fwrite($fp_temp, $new_CONT_tag_data);
+							fseek($fp_source, $AfterOffset, SEEK_SET);
+							while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+								fwrite($fp_temp, $buffer, strlen($buffer));
+							}
+							fclose($fp_temp);
+
+							if (copy($tempfilename, $this->filename)) {
+								unlink($tempfilename);
+								fclose($fp_source);
+								return true;
+							}
+							unlink($tempfilename);
+							$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+
+						} else {
+
+							$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+						}
+						ob_end_clean();
+					}
+					fclose($fp_source);
+					return false;
+
+				}
+
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+				return false;
+			}
+		}
+		$this->errors[] = 'File is not writeable: '.$this->filename;
+		return false;
+	}
+
+	function GenerateRMFchunk(&$chunks) {
+		$oldCONTexists = false;
+		foreach ($chunks as $key => $chunk) {
+			$chunkNameKeys[$chunk['name']] = $key;
+			if ($chunk['name'] == 'CONT') {
+				$oldCONTexists = true;
+			}
+		}
+		$newHeadersCount = $chunks[$chunkNameKeys['.RMF']]['headers_count'] + ($oldCONTexists ? 0 : 1);
+
+		$RMFchunk  = "\x00\x00"; // object version
+		$RMFchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['.RMF']]['file_version'], 4);
+		$RMFchunk .= getid3_lib::BigEndian2String($newHeadersCount,                                4);
+
+		$RMFchunk  = '.RMF'.getid3_lib::BigEndian2String(strlen($RMFchunk) + 8, 4).$RMFchunk; // .RMF chunk identifier + chunk length
+		return $RMFchunk;
+	}
+
+	function GeneratePROPchunk(&$chunks, &$new_CONT_tag_data) {
+		$old_CONT_length = 0;
+		$old_DATA_offset = 0;
+		$old_INDX_offset = 0;
+		foreach ($chunks as $key => $chunk) {
+			$chunkNameKeys[$chunk['name']] = $key;
+			if ($chunk['name'] == 'CONT') {
+				$old_CONT_length = $chunk['length'];
+			} elseif ($chunk['name'] == 'DATA') {
+				if (!$old_DATA_offset) {
+					$old_DATA_offset = $chunk['offset'];
+				}
+			} elseif ($chunk['name'] == 'INDX') {
+				if (!$old_INDX_offset) {
+					$old_INDX_offset = $chunk['offset'];
+				}
+			}
+		}
+		$CONTdelta = strlen($new_CONT_tag_data) - $old_CONT_length;
+
+		$PROPchunk  = "\x00\x00"; // object version
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_bit_rate'],    4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_bit_rate'],    4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['max_packet_size'], 4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['avg_packet_size'], 4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_packets'],     4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['duration'],        4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['preroll'],         4);
+		$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_INDX_offset + $CONTdelta),              4);
+		$PROPchunk .= getid3_lib::BigEndian2String(max(0, $old_DATA_offset + $CONTdelta),              4);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['num_streams'],     2);
+		$PROPchunk .= getid3_lib::BigEndian2String($chunks[$chunkNameKeys['PROP']]['flags_raw'],       2);
+
+		$PROPchunk  = 'PROP'.getid3_lib::BigEndian2String(strlen($PROPchunk) + 8, 4).$PROPchunk; // PROP chunk identifier + chunk length
+		return $PROPchunk;
+	}
+
+	function GenerateCONTchunk() {
+		foreach ($this->tag_data as $key => $value) {
+			// limit each value to 0xFFFF bytes
+			$this->tag_data[$key] = substr($value, 0, 65535);
+		}
+
+		$CONTchunk  = "\x00\x00"; // object version
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['title']), 2);
+		$CONTchunk .= @$this->tag_data['title'];
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['artist']), 2);
+		$CONTchunk .= @$this->tag_data['artist'];
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['copyright']), 2);
+		$CONTchunk .= @$this->tag_data['copyright'];
+
+		$CONTchunk .= getid3_lib::BigEndian2String(strlen(@$this->tag_data['comment']), 2);
+		$CONTchunk .= @$this->tag_data['comment'];
+
+		if ($this->paddedlength > (strlen($CONTchunk) + 8)) {
+			$CONTchunk .= str_repeat("\x00", $this->paddedlength - strlen($CONTchunk) - 8);
+		}
+
+		$CONTchunk  = 'CONT'.getid3_lib::BigEndian2String(strlen($CONTchunk) + 8, 4).$CONTchunk; // CONT chunk identifier + chunk length
+
+		return $CONTchunk;
+	}
+
+	function RemoveReal() {
+		// File MUST be writeable - CHMOD(646) at least
+		if (is_writeable($this->filename)) {
+			if ($fp_source = @fopen($this->filename, 'r+b')) {
+
+				// Initialize getID3 engine
+				$getID3 = new getID3;
+				$OldThisFileInfo = $getID3->analyze($this->filename);
+				if (empty($OldThisFileInfo['real']['chunks']) && !empty($OldThisFileInfo['real']['old_ra_header'])) {
+					$this->errors[] = 'Cannot remove Real tags from old-style file format';
+					fclose($fp_source);
+					return false;
+				}
+
+				if (empty($OldThisFileInfo['real']['chunks'])) {
+					$this->errors[] = 'Cannot remove Real tags because cannot find DATA chunk in file';
+					fclose($fp_source);
+					return false;
+				}
+				foreach ($OldThisFileInfo['real']['chunks'] as $chunknumber => $chunkarray) {
+					$oldChunkInfo[$chunkarray['name']] = $chunkarray;
+				}
+
+				if (empty($oldChunkInfo['CONT'])) {
+					// no existing CONT chunk
+					fclose($fp_source);
+					return true;
+				}
+
+				$BeforeOffset = $oldChunkInfo['CONT']['offset'];
+				$AfterOffset  = $oldChunkInfo['CONT']['offset'] + $oldChunkInfo['CONT']['length'];
+				if ($tempfilename = tempnam('*', 'getID3')) {
+					ob_start();
+					if ($fp_temp = fopen($tempfilename, 'wb')) {
+
+						rewind($fp_source);
+						fwrite($fp_temp, fread($fp_source, $BeforeOffset));
+						fseek($fp_source, $AfterOffset, SEEK_SET);
+						while ($buffer = fread($fp_source, GETID3_FREAD_BUFFER_SIZE)) {
+							fwrite($fp_temp, $buffer, strlen($buffer));
+						}
+						fclose($fp_temp);
+
+						if (copy($tempfilename, $this->filename)) {
+							unlink($tempfilename);
+							fclose($fp_source);
+							return true;
+						}
+						unlink($tempfilename);
+						$this->errors[] = 'FAILED: copy('.$tempfilename.', '.$this->filename.') - '.strip_tags(ob_get_contents());
+
+					} else {
+
+						$this->errors[] = 'Could not open '.$tempfilename.' mode "wb" - '.strip_tags(ob_get_contents());
+
+					}
+					ob_end_clean();
+				}
+				fclose($fp_source);
+				return false;
+
+
+			} else {
+				$this->errors[] = 'Could not open '.$this->filename.' mode "r+b"';
+				return false;
+			}
+		}
+		$this->errors[] = 'File is not writeable: '.$this->filename;
+		return false;
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/getid3/write.vorbiscomment.php b/apps/media/getID3/getid3/write.vorbiscomment.php
new file mode 100644
index 0000000000000000000000000000000000000000..f93b1a1cda8591967f0daa27b109cc001e0d2c7d
--- /dev/null
+++ b/apps/media/getID3/getid3/write.vorbiscomment.php
@@ -0,0 +1,124 @@
+<?php
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+// See readme.txt for more details                             //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// write.vorbiscomment.php                                     //
+// module for writing VorbisComment tags                       //
+// dependencies: /helperapps/vorbiscomment.exe                 //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+
+class getid3_write_vorbiscomment
+{
+
+	var $filename;
+	var $tag_data;
+	var $warnings = array(); // any non-critical errors will be stored here
+	var $errors   = array(); // any critical errors will be stored here
+
+	function getid3_write_vorbiscomment() {
+		return true;
+	}
+
+	function WriteVorbisComment() {
+
+		if (!ini_get('safe_mode')) {
+
+			// Create file with new comments
+			$tempcommentsfilename = tempnam('*', 'getID3');
+			if ($fpcomments = @fopen($tempcommentsfilename, 'wb')) {
+
+				foreach ($this->tag_data as $key => $value) {
+					foreach ($value as $commentdata) {
+						fwrite($fpcomments, $this->CleanVorbisCommentName($key).'='.$commentdata."\n");
+					}
+				}
+				fclose($fpcomments);
+
+			} else {
+
+				$this->errors[] = 'failed to open temporary tags file "'.$tempcommentsfilename.'", tags not written';
+				return false;
+
+			}
+
+			$oldignoreuserabort = ignore_user_abort(true);
+			if (GETID3_OS_ISWINDOWS) {
+
+				if (file_exists(GETID3_HELPERAPPSDIR.'vorbiscomment.exe')) {
+					//$commandline = '"'.GETID3_HELPERAPPSDIR.'vorbiscomment.exe" -w --raw -c "'.$tempcommentsfilename.'" "'.str_replace('/', '\\', $this->filename).'"';
+					//  vorbiscomment works fine if you copy-paste the above commandline into a command prompt,
+					//  but refuses to work with `backtick` if there are "doublequotes" present around BOTH
+					//  the metaflac pathname and the target filename. For whatever reason...??
+					//  The solution is simply ensure that the metaflac pathname has no spaces,
+					//  and therefore does not need to be quoted
+
+					// On top of that, if error messages are not always captured properly under Windows
+					// To at least see if there was a problem, compare file modification timestamps before and after writing
+					clearstatcache();
+					$timestampbeforewriting = filemtime($this->filename);
+
+					$commandline = GETID3_HELPERAPPSDIR.'vorbiscomment.exe -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
+					$VorbiscommentError = `$commandline`;
+
+					if (empty($VorbiscommentError)) {
+						clearstatcache();
+						if ($timestampbeforewriting == filemtime($this->filename)) {
+							$VorbiscommentError = 'File modification timestamp has not changed - it looks like the tags were not written';
+						}
+					}
+				} else {
+					$VorbiscommentError = 'vorbiscomment.exe not found in '.GETID3_HELPERAPPSDIR;
+				}
+
+			} else {
+
+				$commandline = 'vorbiscomment -w --raw -c "'.$tempcommentsfilename.'" "'.$this->filename.'" 2>&1';
+				$VorbiscommentError = `$commandline`;
+
+			}
+
+			// Remove temporary comments file
+			unlink($tempcommentsfilename);
+			ignore_user_abort($oldignoreuserabort);
+
+			if (!empty($VorbiscommentError)) {
+
+				$this->errors[] = 'system call to vorbiscomment failed with message: '."\n\n".$VorbiscommentError;
+				return false;
+
+			}
+
+			return true;
+		}
+
+		$this->errors[] = 'PHP running in Safe Mode (backtick operator not available) - cannot call vorbiscomment, tags not written';
+		return false;
+	}
+
+	function DeleteVorbisComment() {
+		$this->tag_data = array(array());
+		return $this->WriteVorbisComment();
+	}
+
+	function CleanVorbisCommentName($originalcommentname) {
+		// A case-insensitive field name that may consist of ASCII 0x20 through 0x7D, 0x3D ('=') excluded.
+		// ASCII 0x41 through 0x5A inclusive (A-Z) is to be considered equivalent to ASCII 0x61 through
+		// 0x7A inclusive (a-z).
+
+		// replace invalid chars with a space, return uppercase text
+		// Thanks Chris Bolt <chris-getid3Øbolt*cx> for improving this function
+		// note: ereg_replace() replaces nulls with empty string (not space)
+		return strtoupper(ereg_replace('[^ -<>-}]', ' ', str_replace("\x00", ' ', $originalcommentname)));
+
+	}
+
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/getID3/helperapps/readme.txt b/apps/media/getID3/helperapps/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c210a5985430ef6b62dfc2ff3c4551defa270c27
--- /dev/null
+++ b/apps/media/getID3/helperapps/readme.txt
@@ -0,0 +1,55 @@
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// /helperapps/readme.txt - part of getID3()                   //
+// List of binary files required under Windows for some        //
+// features and/or file formats                                //
+// See /readme.txt for more details                            //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+This directory should contain binaries of various helper applications
+that getID3() depends on to handle some file formats under Windows.
+
+The location of this directory is configurable in /getid3/getid3.php
+as GETID3_HELPERAPPSDIR
+
+If this directory is empty, or you are missing any files, please
+download the latest version of the "getID3()-WindowsSupport" package
+from the usual download location (http://getid3.sourceforge.net)
+
+
+
+Included files:
+=====================================================
+
+Taken from http://www.cygwin.com/
+* cygwin1.dll
+
+Taken from http://unxutils.sourceforge.net/
+* head.exe
+* md5sum.exe
+* tail.exe
+
+Taken from http://ebible.org/mpj/software.htm
+* sha1sum.exe
+
+Taken from http://www.vorbis.com/download.psp
+* vorbiscomment.exe
+
+Taken from http://flac.sourceforge.net/download.html
+* metaflac.exe
+
+Taken from http://www.etree.org/shncom.html
+* shorten.exe
+
+
+/////////////////////////////////////////////////////////////////
+
+Changelog:
+
+2003.12.29:
+  * Initial release
\ No newline at end of file
diff --git a/apps/media/getID3/license.commercial.txt b/apps/media/getID3/license.commercial.txt
new file mode 100644
index 0000000000000000000000000000000000000000..416e5a14694b17c7651dfa855199e9d699cf121e
--- /dev/null
+++ b/apps/media/getID3/license.commercial.txt
@@ -0,0 +1,27 @@
+                  getID3() Commercial License
+                  ===========================
+
+getID3() is licensed under the "GNU Public License" (GPL) and/or the
+"getID3() Commercial License" (gCL). This document describes the gCL.
+
+---------------------------------------------------------------------
+
+The license is non-exclusively granted to a single person or company,
+per payment of the license fee, for the lifetime of that person or
+company. The license is non-transferrable.
+
+The gCL grants the licensee the right to use getID3() in commercial
+closed-source projects. Modifications may be made to getID3() with no
+obligation to release the modified source code. getID3() (or pieces
+thereof) may be included in any number of projects authored (in whole
+or in part) by the licensee.
+
+The licensee may use any version of getID3(), past, present or future,
+as is most convenient. This license does not entitle the licensee to
+receive any technical support, updates or bugfixes, except as such are
+made publicly available to all getID3() users.
+
+The licensee may not sub-license getID3() itself, meaning that any
+commercially released product containing all or parts of getID3() must
+have added functionality beyond what is available in getID3();
+getID3() itself may not be re-licensed by the licensee.
diff --git a/apps/media/getID3/license.txt b/apps/media/getID3/license.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9fec8082904c69a5e62e3f590c1e0a409a04f2f1
--- /dev/null
+++ b/apps/media/getID3/license.txt
@@ -0,0 +1,340 @@
+            GNU GENERAL PUBLIC LICENSE
+               Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+              59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+            GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+             END OF TERMS AND CONDITIONS
+
+        How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/apps/media/getID3/readme.txt b/apps/media/getID3/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1a798d1dbe2f8c414b9f1a391e1ef70b52c27112
--- /dev/null
+++ b/apps/media/getID3/readme.txt
@@ -0,0 +1,549 @@
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// changelog.txt - part of getID3()                            //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+        This code is released under the GNU GPL:
+          http://www.gnu.org/copyleft/gpl.html
+
+     +---------------------------------------------+
+     | If you do use this code somewhere, send me  |
+     | an email and tell me how/where you used it. |
+     |                                             |
+     | If you want to donate, there is a link on   |
+     | http://www.getid3.org for PayPal donations. |
+     +---------------------------------------------+
+
+
+
+Quick Start
+===========================================================================
+
+Q: How can I check that getID3() works on my server/files?
+A: Unzip getID3() to a directory, then access /demos/demo.browse.php
+
+
+
+Sourceforge Notification
+===========================================================================
+
+It's highly recommended that you sign up for notification from
+Sourceforge for when new versions are released. Please visit:
+http://sourceforge.net/project/showfiles.php?group_id=55859
+and click the little "monitor package" icon/link.  If you're
+previously signed up for the mailing list, be aware that it has
+been discontinued, only the automated Sourceforge notification
+will be used from now on.
+
+
+
+What does getID3() do?
+===========================================================================
+
+Reads & parses (to varying degrees):
+ ¤ tags:
+  * APE (v1 and v2)
+  * ID3v1 (& ID3v1.1)
+  * ID3v2 (v2.4, v2.3, v2.2)
+  * Lyrics3 (v1 & v2)
+
+ ¤ audio-lossy:
+  * MP3/MP2/MP1
+  * MPC / Musepack
+  * Ogg (Vorbis, OggFLAC, Speex)
+  * RealAudio
+  * Speex
+  * VQF
+
+ ¤ audio-lossless:
+  * AIFF
+  * AU
+  * Bonk
+  * CD-audio (*.cda)
+  * FLAC
+  * LA (Lossless Audio)
+  * LPAC
+  * MIDI
+  * Monkey's Audio
+  * OptimFROG
+  * RKAU
+  * VOC
+  * WAV (RIFF)
+  * WavPack
+
+ ¤ audio-video:
+  * ASF: ASF, Windows Media Audio (WMA), Windows Media Video (WMV)
+  * AVI (RIFF)
+  * Flash
+  * MPEG-1 / MPEG-2
+  * NSV (Nullsoft Streaming Video)
+  * Quicktime
+  * RealVideo
+
+ ¤ still image:
+  * BMP
+  * GIF
+  * JPEG
+  * PNG
+
+ ¤ data:
+  * ISO-9660 CD-ROM image (directory structure)
+  * SZIP (limited support)
+  * ZIP (directory structure)
+
+
+Writes:
+  * ID3v1 (& ID3v1.1)
+  * ID3v2 (v2.3 & v2.4)
+  * VorbisComment on OggVorbis
+  * VorbisComment on FLAC (not OggFLAC)
+  * APE v2
+  * Lyrics3 (delete only)
+
+
+
+Requirements
+===========================================================================
+
+* PHP 4.2.0 (or higher) for getID3() 1.7.8 (and up).
+* PHP 5.0.0 (or higher) for getID3() 2.0.0 (and up).
+* at least 4MB memory for PHP. 8MB is highly recommended.
+  12MB is required with all modules loaded.
+
+
+
+Usage
+===========================================================================
+
+See /demos/demo.basic.php for a very basic use of getID3() with no
+fancy output, just scanning one file.
+
+See structure.txt for the returned data structure.
+
+*>  For an example of a complete directory-browsing,       <*
+*>  file-scanning implementation of getID3(), please run   <*
+*>  /demos/demo.browse.php                                 <*
+
+See /demos/demo.mysql.php for a sample recursive scanning code that
+scans every file in a given directory, and all sub-directories, stores
+the results in a database and allows various analysis / maintenance
+operations
+
+To analyze remote files over HTTP or FTP you need to copy the file
+locally first before running getID3(). Your code would look something
+like this:
+
+// Copy remote file locally to scan with getID3()
+$remotefilename = 'http://www.example.com/filename.mp3';
+if ($fp_remote = fopen($remotefilename, 'rb')) {
+    $localtempfilename = tempnam('/tmp', 'getID3');
+    if ($fp_local = fopen($localtempfilename, 'wb')) {
+        while ($buffer = fread($fp_remote, 8192)) {
+            fwrite($fp_local, $buffer);
+        }
+        fclose($fp_local);
+
+		// Initialize getID3 engine
+		$getID3 = new getID3;
+
+		$ThisFileInfo = $getID3->analyze($filename);
+
+        // Delete temporary file
+        unlink($localtempfilename);
+    }
+    fclose($fp_remote);
+}
+
+
+See /demos/demo.write.php for how to write tags.
+
+
+
+What does the returned data structure look like?
+===========================================================================
+
+See structure.txt
+
+It is recommended that you look at the output of
+/demos/demo.browse.php scanning the file(s) you're interested in to
+confirm what data is actually returned for any particular filetype in
+general, and your files in particular, as the actual data returned
+may vary considerably depending on what information is available in
+the file itself.
+
+
+
+Notes
+===========================================================================
+
+getID3() 1.7:
+If the format parser encounters a critical problem, it will return
+something in $fileinfo['error'], describing the encountered error. If
+a less critical error or notice is generated it will appear in
+$fileinfo['warning']. Both keys may contain more than one warning or
+error. If something is returned in ['error'] then the file was not
+correctly parsed and returned data may or may not be correct and/or
+complete. If something is returned in ['warning'] (and not ['error'])
+then the data that is returned is OK - usually getID3() is reporting
+errors in the file that have been worked around due to known bugs in
+other programs. Some warnings may indicate that the data that is
+returned is OK but that some data could not be extracted due to
+errors in the file.
+
+getID3() 2.0:
+See above except errors are thrown (so you will only get one error).
+
+
+
+Disclaimer
+===========================================================================
+
+getID3() has been tested on many systems, on many types of files,
+under many operating systems, and is generally believe to be stable
+and safe. That being said, there is still the chance there is an
+undiscovered and/or unfixed bug that may potentially corrupt your
+file, especially within the writing functions. By using getID3() you
+agree that it's not my fault if any of your files are corrupted.
+In fact, I'm not liable for anything :)
+
+
+
+License
+===========================================================================
+
+GNU General Public License - see license.txt
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to:
+Free Software Foundation, Inc.
+59 Temple Place - Suite 330
+Boston, MA  02111-1307, USA.
+
+FAQ:
+Q: Can I use getID3() in my program? Do I need a commercial license?
+A: You're generally free to use getID3 however you see fit. The only
+   case in which you would require a commercial license is if you're
+   selling your closed-source program that integrates getID3. If you
+   sell your program including a copy of getID3, that's fine as long
+   as you include a copy of the sourcecode when you sell it.  Or you
+   can distribute your code without getID3 and say "download it from
+   getid3.sourceforge.net"
+
+
+
+Future Plans
+===========================================================================
+
+* Writing support for Real
+* Better support for MP4 container format
+* Support for Matroska (www.matroska.org)
+  http://corecodec.com/modules.php?op=modload&name=PNphpBB2&file=viewtopic&t=227
+* Scan for appended ID3v2 tag at end of file per ID3v2.4 specs (Section 5.0)
+* Support for JPEG-2000 (http://www.morgan-multimedia.com/jpeg2000_overview.htm)
+* Support for MOD (mod/stm/s3m/it/xm/mtm/ult/669)
+* Support for ACE (thanks Vince)
+* Support for Ogg other than Vorbis, Speex and OggFlac (ie. Ogg+Xvid)
+* Ability to create Xing/LAME VBR header for VBR MP3s that are missing VBR header
+* Ability to "clean" ID3v2 padding (replace invalid padding with valid padding)
+* Warn if MP3s change version mid-stream (in full-scan mode)
+* check for corrupt/broken mid-file MP3 streams in histogram scan
+* Support for lossless-compression formats
+  (http://www.firstpr.com.au/audiocomp/lossless/#Links)
+  (http://compression.ca/act-sound.html)
+  (http://web.inter.nl.net/users/hvdh/lossless/lossless.htm)
+* Support for RIFF-INFO chunks
+  * http://lotto.st-andrews.ac.uk/~njh/tag_interchange.html
+    (thanks Nick Humfrey <njhØsurgeradio*co*uk>)
+  * http://abcavi.narod.ru/sof/abcavi/infotags.htm
+    (thanks Kibi)
+* Better support for Bink video
+* http://www.hr/josip/DSP/AudioFile2.html
+* http://www.pcisys.net/~melanson/codecs/
+* Detect mp3PRO
+* Support for PSD
+* Support for JPC
+* Support for JP2
+* Support for JPX
+* Support for JB2
+* Support for IFF
+* Support for ICO
+* Support for ANI
+* Support for EXE (comments, author, etc) (thanks p*quaedackersØplanet*nl)
+* Support for DVD-IFO (region, subtitles, aspect ratio, etc)
+  (thanks p*quaedackersØplanet*nl)
+* More complete support for SWF - parsing encapsulated MP3 and/or JPEG content
+    (thanks n8n8Øyahoo*com)
+* Support for a2b
+* Optional scan-through-frames for AVI verification
+  (thanks rockcohenØmassive-interactive*nl)
+* Support for TTF (thanks infoØbutterflyx*com)
+* Support for DSS (http://www.getid3.org/phpBB2/viewtopic.php?t=171)
+* Support for SMAF (http://smaf-yamaha.com/what/demo.html)
+  http://www.getid3.org/phpBB2/viewtopic.php?t=182
+* Support for AMR (http://www.getid3.org/phpBB2/viewtopic.php?t=195)
+* Support for 3gpp (http://www.getid3.org/phpBB2/viewtopic.php?t=195)
+* Support for ID4 (http://www.wackysoft.cjb.net grizlyY2KØhotmail*com)
+* Parse XML data returned in Ogg comments
+* Parse XML data from Quicktime SMIL metafiles (klausrathØmac*com)
+* ID3v2 genre string creator function
+* More complete parsing of JPG
+* Support for all old-style ASF packets
+* ASF/WMA/WMV tag writing
+* Parse declared T??? ID3v2 text information frames, where appropriate
+    (thanks Christian Fritz for the idea)
+* Recognize encoder:
+  http://www.guerillasoft.com/EncSpot2/index.html
+  http://ff123.net/identify.html
+  http://www.hydrogenaudio.org/?act=ST&f=16&t=9414
+  http://www.hydrogenaudio.org/?showtopic=11785
+* Support for other OS/2 bitmap structures: Bitmap Array('BA'),
+  Color Icon('CI'), Color Pointer('CP'), Icon('IC'), Pointer ('PT')
+  http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
+* Support for WavPack RAW mode
+* ASF/WMA/WMV data packet parsing
+* ID3v2FrameFlagsLookupTagAlter()
+* ID3v2FrameFlagsLookupFileAlter()
+* obey ID3v2 tag alter/preserve/discard rules
+* http://www.geocities.com/SiliconValley/Sector/9654/Softdoc/Illyrium/Aolyr.htm
+* proper checking for LINK/LNK frame validity in ID3v2 writing
+* proper checking for ASPI-TLEN frame validity in ID3v2 writing
+* proper checking for COMR frame validity in ID3v2 writing
+* http://www.geocities.co.jp/SiliconValley-Oakland/3664/index.html
+* decode GEOB ID3v2 structure as encoded by RealJukebox,
+  decode NCON ID3v2 structure as encoded by MusicMatch
+  (probably won't happen - the formats are proprietary)
+
+
+
+Known Bugs/Issues in getID3() that may be fixed eventually
+===========================================================================
+
+* Cannot determine bitrate for MPEG video with VBR video data
+  (need documentation)
+* Interlace/progressive cannot be determined for MPEG video
+  (need documentation)
+* MIDI playtime is sometimes inaccurate
+* AAC-RAW mode files cannot be identified
+* WavPack-RAW mode files cannot be identified
+* mp4 files report lots of "Unknown QuickTime atom type"
+   (need documentation)
+* Encrypted ASF/WMA/WMV files warn about "unhandled GUID
+  ASF_Content_Encryption_Object"
+* Bitrate split between audio and video cannot be calculated for
+  NSV, only the total bitrate. (need documentation)
+* All Ogg formats (Vorbis, OggFLAC, Speex) are affected by the
+  problem of large VorbisComments spanning multiple Ogg pages, but
+  but only OggVorbis files can be processed with vorbiscomment.
+* The version of "head" supplied with Mac OS 10.2.8 (maybe other
+  versions too) does only understands a single option (-n) and
+  therefore fails. getID3 ignores this and returns wrong md5_data.
+
+
+
+Known Bugs/Issues in getID3() that cannot be fixed
+--------------------------------------------------
+
+* Files larger than 2GB cannot always be parsed fully by getID3()
+  due to limitations in the PHP filesystem functions.
+  NOTE: Since v1.7.8b3 there is partial support for larger-than-
+  2GB files, most of which will parse OK, as long as no critical
+  data is located beyond the 2GB offset.
+  Known will-work:
+  * ZIP  (format doesn't support files >2GB)
+  * FLAC (current encoders don't support files >2GB)
+  Known will-not-work:
+  * ID3v1 tags (always located at end-of-file)
+  * Lyrics3 tags (always located at end-of-file)
+  * APE tags (always located at end-of-file)
+  Maybe-will-work:
+  * Quicktime (will work if needed metadata is before 2GB offset,
+    that is if the file has been hinted/optimized for streaming)
+  * RIFF.WAV (should work fine, but gives warnings about not being
+    able to parse all chunks)
+  * RIFF.AVI (playtime will probably be wrong, is only based on
+    "movi" chunk that fits in the first 2GB, should issue error
+    to show that playtime is incorrect. Other data should be mostly
+    correct, assuming that data is constant throughout the file)
+
+
+
+Known Bugs/Issues in other programs
+-----------------------------------
+
+* Winamp (up to v2.80 at least) does not support ID3v2.4 tags,
+    only ID3v2.3
+    see: http://forums.winamp.com/showthread.php?postid=387524
+* Some versions of Helium2 (www.helium2.com) do not write
+    ID3v2.4-compliant Frame Sizes, even though the tag is marked
+    as ID3v2.4)  (detected by getID3())
+* MP3ext V3.3.17 places a non-compliant padding string at the end
+    of the ID3v2 header. This is supposedly fixed in v3.4b21 but
+    only if you manually add a registry key. This fix is not yet
+    confirmed.  (detected by getID3())
+* CDex v1.40 (fixed by v1.50b7) writes non-compliant Ogg comment
+    strings, supposed to be in the format "NAME=value" but actually
+    written just "value"  (detected by getID3())
+* Oggenc 0.9-rc3 flags the encoded file as ABR whether it's
+    actually ABR or VBR.
+* iTunes (versions "X v2.0.3", "v3.0.1" are known-guilty, probably
+    other versions are too) writes ID3v2.3 comment tags using a
+    frame name 'COM ' which is not valid for ID3v2.3+ (it's an
+    ID3v2.2-style frame name)  (detected by getID3())
+* MP2enc does not encode mono CBR MP2 files properly (half speed
+    sound and double playtime)
+* MP2enc does not encode mono VBR MP2 files properly (actually
+    encoded as stereo)
+* tooLAME does not encode mono VBR MP2 files properly (actually
+    encoded as stereo)
+* AACenc encodes files in VBR mode (actually ABR) even if CBR is
+   specified
+* AAC/ADIF - bitrate_mode = cbr for vbr files
+* LAME 3.90-3.92 prepends one frame of null data (space for the
+  LAME/VBR header, but it never gets written) when encoding in CBR
+  mode with the DLL
+* Ahead Nero encodes TwinVQF with a DSIZ value (which is supposed
+  to be the filesize in bytes) of "0" for TwinVQF v1.0 and "1" for
+  TwinVQF v2.0  (detected by getID3())
+* Ahead Nero encodes TwinVQF files 1 second shorter than they
+  should be
+* AAC-ADTS files are always actually encoded VBR, even if CBR mode
+  is specified (the CBR-mode switches on the encoder enable ABR
+  mode, not CBR as such, but it's not possible to tell the
+  difference between such ABR files and true VBR)
+* STREAMINFO.audio_signature in OggFLAC is always null. "The reason
+  it's like that is because there is no seeking support in
+  libOggFLAC yet, so it has no way to go back and write the
+  computed sum after encoding. Seeking support in Ogg FLAC is the
+  #1 item for the next release." - Josh Coalson (FLAC developer)
+  NOTE: getID3() will calculate md5_data in a method similar to
+  other file formats, but that value cannot be compared to the
+  md5_data value from FLAC data in a FLAC file format.
+* STREAMINFO.audio_signature is not calculated in FLAC v0.3.0 &
+  v0.4.0 - getID3() will calculate md5_data in a method similar to
+  other file formats, but that value cannot be compared to the
+  md5_data value from FLAC v0.5.0+
+* RioPort (various versions including 2.0 and 3.11) tags ID3v2 with
+  a WCOM frame that has no data portion
+* Earlier versions of Coolplayer adds illegal ID3 tags to Ogg Vorbis
+  files, thus making them corrupt.
+* Meracl ID3 Tag Writer v1.3.4 (and older) incorrectly truncates the
+  last byte of data from an MP3 file when appending a new ID3v1 tag.
+  (detected by getID3())
+* Lossless-Audio files encoded with and without the -noseek switch
+  do actually differ internally and therefore cannot match md5_data
+* iTunes has been known to append a new ID3v1 tag on the end of an
+  existing ID3v1 tag when ID3v2 tag is also present
+  (detected by getID3())
+
+
+
+
+Reference material:
+===========================================================================
+
+[www.id3.org material now mirrored at http://id3lib.sourceforge.net/id3/]
+* http://www.id3.org/id3v2.4.0-structure.txt
+* http://www.id3.org/id3v2.4.0-frames.txt
+* http://www.id3.org/id3v2.4.0-changes.txt
+* http://www.id3.org/id3v2.3.0.txt
+* http://www.id3.org/id3v2-00.txt
+* http://www.id3.org/mp3frame.html
+* http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html <mathewhendry@hotmail.com>
+* http://www.dv.co.yu/mpgscript/mpeghdr.htm
+* http://www.mp3-tech.org/programmer/frame_header.html
+* http://users.belgacom.net/gc247244/extra/tag.html
+* http://gabriel.mp3-tech.org/mp3infotag.html
+* http://www.id3.org/iso4217.html
+* http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
+* http://www.xiph.org/ogg/vorbis/doc/framing.html
+* http://www.xiph.org/ogg/vorbis/doc/v-comment.html
+* http://leknor.com/code/php/class.ogg.php.txt
+* http://www.id3.org/iso639-2.html
+* http://www.id3.org/lyrics3.html
+* http://www.id3.org/lyrics3200.html
+* http://www.psc.edu/general/software/packages/ieee/ieee.html
+* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/ieee-expl.html
+* http://www.scri.fsu.edu/~jac/MAD3401/Backgrnd/binary.html
+* http://www.jmcgowan.com/avi.html
+* http://www.wotsit.org/
+* http://www.herdsoft.com/ti/davincie/davp3xo2.htm
+* http://www.mathdogs.com/vorbis-illuminated/bitstream-appendix.html
+* "Standard MIDI File Format" by Dustin Caldwell (from www.wotsit.org)
+* http://midistudio.com/Help/GMSpecs_Patches.htm
+* http://www.xiph.org/archives/vorbis/200109/0459.html
+* http://www.replaygain.org/
+* http://www.lossless-audio.com/
+* http://download.microsoft.com/download/winmediatech40/Doc/1.0/WIN98MeXP/EN-US/ASF_Specification_v.1.0.exe
+* http://mediaxw.sourceforge.net/files/doc/Active%20Streaming%20Format%20(ASF)%201.0%20Specification.pdf
+* http://www.uni-jena.de/~pfk/mpp/sv8/
+* http://jfaul.de/atl/
+* http://www.uni-jena.de/~pfk/mpp/
+* http://www.libpng.org/pub/png/spec/png-1.2-pdg.html
+* http://www.real.com/devzone/library/creating/rmsdk/doc/rmff.htm
+* http://www.fastgraph.com/help/bmp_os2_header_format.html
+* http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
+* http://flac.sourceforge.net/format.html
+* http://www.research.att.com/projects/mpegaudio/mpeg2.html
+* http://www.audiocoding.com/wiki/index.php?page=AAC
+* http://libmpeg.org/mpeg4/doc/w2203tfs.pdf
+* http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
+* http://developer.apple.com/techpubs/quicktime/qtdevdocs/RM/frameset.htm
+* http://www.nullsoft.com/nsv/
+* http://www.wotsit.org/download.asp?f=iso9660
+* http://sandbox.mc.edu/~bennet/cs110/tc/tctod.html
+* http://www.cdroller.com/htm/readdata.html
+* http://www.speex.org/manual/node10.html
+* http://www.harmony-central.com/Computer/Programming/aiff-file-format.doc
+* http://www.faqs.org/rfcs/rfc2361.html
+* http://ghido.shelter.ro/
+* http://www.ebu.ch/tech_t3285.pdf
+* http://www.sr.se/utveckling/tu/bwf
+* http://ftp.aessc.org/pub/aes46-2002.pdf
+* http://cartchunk.org:8080/
+* http://www.broadcastpapers.com/radio/cartchunk01.htm
+* http://www.hr/josip/DSP/AudioFile2.html
+* http://home.attbi.com/~chris.bagwell/AudioFormats-11.html
+* http://www.pure-mac.com/extkey.html
+* http://cesnet.dl.sourceforge.net/sourceforge/bonkenc/bonk-binary-format-0.9.txt
+* http://www.headbands.com/gspot/
+* http://www.openswf.org/spec/SWFfileformat.html
+* http://j-faul.virtualave.net/
+* http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
+* http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
+* http://sswf.sourceforge.net/SWFalexref.html
+* http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
+* http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
+* http://developer.apple.com/quicktime/icefloe/dispatch012.html
+* http://www.csdn.net/Dev/Format/graphics/PCD.htm
+* http://tta.iszf.irk.ru/
+* http://www.atsc.org/standards/a_52a.pdf
+* http://www.alanwood.net/unicode/
+* http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
+* http://www.its.msstate.edu/net/real/reports/config/tags.stats
+* http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
+* http://brennan.young.net/Comp/LiveStage/things.html
+* http://www.multiweb.cz/twoinches/MP3inside.htm
+* http://www.geocities.co.jp/SiliconValley-Oakland/3664/alittle.html#GenreExtended
+* http://www.mactech.com/articles/mactech/Vol.06/06.01/SANENormalized/
+* http://www.unicode.org/unicode/faq/utf_bom.html
+* http://tta.corecodec.org/?menu=format
+* http://www.scvi.net/nsvformat.htm
+* http://pda.etsi.org/pda/queryform.asp
+* http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
+* http://trac.musepack.net/trac/wiki/SV8Specification
diff --git a/apps/media/getID3/structure.txt b/apps/media/getID3/structure.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a0651c60936f94f464c50a2a8b36bb52c2c98f58
--- /dev/null
+++ b/apps/media/getID3/structure.txt
@@ -0,0 +1,2251 @@
+/////////////////////////////////////////////////////////////////
+/// getID3() by James Heinrich <info@getid3.org>               //
+//  available at http://getid3.sourceforge.net                 //
+//            or http://www.getid3.org                         //
+/////////////////////////////////////////////////////////////////
+//                                                             //
+// changelog.txt - part of getID3()                            //
+// See readme.txt for more details                             //
+//                                                            ///
+/////////////////////////////////////////////////////////////////
+
+What does the returned data structure look like?
+================================================
+
+Hint: If you take a look at the nicely-formatted output of
+/demos/demo.browse.php you can generally see where the data you want
+is returned.
+
+Note that what is described below is only a rough guide to what data
+is actually returned by getID3(), since the actual data returned
+depends entirely on what data is in your file, what type of file it
+is, what kind of data is in the tags, etc. In addition, some formats
+(Quicktime for example) use a freeform recursive structure that is
+impossible to document completely.
+
+In the vast majority of cases, all the data you'll need is located
+in the root of the array or the special arrays described below in
+Section 1 (['audio'], ['video'], ['tags_html'], ['replay_gain']).
+
+It is suggested that for most applications you should use tag data
+from the root ['tags_html'] array, as this is the only location
+where data is stored in a consistant format: HTML-compatible
+character entities (ie &#1234;) for characters outside the 0x20-0x7F
+range (printable ISO-8859-1 characters). This data can be used as-is
+for output in HTML, and can be converted to whatever character set
+you wish to use if the output is not HTML.
+
+If you want to merge all available tags (for example, ID3v2 + ID3v1)
+into one array, you can call
+getid3_lib::CopyTagsToComments($ThisFileInfo)
+and you'll then have ['comments'] and ['comments_html'] which are
+identical to ['tags'] and ['tags_html'] except the array is one
+dimension shorter (no tag type array keys). For example, artist is:
+['tags_html']['id3v1']['artist'][0] or ['comments_html']['artist'][0]
+
+
+Some commonly-used information is found in these locations:
+
+File type:        ['fileformat']                  // ex 'mp3'
+Song length:      ['playtime_string']             // ex '3:45'    (minutes:seconds)
+                  ['playtime_seconds']            // ex 225.13    (seconds)
+Overall bitrate:  ['bitrate']                     // ex 113485.71 (bits-per-second - divide by 1000 for kbps)
+Audio frequency:  ['audio']['sample_rate']        // ex 44100     (Hertz)
+Artist name:      ['comments_html']['artist'][0]  // ex 'Elvis'   (if CopyTagsToComments() is used - see above)
+                                                  //   more than one artist may be present, you may want to use implode:
+                                                  //   implode(' & ', ['comments_html']['artist'])
+
+
+/////////////////////////////////////////////////////////////////
+
+array() {
+                                         // SECTION 1: Values that are present for most or all file types
+
+    ['getID3version']=>string()          // version of getID3() that scanned this file (ex: '1.6.2')
+    ['error']=>array()                   // if present, contains one or more fatal error messages
+    ['warning']=>array()                 // if present, contains one or more non-fatal warning messages
+    ['exist']=>boolean()                 // does this file actually exist?
+    ['fileformat']=>string()             // one of the standard filetype abbreviations ('mp3', 'riff', 'quicktime', etc)
+    ['filename']=>string()               // filename only, no path
+    ['filenamepath']=>string()           // full filename with path
+    ['filepath']=>string()               // path to file, not including filename
+    ['filesize']=>integer()              // filesize in bytes
+    ['md5_file']=>string()               // md5 hash of entire file
+    ['md5_data']=>string()               // md5 hash of portion of file excluding prepended and appeneded metainformation tags (ID3, APE, etc) - may be identical to ['md5_file']
+    ['md5_data_source']=>string()        // md5 hash of original source file before compression (currently used by FLAC, OptimFROG, WavPack v4+)
+    ['sha1_file']=>string()              // sha1 hash of entire file
+    ['sha1_data']=>string()              // sha1 hash of portion of file excluding prepended and appeneded metainformation tags (ID3, APE, etc) - may be identical to ['md5_file']
+    ['avdataoffset']=>integer()          // offset in bytes where audio/video data starts and prepended tags end
+    ['avdataend']=>integer()             // offset in bytes where audio/video data ends and appended tags start
+    ['bitrate']=>double()                // average bitrate for entire file (all audio/video streams), in bits per second
+    ['mime_type']=>string()              // if present, MIME type of scanned file
+    ['playtime_seconds']=>double()       // playing time of file, in seconds
+    ['playtime_string']=>string()        // playing time of file, formatted as <minutes>:<seconds>
+    ['tags']=>array()                    // array of all metainformation tags present in file ('id3v1', 'id3v2', 'ape', 'riff', 'asf', etc)
+    ['audio']=>array() {
+        ['bitrate']=>double()            // average bitrate for audio portion of file (all audio streams), in bits per second
+        ['bitrate_mode']=>string()       // 'cbr' (Constant Bit Rate) or 'vbr' (Variable Bit Rate)
+        ['bits_per_sample']=>integer()   //
+        ['channelmode']=>string()        // 'mono' or 'stereo'
+        ['channels']=>integer()          // number of audio channels
+        ['codec']=>string()              // name of audio compression codec
+        ['compression_ratio']=>double()  // ratio of compressed byte size of audio to uncompressed size
+        ['dataformat']=>string()         // one of the standard filetype abbreviations ('mp3', 'wma', etc)
+        ['encoder']=>string()            // name and version of encoder used to create file, if known
+        ['lossless']=>boolean()          // true = lossless compression; false = lossy compression
+        ['sample_rate']=>integer()
+    }
+    ['video']=>array() {
+        ['bitrate']=>integer()           // average bitrate for video portion of file (all video streams), in bits per second
+        ['bitrate_mode']=>string()       // 'cbr' (Constant Bit Rate) or 'vbr' (Variable Bit Rate)
+        ['bits_per_sample']=>integer()   //
+        ['codec']=>string()              // name of video compression codec
+        ['compression_ratio']=>double()  // ratio of compressed byte size of video to uncompressed size
+        ['dataformat']=>string()         // one of the standard filetype abbreviations ('avi', 'mpeg', etc)
+        ['encoder']=>string()            // name and version of encoder used to create file, if known
+        ['frame_rate']=>double()         // frames per second
+        ['lossless']=>boolean()          // true = lossless compression; false = lossy compression
+        ['resolution_x']=>integer()      // horizontal dimension of video/image in pixels
+        ['resolution_y']=>integer()      // vertical dimension of video/image in pixels
+        ['pixel_aspect_ratio']=>double() // pixel display aspect ratio
+    }
+    ['tags']=>array() {                  // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+        [<key name>]=>array()            // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+    }
+    ['tags_html']=>array() {             // identical to ['tags'], but with all entries converted to HTML entities as appropriate from various source encodings
+        [<key name>]=>array()            //
+    }
+    ['replay_gain']=>array() {           // replay gain information combined from any source that contains this information (LAME, ID3v2, Vorbis, APE, etc)
+        ['audiophile']=>array() {
+            ['adjustment']=>double()
+            ['originator']=>string()
+            ['peak']=>double()
+        }
+        ['radio']=>array() {
+            ['adjustment']=>double()
+            ['originator']=>string()
+            ['peak']=>double()
+        }
+    }
+
+
+                                         // SECTION 2: Values that are present for specific file types only
+
+    ['aac']=>array() {                            // AAC - Advanced Audio Coding / MPEG-4
+        ['bitrate_distribution']=>array()         //
+        ['header']=>array() {                     //
+            ['channel_configuration']=>integer()  //
+            ['crc_present']=>boolean()            //
+            ['home']=>boolean()                   //
+            ['layer']=>integer()                  //
+            ['mpeg_version']=>integer()           //
+            ['original']=>boolean()               //
+            ['private']=>boolean()                //
+            ['profile_id']=>integer()             //
+            ['profile_text']=>string()            //
+            ['sample_frequency']=>integer()       //
+            ['sample_frequency_index']=>integer() //
+            ['synch']=>integer()                  //
+        }                                         //
+        ['header_type']=>string()                 //
+    }                                             //
+                                                  //
+    ['ape']=>array()                      //
+    {                                     //
+        ['comments']=>array() {           // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+            [<key name>]=>array()         // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+        }                                 //
+        ['footer']=>array()               //
+        {                                 //
+            ['flags']=>array()            //
+            ['raw']=>array()              //
+            ['tag_version']=>integer()    //
+        }                                 //
+        ['header']=>array()               //
+        {                                 //
+            ['flags']=>array()            //
+            ['raw']=>array()              //
+            ['tag_version']=>integer()    //
+        }                                 //
+        ['items']=>array() {              // array of array of strings containing metainformation
+            [<key name>]=>array() {       // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+                ['data']=>array() {       // array of one or more Unicode values
+                ['data_ascii']=>array() { // array of values converted approximately from Unicode to ASCII
+                ['flags']=>array()        //
+            }                             //
+        }                                 //
+        ['tag_offset_end']=>integer()     //
+        ['tag_offset_start']=>integer()   //
+    }                                     //
+
+
+    ['asf']=>array() {                               // ASF - Advanced Streaming Format (ASF, Windows Media Audio (WMA), Windows Media Video (WMV))
+        ['audio_media']=>array() {                   //
+            [<x>]=>array() {                         //
+                ['bitrate']=>integer()               //
+                ['bits_per_sample']=>integer()       //
+                ['channels']=>integer()              //
+                ['codec']=>string()                  //
+                ['codec_data']=>string()             //
+                ['codec_data_size']=>integer()       //
+                ['raw']=>array() {                   //
+                    ['nAvgBytesPerSec']=>integer()   //
+                    ['wBitsPerSample']=>integer()    //
+                    ['nBlockAlign']=>integer()       //
+                    ['nChannels']=>integer()         //
+                    ['nSamplesPerSec']=>integer()    //
+                    ['wFormatTag']=>integer()        //
+                }                                    //
+                ['sample_rate']=>integer()           //
+            }                                        //
+        }                                            //
+        ['codec_list']=>array() {                    //
+            ['codec_entries']=>array() {             //
+                [<x>]=>array() {                     //
+                    ['description']=>string()        //
+                    ['description_ascii']=>string()  //
+                    ['information']=>string()        //
+                    ['name']=>string()               //
+                    ['name_ascii']=>string()         //
+                    ['type']=>string()               //
+                    ['type_raw']=>integer()          //
+                }                                    //
+            }                                        //
+            ['codec_entries_count']=>integer()       //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['reserved']=>string()                   //
+            ['reserved_guid']=>string()              //
+        }                                            //
+        ['comments']=>array() {                      // array of comment values, derived from ['content_description']
+            ['album']=>string()                      //
+            ['artist']=>string()                     //
+            ['comment']=>string()                    //
+            ['copyright']=>string()                  //
+            ['genre']=>string()                      //
+            ['title']=>string()                      //
+            ['track']=>string()                      //
+            ['year']=>string()                       //
+        }                                            //
+        ['content_description']=>array() {           // raw values - should use values from ['comments'] instead
+            ['author']=>string()                     //
+            ['author_ascii']=>string()               //
+            ['author_length']=>integer()             //
+            ['copyright']=>string()                  //
+            ['copyright_ascii']=>string()            //
+            ['copyright_length']=>integer()          //
+            ['description']=>string()                //
+            ['description_ascii']=>string()          //
+            ['description_length']=>integer()        //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['rating']=>string()                     //
+            ['rating_ascii']=>string()               //
+            ['rating_length']=>integer()             //
+            ['title']=>string()                      //
+            ['title_ascii']=>string()                //
+            ['title_length']=>integer()              //
+        }                                            //
+        ['data_object']=>array() {                   //
+            ['fileid']=>string()                     //
+            ['fileid_guid']=>string()                //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['reserved']=>integer()                  //
+            ['total_data_packets']=>integer()        //
+        }                                            //
+        ['extended_content_description']=>array() {  //
+            ['content_descriptors']=>array() {       //
+                [<x>]=>array() {                     //
+                    ['name']=>string()               //
+                    ['name_ascii']=>string()         //
+                    ['name_length']=>integer()       //
+                    ['value']=>string()              //
+                    ['value_ascii']=>string()        //
+                    ['value_length']=>integer()      //
+                    ['value_type']=>integer()        //
+                }                                    //
+            }                                        //
+            ['content_descriptors_count']=>integer() //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+        }                                            //
+        ['file_properties_object']=>array() {        //
+            ['creation_date']=>double()              //
+            ['creation_date_unix']=>double()         //
+            ['data_packets']=>integer()              //
+            ['fileid']=>string()                     //
+            ['fileid_guid']=>string()                //
+            ['filesize']=>integer()                  //
+            ['flags']=>array() {                     //
+                ['broadcast']=>boolean()             //
+                ['seekable']=>boolean()              //
+            }                                        //
+            ['flags_raw']=>integer()                 //
+            ['max_bitrate']=>integer()               //
+            ['max_packet_size']=>integer()           //
+            ['min_packet_size']=>integer()           //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['play_duration']=>double()              //
+            ['preroll']=>integer()                   //
+            ['send_duration']=>double()              //
+        }                                            //
+        ['header_extension_object']=>array() {       //
+            ['extension_data']=>integer()            //
+            ['extension_data_size']=>integer()       //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['reserved_1']=>string()                 //
+            ['reserved_1_guid']=>string()            //
+            ['reserved_2']=>integer()                //
+        }                                            //
+        ['header_object']=>array() {                 //
+            ['headerobjects']=>integer()             //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['reserved1']=>integer()                 //
+            ['reserved2']=>integer()                 //
+        }                                            //
+        ['marker_object']=>array() {                 //
+            ['markers_count']=>integer()             //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+            ['reserved']=>string()                   //
+            ['reserved_2']=>integer()                //
+            ['reserved_guid']=>string()              //
+        }                                            //
+        ['stream_bitrate_properties']=>array() {     //
+            ['bitrate_records']=>array() {           //
+                [<x>]=>array() {                     //
+                    ['bitrate']=>integer()           //
+                    ['flags_raw']=>integer()         //
+                    ['flags']=>array() {             //
+                        ['stream_number']=>integer() //
+                    }                                //
+                }                                    //
+            }                                        //
+            ['bitrate_records_count']=>integer()     //
+            ['objectid']=>string()                   //
+            ['objectid_guid']=>string()              //
+            ['objectsize']=>integer()                //
+        }                                            //
+        ['stream_properties_object']=>array() {      //
+            [<x>]=>array() {                         //
+                ['error_correct_data']=>string()     //
+                ['error_correct_guid']=>string()     //
+                ['error_correct_type']=>string()     //
+                ['error_data_length']=>integer()     //
+                ['flags_raw']=>integer()             //
+                ['flags']=>array() {                 //
+                    ['encrypted']=>boolean()         //
+                }                                    //
+                ['objectid']=>string()               //
+                ['objectid_guid']=>string()          //
+                ['objectsize']=>integer()            //
+                ['stream_type']=>string()            //
+                ['stream_type_guid']=>string()       //
+                ['time_offset']=>integer()           //
+                ['type_data_length']=>integer()      //
+                ['type_specific_data']=>string()     //
+            }                                        //
+        }                                            //
+        ['video_media']=>array() {                   //
+            [<x>]=>array() {                         //
+                ['flags']=>integer()                 //
+                ['format_data']=>array() {           //
+                    ['bits_per_pixel']=>integer()    //
+                    ['codec']=>string()              //
+                    ['codec_data']=>boolean()        //
+                    ['codec_fourcc']=>string()       //
+                    ['colors_important']=>integer()  //
+                    ['colors_used']=>integer()       //
+                    ['format_data_size']=>integer()  //
+                    ['horizontal_pels']=>integer()   //
+                    ['image_height']=>integer()      //
+                    ['image_size']=>integer()        //
+                    ['image_width']=>integer()       //
+                    ['reserved']=>integer()          //
+                    ['vertical_pels']=>integer()     //
+                }                                    //
+                ['format_data_size']=>integer()      //
+                ['image_height']=>integer()          //
+                ['image_width']=>integer()           //
+            }                                        //
+        }                                            //
+    }                                                //
+
+
+    ['au']=>array() {                       // AU - Next/Sun AUdio format
+        ['bits_per_sample']=>integer()      //
+        ['channels']=>integer()             //
+        ['comment']=>string()               //
+        ['data_format']=>string()           //
+        ['data_format_id']=>integer()       //
+        ['data_size']=>integer()            //
+        ['header_length']=>integer()        //
+        ['sample_rate']=>integer()          //
+        ['used_bits_per_sample']=>integer() //
+    }                                       //
+
+
+    ['bmp']=>array() {                          // BMP - OS/2 or Windows BitMaP
+        ['header']=>array() {                   //
+            ['compression']=>string()           //
+            ['raw']=>array() {                  //
+                ['bits_per_pixel']=>integer()   //
+                ['bmp_data_size']=>integer()    //
+                ['colors_important']=>integer() //
+                ['colors_used']=>integer()      //
+                ['compression']=>integer()      //
+                ['data_offset']=>integer()      //
+                ['filesize']=>integer()         //
+                ['header_size']=>integer()      //
+                ['height']=>integer()           //
+                ['identifier']=>string()        //
+                ['planes']=>integer()           //
+                ['resolution_h']=>integer()     //
+                ['resolution_v']=>integer()     //
+                ['width']=>integer()            //
+            }                                   //
+        }                                       //
+        ['type_os']=>string()                   //
+        ['type_version']=>integer()             //
+    }                                           //
+
+
+    ['bonk']=>array() {                       // BONK - lossy/lossless audio compression (www.bonkenc.org)
+        ['BONK']=>array() {                   //
+            ['channels']=>integer()           //
+            ['downsampling_ratio']=>integer() //
+            ['joint_stereo']=>boolean()       //
+            ['lossless']=>boolean()           //
+            ['number_samples']=>integer()     //
+            ['number_taps']=>integer()        //
+            ['offset']=>integer()             //
+            ['sample_rate']=>integer()        //
+            ['samples_per_packet']=>integer() //
+            ['size']=>integer()               //
+            ['version']=>integer()            //
+        }                                     //
+        ['INFO']=>array() {                   //
+            ['size']=>integer()               //
+            ['offset']=>integer()             //
+            ['version']=>integer()            //
+            [<x>]=>array() {                  //
+                ['nextbit']=>integer()        //
+                ['offset']=>integer()         //
+            }                                 //
+        }                                     //
+        ['dataend']=>integer()                //
+        ['dataoffset']=>integer()             //
+    }                                         //
+
+
+    ['flac']=>array() {                         // FLAC - Free Lossless Audio Compressor
+        ['SEEKTABLE']=>array() {                //
+            [<x>]=>array() {                    //
+                ['offset']=>integer()           //
+                ['samples']=>integer()          //
+            }                                   //
+            ['placeholders']=>integer()         //
+            ['raw']=>array() {                  //
+                ['block_data']=>string()        //
+                ['block_length']=>integer()     //
+                ['block_type']=>integer()       //
+                ['block_type_text']=>string()   //
+                ['last_meta_block']=>boolean()  //
+                ['offset']=>integer()           //
+            }                                   //
+        }                                       //
+        ['STREAMINFO']=>array() {               //
+            ['audio_signature']=>string()       //
+            ['bits_per_sample']=>integer()      //
+            ['channels']=>integer()             //
+            ['max_block_size']=>integer()       //
+            ['max_frame_size']=>integer()       //
+            ['min_block_size']=>integer()       //
+            ['min_frame_size']=>integer()       //
+            ['raw']=>array() {                  //
+                ['block_data']=>string()        //
+                ['block_length']=>integer()     //
+                ['block_type']=>integer()       //
+                ['block_type_text']=>string()   //
+                ['last_meta_block']=>boolean()  //
+                ['offset']=>integer()           //
+            }                                   //
+            ['sample_rate']=>integer()          //
+            ['samples_stream']=>integer()       //
+        }                                       //
+        ['VORBIS_COMMENT']=>array() {           //
+            ['raw']=>array() {                  //
+                ['block_data']=>string()        //
+                ['block_length']=>integer()     //
+                ['block_type']=>integer()       //
+                ['block_type_text']=>string()   //
+                ['last_meta_block']=>boolean()  //
+                ['offset']=>integer()           //
+            }                                   //
+        }                                       //
+        ['compressed_audio_bytes']=>integer()   //
+        ['compression_ratio']=>double()         //
+        ['uncompressed_audio_bytes']=>integer() //
+    }                                           //
+
+
+    ['gif']=>array() {                             // GIF - Graphics Interchange Format
+        ['global_color_table']=>array() {          //
+            [<x>]=>integer()                       //
+        }                                          //
+        ['header']=>array() {                      //
+            ['bits_per_pixel']=>integer()          //
+            ['flags']=>array() {                   //
+                ['global_color_sorted']=>boolean() //
+                ['global_color_table']=>boolean()  //
+            }                                      //
+            ['global_color_size']=>integer()       //
+            ['raw']=>array() {                     //
+                ['aspect_ratio']=>integer()        //
+                ['bg_color_index']=>integer()      //
+                ['flags']=>integer()               //
+                ['height']=>integer()              //
+                ['identifier']=>string()           //
+                ['version']=>string()              //
+                ['width']=>integer()               //
+            }                                      //
+        }                                          //
+        ['version']=>string()                      //
+    }                                              //
+
+
+    ['id3v1']=>array() {                // ID3v1
+        ['album']=>string()             //
+        ['artist']=>string()            //
+        ['comment']=>string()           //
+        ['genre']=>string()             //
+        ['genreid']=>integer()          //
+        ['title']=>string()             //
+        ['track']=>integer()            //
+        ['year']=>string()              //
+        ['padding_valid']=>boolean()    //
+        ['comments']=>array()           //
+        ['tag_offset_start']=>integer() //
+        ['tag_offset_end']=>integer()   //
+    }                                   //
+
+
+    ['id3v2']=>array() {                                 // ID3v2 - www.id3.org
+        [<frame name>]=>array() {                        // <frame name> can be any of the 4-character (3-character in ID3v2.2) frame names allowed in the ID3v2 spec. Exact contents of returned array data varies with frame type.
+            [<x>]=>array() {                             // some frames types allow multiple values ('COMM' for example), others do not and do not have this array level
+                ['asciidata']=>boolean()                 //
+                ['asciidescription']=>string()           //
+                ['data']=>boolean()                      //
+                ['datalength']=>integer()                //
+                ['dataoffset']=>integer()                //
+                ['description']=>string()                //
+                ['encoding']=>string()                   //
+                ['encodingid']=>integer()                //
+                ['flags']=>array() {                     //
+                    ['Encryption']=>boolean()            //
+                    ['FileAlterPreservation']=>boolean() //
+                    ['GroupingIdentity']=>boolean()      //
+                    ['ReadOnly']=>boolean()              //
+                    ['TagAlterPreservation']=>boolean()  //
+                    ['compression']=>boolean()           //
+                }                                        //
+                ['framenamelong']=>string()              //
+                ['language']=>string()                   //
+                ['languagename']=>string()               //
+            }                                            //
+        }                                                //
+        ['comments']=>array() {                          // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+            [<key name>]=>array()                        // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+        }                                                //
+        ['flags']=>array() {                             //
+            ['experim']=>string()                        //
+            ['exthead']=>string()                        //
+            ['unsynch']=>string()                        //
+        }                                                //
+        ['header']=>boolean()                            //
+        ['headerlength']=>integer()                      //
+        ['majorversion']=>integer()                      //
+        ['minorversion']=>integer()                      //
+        ['padding']=>array() {                           //
+            ['length']=>integer()                        //
+            ['start']=>integer()                         //
+            ['valid']=>boolean()                         //
+        }                                                //
+        ['tag_offset_end']=>integer()                    //
+        ['tag_offset_start']=>integer()                  //
+    }                                                    //
+
+
+    ['iso']=>array() {                                           // ISO-9660 - CD-ROM Image
+        ['directories']=>array() {                               //
+            [<x>]=>array() {                                     //
+                [<x>]=>array() {                                 //
+                    ['file_flags']=>array() {                    //
+                        ['associated']=>boolean()                //
+                        ['directory']=>boolean()                 //
+                        ['extended']=>boolean()                  //
+                        ['hidden']=>boolean()                    //
+                        ['multiple']=>boolean()                  //
+                        ['permissions']=>boolean()               //
+                    }                                            //
+                    ['file_identifier_ascii']=>string()          //
+                    ['filename']=>string()                       //
+                    ['filesize']=>integer()                      //
+                    ['offset_bytes']=>integer()                  //
+                    ['raw']=>array() {                           //
+                        ['extended_attribute_length']=>integer() //
+                        ['file_flags']=>integer()                //
+                        ['file_identifier']=>string()            //
+                        ['file_identifier_length']=>integer()    //
+                        ['file_unit_size']=>integer()            //
+                        ['filesize']=>integer()                  //
+                        ['interleave_gap_size']=>integer()       //
+                        ['length']=>integer()                    //
+                        ['offset_logical']=>integer()            //
+                        ['recording_date_time']=>string()        //
+                        ['volume_sequence_number']=>integer()    //
+                    }                                            //
+                    ['recording_timestamp']=>integer()           //
+                }                                                //
+            }                                                    //
+        }                                                        //
+        ['files']=>array() {                                     // multidimensional tree-structure array listing of all files and directories in image
+            [<directory name>]=>array()                          // entries of type array are directories (key is directory name), may contain files and/or other subdirectories
+            [<file name>]=>integer()                             // entries of type integer are files (key is file name, value is file size in bytes)
+        }                                                        //
+        ['path_table']=>array() {                                //
+            ['directories']=>array() {                           //
+                [<x>]=>array() {                                 //
+                    ['extended_length']=>integer()               //
+                    ['full_path']=>string()                      //
+                    ['length']=>integer()                        //
+                    ['location_bytes']=>integer()                //
+                    ['location_logical']=>integer()              //
+                    ['name']=>string()                           //
+                    ['name_ascii']=>string()                     //
+                    ['parent_directory']=>integer()              //
+                }                                                //
+            }                                                    //
+            ['offset']=>integer()                                //
+            ['raw']=>string()                                    //
+        }                                                        //
+        ['primary_volume_descriptor']=>array() {                 //
+            ['abstract_file_identifier']=>string()               //
+            ['application_identifier']=>string()                 //
+            ['bibliographic_file_identifier']=>string()          //
+            ['copyright_file_identifier']=>string()              //
+            ['data_preparer_identifier']=>string()               //
+            ['offset']=>integer()                                //
+            ['publisher_identifier']=>string()                   //
+            ['raw']=>array() {                                   //
+                ['abstract_file_identifier']=>string()           //
+                ['application_data']=>string()                   //
+                ['application_identifier']=>string()             //
+                ['bibliographic_file_identifier']=>string()      //
+                ['copyright_file_identifier']=>string()          //
+                ['data_preparer_identifier']=>string()           //
+                ['file_structure_version']=>integer()            //
+                ['logical_block_size']=>integer()                //
+                ['path_table_l_location']=>integer()             //
+                ['path_table_l_opt_location']=>integer()         //
+                ['path_table_m_location']=>integer()             //
+                ['path_table_m_opt_location']=>integer()         //
+                ['path_table_size']=>integer()                   //
+                ['publisher_identifier']=>string()               //
+                ['root_directory_record']=>string()              //
+                ['standard_identifier']=>string()                //
+                ['system_identifier']=>string()                  //
+                ['unused_1']=>string()                           //
+                ['unused_2']=>string()                           //
+                ['unused_3']=>string()                           //
+                ['unused_4']=>integer()                          //
+                ['volume_creation_date_time']=>string()          //
+                ['volume_descriptor_type']=>integer()            //
+                ['volume_descriptor_version']=>integer()         //
+                ['volume_effective_date_time']=>string()         //
+                ['volume_expiration_date_time']=>string()        //
+                ['volume_identifier']=>string()                  //
+                ['volume_modification_date_time']=>string()      //
+                ['volume_sequence_number']=>integer()            //
+                ['volume_set_identifier']=>string()              //
+                ['volume_set_size']=>integer()                   //
+                ['volume_space_size']=>integer()                 //
+            }                                                    //
+            ['system_identifier']=>string()                      //
+            ['volume_creation_date_time']=>integer()             //
+            ['volume_effective_date_time']=>boolean()            //
+            ['volume_expiration_date_time']=>boolean()           //
+            ['volume_identifier']=>string()                      //
+            ['volume_modification_date_time']=>integer()         //
+            ['volume_set_identifier']=>string()                  //
+        }                                                        //
+        ['supplementary_volume_descriptor']=>array() {           //
+            ['abstract_file_identifier']=>string()               //
+            ['application_identifier']=>string()                 //
+            ['bibliographic_file_identifier']=>string()          //
+            ['copyright_file_identifier']=>string()              //
+            ['data_preparer_identifier']=>string()               //
+            ['offset']=>integer()                                //
+            ['publisher_identifier']=>string()                   //
+            ['raw']=>array() {                                   //
+                ['abstract_file_identifier']=>string()           //
+                ['application_data']=>string()                   //
+                ['application_identifier']=>string()             //
+                ['bibliographic_file_identifier']=>string()      //
+                ['copyright_file_identifier']=>string()          //
+                ['data_preparer_identifier']=>string()           //
+                ['file_structure_version']=>integer()            //
+                ['logical_block_size']=>integer()                //
+                ['path_table_l_location']=>integer()             //
+                ['path_table_l_opt_location']=>integer()         //
+                ['path_table_m_location']=>integer()             //
+                ['path_table_m_opt_location']=>integer()         //
+                ['path_table_size']=>integer()                   //
+                ['publisher_identifier']=>string()               //
+                ['root_directory_record']=>string()              //
+                ['standard_identifier']=>string()                //
+                ['system_identifier']=>string()                  //
+                ['unused_1']=>string()                           //
+                ['unused_2']=>string()                           //
+                ['unused_3']=>string()                           //
+                ['unused_4']=>integer()                          //
+                ['volume_creation_date_time']=>string()          //
+                ['volume_descriptor_type']=>integer()            //
+                ['volume_descriptor_version']=>integer()         //
+                ['volume_effective_date_time']=>string()         //
+                ['volume_expiration_date_time']=>string()        //
+                ['volume_identifier']=>string()                  //
+                ['volume_modification_date_time']=>string()      //
+                ['volume_sequence_number']=>integer()            //
+                ['volume_set_identifier']=>string()              //
+                ['volume_set_size']=>integer()                   //
+                ['volume_space_size']=>integer()                 //
+            }                                                    //
+            ['system_identifier']=>string()                      //
+            ['volume_creation_date_time']=>integer()             //
+            ['volume_effective_date_time']=>boolean()            //
+            ['volume_expiration_date_time']=>boolean()           //
+            ['volume_identifier']=>string()                      //
+            ['volume_modification_date_time']=>integer()         //
+            ['volume_set_identifier']=>string()                  //
+        }                                                        //
+    }                                                            //
+
+
+    ['jpg']=>array() {    // JPEG - still image
+        ['exif']=>array() // data returned from PHP's exif_read_data() function
+    }                     //
+
+
+    ['la']=>array() {                        // LA - Lossless Audio (www.lossless-audio.com)
+        ['raw']=>array() {
+            ['format']=>integer()            //
+            ['flags']=>integer()             //
+        }                                    //
+        ['flags']=>array() {                 //
+            ['seekable']=>boolean()          //
+            ['high_compression']=>boolean()  //
+        }                                    //
+        ['bits_per_sample']=>integer()       //
+        ['bytes_per_sample']=>integer()      //
+        ['bytes_per_second']=>integer()      //
+        ['channels']=>integer()              //
+        ['compression_ratio']=>double()      //
+        ['format_size']=>integer()           //
+        ['header_size']=>integer()           //
+        ['original_crc']=>double()           //
+        ['sample_rate']=>integer()           //
+        ['samples']=>integer()               //
+        ['uncompressed_size']=>integer()     //
+        ['version']=>double()                //
+        ['version_major']=>integer()         //
+        ['version_minor']=>integer()         //
+        ['footerstart']=>double()            //
+    }
+
+
+    ['lpac']=>array() {                               // LPAC - Lossless Predictive Audio Compressor
+        ['block_length']=>integer()                   //
+        ['file_version']=>integer()                   //
+        ['flags']=>array() {                          //
+            ['16_bit']=>boolean()                     //
+            ['24_bit']=>boolean()                     //
+            ['adaptive_prediction_order']=>boolean()  //
+            ['adaptive_quantization']=>boolean()      //
+            ['fast_compress']=>boolean()              //
+            ['is_wave']=>boolean()                    //
+            ['joint_stereo']=>boolean()               //
+            ['max_prediction_order']=>integer()       //
+            ['quantization']=>integer()               //
+            ['random_access']=>boolean()              //
+            ['stereo']=>boolean()                     //
+        }                                             //
+        ['raw']=>array() {                            //
+            ['audio_type']=>integer()                 //
+            ['parameters']=>double()                  //
+        }                                             //
+        ['total_samples']=>integer()                  //
+    }                                                 //
+
+
+    ['lyrics3']=>array() {                // Lyrics3 - metainformation tags
+        ['comments']=>array() {           //
+            ['album']=>string()           //
+            ['artist']=>string()          //
+            ['author']=>string()          //
+            ['comment']=>string()         //
+            ['title']=>string()           //
+        }                                 //
+        ['flags']=>array() {              //
+            ['lyrics']=>boolean()         //
+            ['timestamps']=>boolean()     //
+        }                                 //
+        ['images']=>array() {             //
+            [<x>]=>array() {              //
+                ['description']=>string() //
+                ['filename']=>string()    //
+                ['timestamp']=>integer()  //
+            }                             //
+        }                                 //
+        ['raw']=>array() {                //
+            ['offset_start']=>integer()   //
+            ['offset_end']=>integer()     //
+            ['AUT']=>string()             //
+            ['EAL']=>string()             //
+            ['EAR']=>string()             //
+            ['ETT']=>string()             //
+            ['IMG']=>string()             //
+            ['IND']=>string()             //
+            ['INF']=>string()             //
+            ['LYR']=>string()             //
+            ['lyrics3tagsize']=>integer() //
+            ['lyrics3version']=>integer() //
+            ['unparsed']=>string()        //
+        }                                 //
+        ['synchedlyrics']=>array() {      //
+            [<x>]=>string()               //
+        }                                 //
+        ['unsynchedlyrics']=>string()     //
+    }                                     //
+
+
+    ['midi']=>array() {                         // MIDI (Musical Instrument Digital Interface) - sequenced music
+        ['comments']=>array() {                 //
+            ['comment']=>string()               //
+            ['copyright']=>string()             //
+        }                                       //
+        ['keysignature']=>array() {             //
+            [<x>]=>string()                     //
+        }                                       //
+        ['raw']=>array() {                      //
+            ['events']=>array() {               //
+                [<x>]=>array() {                //
+                    [<x>]=>array() {            //
+                        ['us_qnote']=>integer() //
+                    }                           //
+                }                               //
+            }                                   //
+            ['fileformat']=>integer()           //
+            ['headersize']=>integer()           //
+            ['ticksperqnote']=>integer()        //
+            ['track']=>array() {                //
+                [<x>]=>array() {                //
+                    ['instrument']=>string()    //
+                    ['instrumentid']=>integer() //
+                    ['name']=>string()          //
+                }                               //
+            }                                   //
+            ['tracks']=>integer()               //
+        }                                       //
+        ['timesignature']=>array() {            //
+            [<x>]=>string()                     //
+        }                                       //
+        ['totalticks']=>integer()               //
+    }                                           //
+
+
+    ['monkeys_audio']=>array() {                // Monkey's Audio - lossless audio compression
+        ['bitrate']=>double()                   //
+        ['bits_per_sample']=>integer()          //
+        ['channels']=>integer()                 //
+        ['compressed_size']=>integer()          //
+        ['compression']=>string()               //
+        ['compression_ratio']=>double()         //
+        ['flags']=>array() {                    //
+            ['24-bit']=>boolean()               //
+            ['8-bit']=>boolean()                //
+            ['crc-32']=>boolean()               //
+            ['no_wav_header']=>boolean()        //
+            ['peak_level']=>boolean()           //
+            ['seek_elements']=>boolean()        //
+        }                                       //
+        ['frames']=>integer()                   //
+        ['peak_level']=>integer()               //
+        ['peak_ratio']=>double()                //
+        ['playtime']=>double()                  //
+        ['raw']=>array() {                      //
+            ['header_tag']=>string()            //
+            ['nChannels']=>integer()            //
+            ['nCompressionLevel']=>integer()    //
+            ['nFinalFrameSamples']=>integer()   //
+            ['nFormatFlags']=>integer()         //
+            ['nPeakLevel']=>integer()           //
+            ['nSampleRate']=>integer()          //
+            ['nSeekElements']=>integer()        //
+            ['nTotalFrames']=>integer()         //
+            ['nVersion']=>integer()             //
+            ['nWAVHeaderBytes']=>integer()      //
+            ['nWAVTerminatingBytes']=>integer() //
+        }                                       //
+        ['sample_rate']=>integer()              //
+        ['samples']=>integer()                  //
+        ['samples_per_frame']=>integer()        //
+        ['uncompressed_size']=>integer()        //
+        ['version']=>double()                   //
+    }                                           //
+
+
+    ['mpc']=>array() {                          // MPC (Musepack) - lossy audio compression
+        ['header']=>array() {                   //
+            ['album_gain_db']=>integer()        //
+            ['album_peak']=>integer()           //
+            ['album_peak_db']=>boolean()        //
+            ['title_gain_db']=>integer()        //
+            ['title_peak']=>integer()           //
+            ['title_peak_db']=>boolean()        //
+            ['begin_loud']=>boolean()           //
+            ['end_loud']=>boolean()             //
+            ['encoder_version']=>string()       //
+            ['frame_count']=>integer()          //
+            ['intensity_stereo']=>boolean()     //
+            ['last_frame_length']=>integer()    //
+            ['max_level']=>integer()            //
+            ['max_subband']=>integer()          //
+            ['mid_side_stereo']=>boolean()      //
+            ['profile']=>string()               //
+            ['sample_rate']=>integer()          //
+            ['samples']=>integer()              //
+            ['size']=>integer()                 //
+            ['stream_major_version']=>integer() //
+            ['stream_minor_version']=>integer() //
+            ['true_gapless']=>boolean()         //
+            ['raw']=>array() {                  //
+                ['album_gain']=>integer()       //
+                ['album_peak']=>integer()       //
+                ['encoder_version']=>integer()  //
+                ['preamble']=>string()          //
+                ['profile']=>integer()          //
+                ['sample_rate']=>integer()      //
+                ['title_gain']=>integer()       //
+                ['title_peak']=>integer()       //
+            }                                   //
+        }                                       //
+    }                                           //
+
+
+    ['mpeg']=>array() {                                // MPEG (Motion Picture Experts Group) - MPEG video and/or MPEG audio (MP3/MP2/MP1)
+        ['audio']=>array() {                           //
+            ['LAME']=>array() {                        //
+                ['RGAD']=>array() {                    //
+                    ['peak_amplitude']=>double()       //
+                }                                      //
+                ['ath_type']=>integer()                //
+                ['audio_bytes']=>integer()             //
+                ['bitrate_min']=>integer()             //
+                ['encoder_delay']=>integer()           //
+                ['encoding_flags']=>array() {          //
+                    ['nogap_next']=>boolean()          //
+                    ['nogap_prev']=>boolean()          //
+                    ['nspsytune']=>boolean()           //
+                    ['nssafejoint']=>boolean()         //
+                }                                      //
+                ['end_padding']=>integer()             //
+                ['lame_tag_crc']=>integer()            //
+                ['lowpass_frequency']=>integer()       //
+                ['mp3_gain_db']=>double()              //
+                ['mp3_gain_factor']=>double()          //
+                ['mp3_gain_raw']=>integer()            //
+                ['music_crc']=>integer()               //
+                ['noise_shaping']=>integer()           //
+                ['noise_shaping_raw']=>integer()       //
+                ['not_optimal_quality']=>boolean()     //
+                ['not_optimal_quality_raw']=>integer() //
+                ['preset_used_id']=>integer()          //
+                ['short_version']=>string()            // ex: "LAME 3.93"
+                ['long_version']=>string()             // (pre-v3.90 only) ex: "LAME 3.88 (alpha)"
+                ['source_sample_freq']=>string()       //
+                ['source_sample_freq_raw']=>integer()  //
+                ['stereo_mode']=>string()              //
+                ['stereo_mode_raw']=>integer()         //
+                ['surround_info']=>string()            //
+                ['surround_info_id']=>integer()        //
+                ['tag_revision']=>integer()            //
+                ['vbr_method']=>string()               //
+                ['vbr_method_raw']=>integer()          //
+            }                                          //
+            ['VBR_bitrate']=>double()                  //
+            ['VBR_bytes']=>integer()                   //
+            ['VBR_frames']=>integer()                  //
+            ['VBR_method']=>string()                   //
+            ['VBR_scale']=>integer()                   //
+            ['bitrate']=>integer()                     //
+            ['bitrate_distribution']=>array() {        //
+                ['free']=>integer()                    //
+                ['8']=>integer()                       //
+                ['16']=>integer()                      //
+                ['24']=>integer()                      //
+                ['32']=>integer()                      //
+                ['40']=>integer()                      //
+                ['48']=>integer()                      //
+                ['56']=>integer()                      //
+                ['64']=>integer()                      //
+                ['80']=>integer()                      //
+                ['96']=>integer()                      //
+                ['112']=>integer()                     //
+                ['128']=>integer()                     //
+                ['144']=>integer()                     //
+                ['160']=>integer()                     //
+            }                                          //
+            ['bitrate_mode']=>string()                 //
+            ['channelmode']=>string()                  //
+            ['channels']=>integer()                    //
+            ['copyright']=>boolean()                   //
+            ['crc']=>integer()                         //
+            ['emphasis']=>string()                     //
+            ['frame_count']=>integer()                 //
+            ['framelength']=>integer()                 //
+            ['layer']=>integer()                       //
+            ['modeextension']=>string()                //
+            ['original']=>boolean()                    //
+            ['padding']=>boolean()                     //
+            ['private']=>boolean()                     //
+            ['protection']=>boolean()                  //
+            ['raw']=>array() {                         //
+                ['bitrate']=>integer()                 //
+                ['channelmode']=>integer()             //
+                ['copyright']=>integer()               //
+                ['emphasis']=>integer()                //
+                ['layer']=>integer()                   //
+                ['modeextension']=>integer()           //
+                ['original']=>integer()                //
+                ['padding']=>integer()                 //
+                ['private']=>integer()                 //
+                ['protection']=>integer()              //
+                ['sample_rate']=>integer()             //
+                ['synch']=>integer()                   //
+                ['version']=>integer()                 //
+            }                                          //
+            ['sample_rate']=>integer()                 //
+            ['stereo_distribution']=>array() {         //
+                ['dual channel']=>integer()            //
+                ['joint stereo']=>integer()            //
+                ['mono']=>integer()                    //
+                ['stereo']=>integer()                  //
+            }                                          //
+            ['toc']=>array() {                         //
+                [<x>]=>integer()                       //
+            }                                          //
+            ['version']=>string()                      //
+            ['version_distribution']=>array() {        //
+                [<x>]=>integer()                       //
+                [<x>]=>integer()                       //
+                ['2.5']=>integer()                     //
+            }                                          //
+            ['xing_flags']=>array() {                  //
+                ['bytes']=>boolean()                   //
+                ['frames']=>boolean()                  //
+                ['toc']=>boolean()                     //
+                ['vbr_scale']=>boolean()               //
+            }                                          //
+            ['xing_flags_raw']=>string()               //
+        }                                              //
+        ['video']=>array() {                           //
+            ['bitrate']=>integer()                     //
+            ['bitrate_mode']=>string()                 //
+            ['frame_rate']=>double()                   //
+            ['framesize_horizontal']=>integer()        //
+            ['framesize_vertical']=>integer()          //
+            ['pixel_aspect_ratio']=>double()           //
+            ['pixel_aspect_ratio_text']=>string()      //
+            ['raw']=>array() {                         //
+                ['bitrate']=>integer()                 //
+                ['constrained_param_flag']=>integer()  //
+                ['frame_rate']=>integer()              //
+                ['framesize_horizontal']=>integer()    //
+                ['framesize_vertical']=>integer()      //
+                ['intra_quant_flag']=>integer()        //
+                ['marker_bit']=>integer()              //
+                ['pixel_aspect_ratio']=>integer()      //
+                ['vbv_buffer_size']=>integer()         //
+            }                                          //
+        }                                              //
+    }                                                  //
+
+
+    ['nsv']=>array() {                     // NSV - Nullsoft Streaming Video
+        ['NSVf']=>array() {                //
+            ['TOC_entries_1']=>integer()   //
+            ['TOC_entries_2']=>integer()   //
+            ['file_size']=>integer()       //
+            ['header_length']=>integer()   //
+            ['identifier']=>string()       //
+            ['meta_size']=>integer()       //
+            ['metadata']=>string()         //
+            ['playtime_ms']=>integer()     //
+        }                                  //
+        ['NSVs']=>array() {                //
+            ['audio_codec']=>string()      //
+            ['frame_rate']=>double()       //
+            ['framerate_index']=>integer() //
+            ['identifier']=>string()       //
+            ['offset']=>integer()          //
+            ['resolution_x']=>integer()    //
+            ['resolution_y']=>integer()    //
+            ['unknown1b']=>integer()       //
+            ['unknown1c']=>integer()       //
+            ['unknown1d']=>integer()       //
+            ['unknown2a']=>integer()       //
+            ['unknown2b']=>integer()       //
+            ['unknown2c']=>integer()       //
+            ['unknown2d']=>integer()       //
+            ['unknown3a']=>integer()       //
+            ['unknown3b']=>integer()       //
+            ['unknown3c']=>integer()       //
+            ['unknown3d']=>integer()       //
+            ['video_codec']=>string()      //
+        }                                  //
+        ['comments']=>array() {            //
+            ['aspect']=>string()           //
+            ['title']=>string()            //
+        }                                  //
+    }                                      //
+
+
+    ['ofr']=>array() {                                   // OFR (OptimFROG) - lossless audio compression
+        ['COMP']=>array() {                              //
+            [<x>]=>array() {                             //
+                ['channel_configuration']=>string()      //
+                ['crc_32']=>boolean()                    //
+                ['encoder']=>string()                    //
+                ['offset']=>integer()                    //
+                ['raw']=>array() {                       //
+                    ['algorithm_id']=>integer()          //
+                    ['channel_configuration']=>integer() //
+                    ['encoder_id']=>integer()            //
+                    ['sample_type']=>integer()           //
+                }                                        //
+                ['sample_count']=>integer()              //
+                ['sample_type']=>string()                //
+                ['size']=>integer()                      //
+            }                                            //
+        }                                                //
+        ['HEAD']=>array() {                              //
+            ['offset']=>integer()                        //
+            ['size']=>integer()                          //
+        }                                                //
+        ['OFR ']=>array() {                              //
+            ['channel_config']=>integer()                //
+            ['channels']=>integer()                      //
+            ['compression']=>string()                    //
+            ['encoder']=>string()                        //
+            ['offset']=>integer()                        //
+            ['raw']=>array() {                           //
+                ['compression']=>integer()               //
+                ['encoder_id']=>integer()                //
+                ['sample_type']=>integer()               //
+            }                                            //
+            ['sample_rate']=>integer()                   //
+            ['sample_type']=>string()                    //
+            ['size']=>integer()                          //
+            ['total_samples']=>integer()                 //
+        }                                                //
+        ['TAIL']=>array() {                              //
+            ['offset']=>integer()                        //
+            ['size']=>integer()                          //
+        }                                                //
+    }                                                    //
+
+
+    ['ogg']=>array() {                           // OGG - container format for Ogg Vorbis, OggFLAC, Speex, etc
+        ['bitrate_average']=>double()            //
+        ['bitrate_max']=>integer()               //
+        ['bitrate_min']=>integer()               //
+        ['bitrate_nominal']=>integer()           //
+        ['bitstreamversion']=>integer()          //
+        ['blocksize_large']=>integer()           //
+        ['blocksize_small']=>integer()           //
+        ['comments']=>array() {                  // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+            [<key name>]=>array()                // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+        }                                        //
+        ['comments_raw']=>array() {              //
+            [<x>]=>array() {                     //
+                ['dataoffset']=>integer()        //
+                ['key']=>string()                //
+                ['size']=>integer()              //
+                ['value']=>string()              //
+            }                                    //
+        }                                        //
+        ['numberofchannels']=>integer()          //
+        ['pageheader']=>array() {                //
+            [<x>]=>array() {                     //
+                ['flags']=>array() {             //
+                    ['bos']=>boolean()           //
+                    ['eos']=>boolean()           //
+                    ['fresh']=>boolean()         //
+                }                                //
+                ['flags_raw']=>integer()         //
+                ['header_end_offset']=>integer() //
+                ['packet_type']=>integer()       //
+                ['page_checksum']=>double()      //
+                ['page_end_offset']=>integer()   //
+                ['page_length']=>integer()       //
+                ['page_segments']=>integer()     //
+                ['page_seqno']=>integer()        //
+                ['page_start_offset']=>integer() //
+                ['pcm_abs_position']=>integer()  //
+                ['segment_table']=>array() {     //
+                    [<x>]=>integer()             //
+                }                                //
+                ['stream_serialno']=>integer()   //
+                ['stream_structver']=>integer()  //
+                ['stream_type']=>string()        //
+            }                                    //
+            ['eos']=>array() {                   //
+                ['flags']=>array() {             //
+                    ['bos']=>boolean()           //
+                    ['eos']=>boolean()           //
+                    ['fresh']=>boolean()         //
+                }                                //
+                ['flags_raw']=>integer()         //
+                ['header_end_offset']=>integer() //
+                ['page_checksum']=>double()      //
+                ['page_end_offset']=>integer()   //
+                ['page_length']=>integer()       //
+                ['page_segments']=>integer()     //
+                ['page_seqno']=>integer()        //
+                ['page_start_offset']=>integer() //
+                ['pcm_abs_position']=>integer()  //
+                ['segment_table']=>array() {     //
+                    [<x>]=>integer()             //
+                }                                //
+                ['stream_serialno']=>integer()   //
+                ['stream_structver']=>integer()  //
+            }                                    //
+        }                                        //
+        ['samplerate']=>integer()                //
+        ['samples']=>integer()                   //
+        ['stop_bit']=>integer()                  //
+        ['vendor']=>string()                     //
+    }                                            //
+
+
+    ['png']=>array() {                                // PNG (Portable Network Graphics) - still image
+        ['IDAT']=>array() {                           //
+            [<x>]=>array() {                          //
+                ['header']=>array() {                 //
+                    ['crc']=>integer()                //
+                    ['data_length']=>integer()        //
+                    ['flags']=>array() {              //
+                        ['ancilliary']=>boolean()     //
+                        ['private']=>boolean()        //
+                        ['reserved']=>boolean()       //
+                        ['safe_to_copy']=>boolean()   //
+                    }                                 //
+                    ['type_raw']=>double()            //
+                    ['type_text']=>string()           //
+                }                                     //
+            }                                         //
+        }                                             //
+        ['IEND']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+        }                                             //
+        ['IHDR']=>array() {                           //
+            ['color_type']=>array() {                 //
+                ['alpha']=>boolean()                  //
+                ['palette']=>boolean()                //
+                ['true_color']=>boolean()             //
+            }                                         //
+            ['compression_method_text']=>string()     //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['height']=>integer()                     //
+            ['raw']=>array() {                        //
+                ['bit_depth']=>integer()              //
+                ['color_type']=>integer()             //
+                ['compression_method']=>integer()     //
+                ['filter_method']=>integer()          //
+                ['interlace_method']=>integer()       //
+            }                                         //
+            ['width']=>integer()                      //
+        }                                             //
+        ['PLTE']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            [<x>]=>integer()                          //
+        }                                             //
+        ['comments']=>array() {                       // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+            [<key name>]=>array()                     // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+        }                                             //
+        ['gAMA']=>array() {                           //
+            ['gamma']=>double()                       //
+            ['header']=>array() {                     //
+                ['crc']=>integer()                    //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+        }                                             //
+        ['oFFs']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['position_x']=>integer()                 //
+            ['position_y']=>integer()                 //
+            ['unit']=>string()                        //
+            ['unit_specifier']=>integer()             //
+        }                                             //
+        ['pHYs']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>integer()                    //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['pixels_per_unit_x']=>integer()          //
+            ['pixels_per_unit_y']=>integer()          //
+            ['unit']=>string()                        //
+            ['unit_specifier']=>integer()             //
+        }                                             //
+        ['pcLb']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+        }                                             //
+        ['tEXt']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>integer()                    //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['keyword']=>string()                     //
+            ['text']=>string()                        //
+        }                                             //
+        ['tIME']=>array() {                           //
+            ['day']=>integer()                        //
+            ['header']=>array() {                     //
+                ['crc']=>integer()                    //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['hour']=>integer()                       //
+            ['minute']=>integer()                     //
+            ['month']=>integer()                      //
+            ['second']=>integer()                     //
+            ['unix']=>integer()                       //
+            ['year']=>integer()                       //
+        }                                             //
+        ['tRNS']=>array() {                           //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['transparent_color_blue']=>integer()     //
+            ['transparent_color_green']=>integer()    //
+            ['transparent_color_red']=>integer()      //
+        }                                             //
+        ['zTXt']=>array() {                           //
+            ['compressed_text']=>string()             //
+            ['compression_method']=>integer()         //
+            ['compression_method_text']=>string()     //
+            ['header']=>array() {                     //
+                ['crc']=>double()                     //
+                ['data']=>string()                    //
+                ['data_length']=>integer()            //
+                ['flags']=>array() {                  //
+                    ['ancilliary']=>boolean()         //
+                    ['private']=>boolean()            //
+                    ['reserved']=>boolean()           //
+                    ['safe_to_copy']=>boolean()       //
+                }                                     //
+                ['type_raw']=>double()                //
+                ['type_text']=>string()               //
+            }                                         //
+            ['keyword']=>string()                     //
+            ['text']=>string()                        //
+        }                                             //
+    }                                                 //
+
+
+    ['quicktime']=>array() {               // Quicktime - video/audio
+        ['']=>array() {                    //
+            ['name']=>boolean()            //
+            ['offset']=>integer()          //
+            ['size']=>integer()            //
+        }                                  //
+        ['audio']=>array() {               //
+            ['bit_depth']=>integer()       //
+            ['channels']=>integer()        //
+            ['codec']=>string()            //
+            ['sample_rate']=>double()      //
+        }                                  //
+        ['free']=>array() {                //
+            ['name']=>string()             //
+            ['offset']=>integer()          //
+            ['size']=>integer()            //
+        }                                  //
+        ['mdat']=>array() {                //
+            ['name']=>string()             //
+            ['offset']=>integer()          //
+            ['size']=>integer()            //
+        }                                  //
+        ['moov']=>array() {                //
+            ['hierarchy']=>string()        //
+            ['name']=>string()             //
+            ['offset']=>integer()          //
+            ['size']=>integer()            //
+            ['subatoms']=>array()          // This is an undocumentably-complex recursive array, typically containing a huge amount of seemingly disorganized data. Avoid this like the plague.
+        }                                  //
+        ['time_scale']=>integer()          //
+        ['display_scale']=>integer()       // 1 = normal; 0.5 = half; 2 = double
+        ['video']=>array() {               //
+            ['codec']=>string()            //
+            ['color_depth']=>integer()     //
+            ['color_depth_name']=>string() //
+            ['resolution_x']=>double()     //
+            ['resolution_y']=>double()     //
+        }                                  //
+        ['wide']=>array() {                //
+            ['name']=>string()             //
+            ['offset']=>integer()          //
+            ['size']=>integer()            //
+        }                                  //
+    }                                      //
+
+
+    ['real']=>array() {                           // Real (RealAudio / RealVideo) - audio/video
+        ['chunks']=>array() {                     //
+            [<x>]=>array() {                      //
+                ['file_version']=>integer()       //
+                ['headers_count']=>integer()      //
+                ['length']=>integer()             //
+                ['name']=>string()                //
+                ['object_version']=>integer()     //
+                ['offset']=>integer()             //
+            }                                     //
+            [<x>]=>array() {                      //
+                ['avg_bit_rate']=>integer()       //
+                ['avg_packet_size']=>integer()    //
+                ['data_offset']=>integer()        //
+                ['duration']=>integer()           //
+                ['flags']=>array() {              //
+                    ['live_broadcast']=>boolean() //
+                    ['perfect_play']=>boolean()   //
+                    ['save_enabled']=>boolean()   //
+                }                                 //
+                ['flags_raw']=>integer()          //
+                ['index_offset']=>integer()       //
+                ['length']=>integer()             //
+                ['max_bit_rate']=>integer()       //
+                ['max_packet_size']=>integer()    //
+                ['name']=>string()                //
+                ['num_packets']=>integer()        //
+                ['num_streams']=>integer()        //
+                ['object_version']=>integer()     //
+                ['offset']=>integer()             //
+                ['preroll']=>integer()            //
+            }                                     //
+        }                                         //
+        ['comments']=>array() {                   //
+            ['artist']=>string()                  //
+            ['comment']=>string()                 //
+            ['title']=>string()                   //
+        }                                         //
+    }                                             //
+
+
+    ['riff']=>array() {                                     // RIFF (Resource Interchange File Format) - audio/video container format (AVI, WAV, CDDA, etc)
+        ['AIFC']=>array() {                                 //
+            ['COMM']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['FVER']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['INST']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['MARK']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['SSND']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+        }                                                   //
+        ['AIFF']=>array() {                                 //
+            ['(c) ']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['COMM']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['SSND']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+        }                                                   //
+        ['AVI ']=>array() {                                 //
+            ['JUNK']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['hdrl']=>array() {                             //
+                ['avih']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['odml']=>array() {                         //
+                    ['dmlh']=>array() {                     //
+                        [<x>]=>array() {                    //
+                            ['data']=>string()              //
+                            ['offset']=>integer()           //
+                            ['size']=>integer()             //
+                        }                                   //
+                    }                                       //
+                }                                           //
+                ['strl']=>array() {                         //
+                    ['JUNK']=>array() {                     //
+                        [<x>]=>array() {                    //
+                            ['offset']=>integer()           //
+                            ['size']=>integer()             //
+                        }                                   //
+                    }                                       //
+                    ['strf']=>array() {                     //
+                        [<x>]=>array() {                    //
+                            ['data']=>string()              //
+                            ['offset']=>integer()           //
+                            ['size']=>integer()             //
+                        }                                   //
+                    }                                       //
+                    ['strh']=>array() {                     //
+                        [<x>]=>array() {                    //
+                            ['data']=>string()              //
+                            ['offset']=>integer()           //
+                            ['size']=>integer()             //
+                        }                                   //
+                    }                                       //
+                    ['strn']=>array() {                     //
+                        [<x>]=>array() {                    //
+                            ['data']=>string()              //
+                            ['offset']=>integer()           //
+                            ['size']=>integer()             //
+                        }                                   //
+                    }                                       //
+                }                                           //
+            }                                               //
+            ['idx1']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['movi']=>array() {                             //
+                ['offset']=>integer()                       //
+                ['size']=>integer()                         //
+            }                                               //
+        }                                                   //
+        ['CDDA']=>array() {                                 //
+            ['fmt ']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['disc_id']=>integer()                  //
+                    ['offset']=>integer()                   //
+                    ['playtime_frames']=>integer()          //
+                    ['playtime_seconds']=>double()          //
+                    ['size']=>integer()                     //
+                    ['start_offset_frame']=>integer()       //
+                    ['start_offset_seconds']=>double()      //
+                    ['track_num']=>integer()                //
+                    ['unknown1']=>integer()                 //
+                    ['unknown6']=>integer()                 //
+                    ['unknown7']=>integer()                 //
+                }                                           //
+            }                                               //
+        }                                                   //
+        ['WAVE']=>array() {                                 //
+            ['DISP']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['INFO']=>array() {                             //
+                ['IART']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ICMT']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ICOP']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['IENG']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['IGNR']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['IKEY']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['IMED']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['INAM']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ISBJ']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ISFT']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ISRC']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ISRF']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+                ['ITCH']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['data']=>string()                  //
+                        ['offset']=>integer()               //
+                        ['size']=>integer()                 //
+                    }                                       //
+                }                                           //
+            }                                               //
+            ['MEXT']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['anciliary_data_length']=>integer()    //
+                    ['data']=>string()                      //
+                    ['flags']=>array() {                    //
+                        ['anciliary_data_free']=>boolean()  //
+                        ['anciliary_data_left']=>boolean()  //
+                        ['anciliary_data_right']=>boolean() //
+                        ['homogenous']=>boolean()           //
+                    }                                       //
+                    ['offset']=>integer()                   //
+                    ['raw']=>array() {                      //
+                        ['anciliary_data_def']=>integer()   //
+                        ['sound_information']=>integer()    //
+                    }                                       //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['bext']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['author']=>string()                    //
+                    ['bwf_version']=>integer()              //
+                    ['coding_history']=>array() {           //
+                        [<x>]=>string()                     //
+                    }                                       //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['origin_date']=>string()               //
+                    ['origin_date_unix']=>integer()         //
+                    ['origin_time']=>string()               //
+                    ['reference']=>string()                 //
+                    ['reserved']=>integer()                 //
+                    ['size']=>integer()                     //
+                    ['time_reference']=>integer()           //
+                    ['title']=>string()                     //
+                }                                           //
+            }                                               //
+            ['cart']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['artist']=>string()                    //
+                    ['category']=>string()                  //
+                    ['classification']=>string()            //
+                    ['client_id']=>string()                 //
+                    ['cut_id']=>string()                    //
+                    ['data']=>string()                      //
+                    ['end_date']=>string()                  //
+                    ['end_time']=>string()                  //
+                    ['offset']=>integer()                   //
+                    ['out_cue']=>string()                   //
+                    ['post_time']=>array() {                //
+                        [<x>]=>array() {                    //
+                            ['timer_value']=>integer()      //
+                            ['usage_fourcc']=>string()      //
+                        }                                   //
+                    }                                       //
+                    ['producer_app_id']=>string()           //
+                    ['producer_app_version']=>string()      //
+                    ['size']=>integer()                     //
+                    ['start_date']=>string()                //
+                    ['start_time']=>string()                //
+                    ['tag_text']=>array() {                 //
+                        [<x>]=>string()                     //
+                    }                                       //
+                    ['title']=>string()                     //
+                    ['url']=>string()                       //
+                    ['user_defined_text']=>string()         //
+                    ['version']=>string()                   //
+                    ['zero_db_reference']=>integer()        //
+                }                                           //
+            }                                               //
+            ['data']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['fact']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['fmt ']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+            ['rgad']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['data']=>string()                      //
+                    ['offset']=>integer()                   //
+                    ['size']=>integer()                     //
+                }                                           //
+            }                                               //
+        }                                                   //
+        ['audio']=>array() {                                //
+            [<x>]=>array() {                                //
+                ['bitrate']=>integer()                      //
+                ['bits_per_sample']=>integer()              //
+                ['channels']=>integer()                     //
+                ['codec']=>string()                         //
+                ['sample_rate']=>integer()                  //
+            }                                               //
+            ['bits_per_sample']=>integer()                  //
+            ['channels']=>integer()                         //
+            ['codec_fourcc']=>string()                      //
+            ['codec_name']=>string()                        //
+            ['sample_rate']=>integer()                      //
+            ['total_samples']=>integer()                    //
+        }                                                   //
+        ['comments']=>array() {                             // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+            [<key name>]=>array()                           // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+        }                                                   //
+        ['header_size']=>integer()                          //
+        ['raw']=>array() {                                  //
+            ['avih']=>array() {                             //
+                ['dwFlags']=>integer()                      //
+                ['dwHeight']=>integer()                     //
+                ['dwInitialFrames']=>integer()              //
+                ['dwLength']=>integer()                     //
+                ['dwMaxBytesPerSec']=>integer()             //
+                ['dwMicroSecPerFrame']=>integer()           //
+                ['dwPaddingGranularity']=>integer()         //
+                ['dwRate']=>integer()                       //
+                ['dwScale']=>integer()                      //
+                ['dwStart']=>integer()                      //
+                ['dwStreams']=>integer()                    //
+                ['dwSuggestedBufferSize']=>integer()        //
+                ['dwTotalFrames']=>integer()                //
+                ['dwWidth']=>integer()                      //
+                ['flags']=>array() {                        //
+                    ['capturedfile']=>boolean()             //
+                    ['copyrighted']=>boolean()              //
+                    ['hasindex']=>boolean()                 //
+                    ['interleaved']=>boolean()              //
+                    ['mustuseindex']=>boolean()             //
+                    ['trustcktype']=>boolean()              //
+                }                                           //
+            }                                               //
+            ['fact']=>array() {                             //
+                ['NumberOfSamples']=>integer()              //
+            }                                               //
+            ['fmt ']=>array() {                             //
+                ['nAvgBytesPerSec']=>integer()              //
+                ['wBitsPerSample']=>integer()               //
+                ['nBlockAlign']=>integer()                  //
+                ['nChannels']=>integer()                    //
+                ['nSamplesPerSec']=>integer()               //
+                ['wFormatTag']=>integer()                   //
+            }                                               //
+            ['rgad']=>array() {                             //
+                ['audiophile']=>array() {                   //
+                    ['adjustment']=>integer()               //
+                    ['name']=>integer()                     //
+                    ['originator']=>integer()               //
+                    ['signbit']=>integer()                  //
+                }                                           //
+                ['fPeakAmplitude']=>double()                //
+                ['nAudiophileRgAdjust']=>integer()          //
+                ['nRadioRgAdjust']=>integer()               //
+                ['radio']=>array() {                        //
+                    ['adjustment']=>integer()               //
+                    ['name']=>integer()                     //
+                    ['originator']=>integer()               //
+                    ['signbit']=>integer()                  //
+                }                                           //
+            }                                               //
+            ['strf']=>array() {                             //
+                ['auds']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['nAvgBytesPerSec']=>integer()      //
+                        ['wBitsPerSample']=>integer()       //
+                        ['nBlockAlign']=>integer()          //
+                        ['nChannels']=>integer()            //
+                        ['nSamplesPerSec']=>integer()       //
+                        ['wFormatTag']=>integer()           //
+                    }                                       //
+                }                                           //
+                ['vids']=>array() {                         //
+                    [<x>]=>array() {                        //
+                        ['biBitCount']=>integer()           //
+                        ['biClrImportant']=>integer()       //
+                        ['biClrUsed']=>integer()            //
+                        ['biHeight']=>integer()             //
+                        ['biPlanes']=>integer()             //
+                        ['biSize']=>integer()               //
+                        ['biSizeImage']=>integer()          //
+                        ['biWidth']=>integer()              //
+                        ['biXPelsPerMeter']=>integer()      //
+                        ['biYPelsPerMeter']=>integer()      //
+                        ['fourcc']=>string()                //
+                    }                                       //
+                }                                           //
+            }                                               //
+            ['strh']=>array() {                             //
+                [<x>]=>array() {                            //
+                    ['dwFlags']=>integer()                  //
+                    ['dwInitialFrames']=>integer()          //
+                    ['dwLength']=>integer()                 //
+                    ['dwQuality']=>integer()                //
+                    ['dwRate']=>integer()                   //
+                    ['dwSampleSize']=>integer()             //
+                    ['dwScale']=>integer()                  //
+                    ['dwStart']=>integer()                  //
+                    ['dwSuggestedBufferSize']=>integer()    //
+                    ['fccHandler']=>string()                //
+                    ['fccType']=>string()                   //
+                    ['rcFrame']=>integer()                  //
+                    ['wLanguage']=>integer()                //
+                    ['wPriority']=>integer()                //
+                }                                           //
+            }                                               //
+        }                                                   //
+        ['rgad']=>array() {                                 //
+            ['audiophile']=>array() {                       //
+                ['adjustment']=>double()                    //
+                ['name']=>string()                          //
+                ['originator']=>string()                    //
+            }                                               //
+            ['peakamplitude']=>double()                     //
+            ['radio']=>array() {                            //
+                ['adjustment']=>double()                    //
+                ['name']=>string()                          //
+                ['originator']=>string()                    //
+            }                                               //
+        }                                                   //
+        ['video']=>array() {                                //
+            [<x>]=>array() {                                //
+                ['codec']=>string()                         //
+                ['frame_height']=>integer()                 //
+                ['frame_rate']=>double()                    //
+                ['frame_width']=>integer()                  //
+            }                                               //
+        }                                                   //
+        ['litewave']=>array() {                             // http://www.clearjump.com
+            ['raw']=>array() {                              //
+                ['compression_method']=>integer()           // 1=lossy; 2=lossless
+                ['compression_flags']=>integer()            //
+                ['m_dwScale']=>integer()                    // scalefactor for lossy compression - related to m_wQuality as: $m_wQuality = round((2000 - $m_dwScale) / 20)
+                ['m_dwBlockSize']=>integer()                // number of samples in encoded blocks
+                ['m_wQuality']=>integer()                   // quality factor (0=most compressed lossy; 99=best quality lossy; 100=lossless)
+                ['m_wMarkDistance']=>integer()              // distance between marks in bytes
+                ['m_wReserved']=>integer()                  //
+                ['m_dwOrgSize']=>integer()                  // original file size in bytes
+                ['m_bFactExists']=>integer()                // indicates if 'fact' chunk exists in the original file
+                ['m_dwRiffChunkSize']=>integer()            // riff chunk size in the original file
+            }                                               //
+            ['quality_factor']=>integer()                   // alias of ['raw']['m_wQuality']
+        }                                                   //
+    }                                                       //
+
+
+    ['shn']=>array() {             // Shorten - lossless audio compression
+        ['seektable']=>array() {   //
+            ['length']=>integer()  //
+            ['offset']=>integer()  //
+            ['present']=>boolean() //
+        }                          //
+        ['version']=>integer()     //
+    }                              //
+
+
+    ['swf']=>array() {                  // SWF - ShockWave Flash (www.openswf.org)
+        ['header']=>array() {           //
+            ['frame_count']=>integer()  //
+            ['frame_height']=>integer() //
+            ['frame_width']=>integer()  //
+            ['length']=>integer()       //
+            ['signature']=>string()     //
+            ['version']=>integer()      //
+        }                               //
+        ['bgcolor']=>string()           //
+        ['tags']=>array()               //
+    }                                   //
+
+
+    ['voc']=>array() {                            // VOC - SoundBlaster VOC audio format
+        ['blocks']=>array() {                     //
+            [<x>]=>array() {                      //
+                ['bits_per_sample']=>integer()    //
+                ['block_offset']=>integer()       //
+                ['block_size']=>integer()         //
+                ['block_type_id']=>integer()      //
+                ['channels']=>integer()           //
+                ['compression_name']=>string()    //
+                ['compression_type']=>integer()   //
+                ['pack_method']=>integer()        //
+                ['sample_rate']=>integer()        //
+                ['sample_rate_id']=>integer()     //
+                ['stereo']=>boolean()             //
+                ['time_constant']=>integer()      //
+                ['wFormat']=>integer()            //
+            }                                     //
+        }                                         //
+        ['compressed_bits_per_sample']=>integer() //
+        ['header']=>array() {                     //
+            ['datablock_offset']=>integer()       //
+            ['major_version']=>integer()          //
+            ['minor_version']=>integer()          //
+        }                                         //
+    }                                             //
+
+
+    ['vqf']=>array() {                    // VQF - transform-domain weighted interleave Vector Quantization Format (lossy audio)
+        ['COMM']=>array() {               //
+            ['bitrate']=>integer()        //
+            ['channel_mode']=>integer()   //
+            ['sample_rate']=>integer()    //
+            ['security_level']=>integer() //
+        }                                 //
+        ['DSIZ']=>integer()               //
+        ['comments']=>array() {           // array of array of strings containing best data from any available metainformation tag (APE, ID3v2, ID3v1, Lyrics3, Vorbis, ASF, RIFF, Real, etc.)
+            [<key name>]=>array()         // <key name> can be anything, usually 'artist', 'title', etc. Contains array of one or more values (eg: multiple artists are possible)
+        }                                 //
+        ['raw']=>array() {                //
+            ['header_tag']=>string()      //
+            ['size']=>integer()           //
+            ['version']=>string()         //
+        }                                 //
+    }                                     //
+
+
+    ['wavpack']=>array() {           // WavPack - lossless audio compression
+        ['bits']=>integer()          //
+        ['crc1']=>double()           //
+        ['crc2']=>integer()          //
+        ['extension']=>string()      //
+        ['extra_bc']=>string()       //
+        ['extras']=>string()         //
+        ['flags_raw']=>integer()     //
+        ['offset']=>integer()        //
+        ['shift']=>integer()         //
+        ['size']=>integer()          //
+        ['total_samples']=>integer() //
+        ['version']=>integer()       //
+    }                                //
+
+
+    ['zip']=>array() {                                           // ZIP - lossless data compression
+        ['central_directory']=>array() {                         //
+            [<x>]=>array() {                                     //
+                ['compressed_size']=>integer()                   //
+                ['compression_method']=>string()                 //
+                ['create_version']=>string()                     //
+                ['entry_offset']=>integer()                      //
+                ['extract_version']=>string()                    //
+                ['filename']=>string()                           //
+                ['flags']=>array() {                             //
+                    ['compression_speed']=>string()              //
+                    ['data_descriptor_used']=>boolean()          //
+                    ['encrypted']=>boolean()                     //
+                }                                                //
+                ['host_os']=>string()                            //
+                ['last_modified_timestamp']=>integer()           //
+                ['offset']=>integer()                            //
+                ['raw']=>array() {                               //
+                    ['compressed_size']=>integer()               //
+                    ['compression_method']=>integer()            //
+                    ['crc_32']=>double()                         //
+                    ['create_version']=>integer()                //
+                    ['disk_number_start']=>integer()             //
+                    ['external_file_attrib']=>double()           //
+                    ['extra_field_length']=>integer()            //
+                    ['extract_version']=>integer()               //
+                    ['file_comment_length']=>integer()           //
+                    ['filename_length']=>integer()               //
+                    ['general_flags']=>integer()                 //
+                    ['internal_file_attrib']=>integer()          //
+                    ['last_mod_file_date']=>integer()            //
+                    ['last_mod_file_time']=>integer()            //
+                    ['local_header_offset']=>integer()           //
+                    ['signature']=>integer()                     //
+                    ['uncompressed_size']=>integer()             //
+                }                                                //
+                ['uncompressed_size']=>integer()                 //
+            }                                                    //
+        }                                                        //
+        ['comments']=>array() {                                  //
+            ['comment']=>string()                                //
+        }                                                        //
+        ['compressed_size']=>integer()                           //
+        ['compression_method']=>string()                         //
+        ['compression_speed']=>string()                          //
+        ['end_central_directory']=>array() {                     //
+            ['comment']=>string()                                //
+            ['comment_length']=>integer()                        //
+            ['directory_entries_this_disk']=>integer()           //
+            ['directory_entries_total']=>integer()               //
+            ['directory_offset']=>integer()                      //
+            ['directory_size']=>integer()                        //
+            ['disk_number_current']=>integer()                   //
+            ['disk_number_start_directory']=>integer()           //
+            ['offset']=>integer()                                //
+            ['signature']=>integer()                             //
+        }                                                        //
+        ['entries']=>array() {                                   //
+            [<x>]=>array() {                                     //
+                ['compressed_size']=>integer()                   //
+                ['compression_method']=>string()                 //
+                ['extract_version']=>string()                    //
+                ['filename']=>string()                           //
+                ['flags']=>array() {                             //
+                    ['compression_speed']=>string()              //
+                    ['data_descriptor_used']=>boolean()          //
+                    ['encrypted']=>boolean()                     //
+                }                                                //
+                ['host_os']=>string()                            //
+                ['last_modified_timestamp']=>integer()           //
+                ['offset']=>integer()                            //
+                ['raw']=>array() {                               //
+                    ['compressed_size']=>integer()               //
+                    ['compression_method']=>integer()            //
+                    ['crc_32']=>integer()                        //
+                    ['extra_field_length']=>integer()            //
+                    ['extract_version']=>integer()               //
+                    ['filename_length']=>integer()               //
+                    ['general_flags']=>integer()                 //
+                    ['last_mod_file_date']=>integer()            //
+                    ['last_mod_file_time']=>integer()            //
+                    ['signature']=>integer()                     //
+                    ['uncompressed_size']=>integer()             //
+                }                                                //
+                ['uncompressed_size']=>integer()                 //
+            }                                                    //
+        }                                                        //
+        ['entries_count']=>integer()                             //
+        ['files']=>array() {                                     // multidimensional tree-structure array listing of all files and directories in image
+            [<directory name>]=>array()                          // entries of type array are directories (key is directory name), may contain files and/or other subdirectories
+            [<file name>]=>integer()                             // entries of type integer are files (key is file name, value is file size in bytes)
+        }                                                        //
+        ['uncompressed_size']=>integer()                         //
+    }                                                            //
+}                                                                //
diff --git a/apps/media/img/jplayer.blue.monday.jpg b/apps/media/img/jplayer.blue.monday.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..29c9382df7425f5b3e006dd540616c9d92540c7f
Binary files /dev/null and b/apps/media/img/jplayer.blue.monday.jpg differ
diff --git a/apps/media/img/jplayer.blue.monday.png b/apps/media/img/jplayer.blue.monday.png
new file mode 100644
index 0000000000000000000000000000000000000000..16cf2892993cb11340189e1158b3f810aa9be221
Binary files /dev/null and b/apps/media/img/jplayer.blue.monday.png differ
diff --git a/apps/media/img/pbar-ani.gif b/apps/media/img/pbar-ani.gif
new file mode 100644
index 0000000000000000000000000000000000000000..0dfd45b885a2dd69a4c16febc5e886300cdb08e0
Binary files /dev/null and b/apps/media/img/pbar-ani.gif differ
diff --git a/apps/media/index.php b/apps/media/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..fe724b45ddf2e850809e35be21a3d30f8fa73d44
--- /dev/null
+++ b/apps/media/index.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+require_once('lib_collection.php');
+require_once('lib_scanner.php');
+
+OC_Util::addScript('media','player');
+OC_Util::addScript('media','music');
+OC_Util::addScript('media','playlist');
+OC_Util::addScript('media','collection');
+OC_Util::addScript('media','scanner');
+OC_Util::addScript('media','jquery.jplayer.min');
+OC_Util::addStyle('media','player');
+OC_Util::addStyle('media','music');
+
+OC_App::setActiveNavigationEntry( 'media_index' );
+
+$tmpl = new OC_Template( 'media', 'music', 'user' );
+$tmpl->printPage();
+?>
+ 
diff --git a/apps/media/js/Jplayer.swf b/apps/media/js/Jplayer.swf
new file mode 100644
index 0000000000000000000000000000000000000000..c213fd578e52c04c9d53e32fb8038b75c4d49802
Binary files /dev/null and b/apps/media/js/Jplayer.swf differ
diff --git a/apps/media/js/collection.js b/apps/media/js/collection.js
new file mode 100644
index 0000000000000000000000000000000000000000..13eb0aff7d98a2901c8944e5900bea93384ef240
--- /dev/null
+++ b/apps/media/js/collection.js
@@ -0,0 +1,320 @@
+Collection={
+	artists:[],
+	albums:[],
+	songs:[],
+	artistsById:{},
+	albumsById:{},
+	loaded:false,
+	loading:false,
+	loadedListeners:[],
+	load:function(ready){
+		if(ready){
+			Collection.loadedListeners.push(ready);
+		}
+		if(!Collection.loading){
+			Collection.loading=true;
+			$.ajax({
+				url: OC.linkTo('media','ajax/api.php')+'?action=get_collection',
+				dataType: 'json',
+				success: function(data){
+					//normalize the data
+					for(var i=0;i<data.artists.length;i++){
+						var artist=data.artists[i];
+						var artistData={name:artist.artist_name,songs:[],albums:[]};
+						Collection.artistsById[artist.artist_id]=artistData;
+						Collection.artists.push(artistData);
+					}
+					for(var i=0;i<data.albums.length;i++){
+						var album=data.albums[i];
+						var artistName=Collection.artistsById[album.album_artist].name;
+						var albumData={name:album.album_name,artist:artistName,songs:[]};
+						Collection.albumsById[album.album_id]=albumData;
+						Collection.albums.push(albumData);
+						Collection.artistsById[album.album_artist].albums.push(albumData);
+					}
+					for(var i=0;i<data.songs.length;i++){
+						var song=data.songs[i];
+						if(Collection.artistsById[song.song_artist] && Collection.albumsById[song.song_album]){
+							var songData={
+								name:song.song_name,
+								artist:Collection.artistsById[song.song_artist].name,
+								album:Collection.albumsById[song.song_album].name,
+								lastPlayed:song.song_lastplayed,
+								length:song.song_length,
+								path:song.song_path,
+								playCount:song.song_playcount,
+							};
+							Collection.songs.push(songData);
+							Collection.artistsById[song.song_artist].songs.push(songData);
+							Collection.albumsById[song.song_album].songs.push(songData);
+						}
+					}
+					
+					Collection.loaded=true;
+					Collection.loading=false;
+					for(var i=0;i<Collection.loadedListeners.length;i++){
+						Collection.loadedListeners[i]();
+					}
+					if(data.songs.length==0){
+						$('#scan input.start').val(t('media','Scan Collection'));
+						$('#scan input.start').click();
+					}
+					
+				}
+			});
+		}
+	},
+	display:function(){
+		if(Collection.parent){
+			Collection.parent.show();
+		}
+		if(!Collection.loaded){
+			Collection.load(Collection.display)
+		}else{
+			if(Collection.parent){
+				Collection.parent.find('tr:not(.template)').remove();
+				var template=Collection.parent.find('tr.template');
+				var lastArtist='';
+				var lastAlbum='';
+				$.each(Collection.artists,function(i,artist){
+					if(artist.name && artist.songs.length>0){
+						var tr=template.clone().removeClass('template');
+						tr.find('td.title a').text(artist.songs.length+' '+t('media','songs'));
+						tr.find('td.album a').text(artist.albums.length+' '+t('media','albums'));
+						tr.find('td.artist a').text(artist.name);
+						tr.data('artistData',artist);
+						tr.find('td.artist a').click(function(event){
+							event.preventDefault();
+							PlayList.add(artist);
+							PlayList.play(0);
+							Collection.parent.find('tr').removeClass('active');
+							$('tr[data-artist="'+artist.name+'"]').addClass('active');
+						});
+						var expander=$('<a class="expander">&gt;</a>');
+						expander.data('expanded',false);
+						expander.click(function(event){
+							var tr=$(this).parent().parent();
+							if(expander.data('expanded')){
+								Collection.hideArtist(tr.data('artist'));
+							}else{
+								Collection.showArtist(tr.data('artist'));
+							}
+						});
+                        tr.find('td.artist').addClass('buttons');
+                        Collection.addButtons(tr,artist);
+						tr.children('td.artist').append(expander);
+						tr.attr('data-artist',artist.name);
+						Collection.parent.find('tbody').append(tr);
+					}
+				});
+			}
+		}
+	},
+	showArtist:function(artist){
+		var tr=Collection.parent.find('tr[data-artist="'+artist+'"]');
+		var nextRow=tr.next();
+		var artist=tr.data('artistData');
+		var first=true;
+		$.each(artist.albums,function(foo,album){
+			$.each(album.songs,function(i,song){
+				if(first){
+					newRow=tr;
+				}else{
+					var newRow=tr.clone();
+				}
+                newRow.find('.expander').remove();
+				if(i==0){
+					newRow.find('td.album a').text(album.name);
+					newRow.find('td.album a').click(function(event){
+						event.preventDefault();
+						PlayList.add(album);
+						PlayList.play(0);
+						Collection.parent.find('tr').removeClass('active');
+						$('tr[data-album="'+album.name+'"]').addClass('active');
+					});
+                    var expander=$('<a class="expander">v </a>');
+                    expander.data('expanded',true);
+                    expander.click(function(event){
+                        var tr=$(this).parent().parent();
+                        if(expander.data('expanded')) {
+                            Collection.hideAlbum(tr.data('artist'),tr.data('album'));
+                        } else {
+                            Collection.showAlbum(tr.data('artist'),tr.data('album'));
+                        }
+                    });
+                    newRow.children('td.artist').append(expander);
+                    Collection.addButtons(newRow,album);
+				} else {
+					newRow.find('td.album a').text('');
+                    Collection.addButtons(newRow,song);
+				}
+				newRow.find('td.title a').text(song.name);
+				newRow.find('td.title a').click(function(event){
+					event.preventDefault();
+					PlayList.add(song);
+					PlayList.play(0);
+					Collection.parent.find('tr').removeClass('active');
+					$('tr[data-title="'+song.name+'"]').addClass('active');
+				});
+				newRow.attr('data-album',album.name);
+				newRow.attr('data-title',song.name);
+				newRow.attr('data-artist',artist.name);
+				if(!first){
+					nextRow.before(newRow);
+				}
+				first=false;
+			});
+		});
+		tr.removeClass('collapsed');
+		tr.find('a.expander').data('expanded',true);
+		tr.find('a.expander').addClass('expanded');
+		tr.find('a.expander').text('v');
+	},
+	hideArtist:function(artist){
+		var tr=Collection.parent.find('tr[data-artist="'+artist+'"]');
+		if(tr.length>1){
+			var artist=tr.first().data('artistData');
+			tr.first().find('td.album a').text(artist.albums.length+' '+t('media','albums'));
+			tr.first().find('td.title a').text(artist.songs.length+' '+t('media','songs'));
+			tr.first().find('td.album a').unbind('click');
+			tr.first().find('td.title a').unbind('click');
+			tr.each(function(i,row){
+				if(i>0){
+					$(row).remove();
+				}
+			});
+			tr.find('a.expander').data('expanded',false);
+			tr.find('a.expander').removeClass('expanded');
+			tr.find('a.expander').text('>');
+            Collection.addButtons(tr,artist);
+		}
+	},
+	showAlbum:function(artist,album){
+        var tr = Collection.parent.find('tr[data-artist="'+artist+'"][data-album="'+album+'"]');
+        tr.find('a.expander').data('expanded',true);
+		tr.find('a.expander').addClass('expanded');
+		tr.find('a.expander').text('v ');
+        tr.show();
+	},
+	hideAlbum:function(artist,album){
+		var tr = Collection.parent.find('tr[data-artist="'+artist+'"][data-album="'+album+'"]');
+        tr.find('a.expander').data('expanded',false);
+        tr.find('a.expander').removeClass('expanded');
+        tr.find('a.expander').text('> ');
+        tr.hide();
+		tr.first().show();
+	},
+	parent:null,
+	hide:function(){
+		if(Collection.parent){
+			Collection.parent.hide();
+		}
+	},
+	registerPlay:function(item){
+		if(item){
+			var song=Collection.findSong(item.artist,item.album,item.name);
+			song.song_playcount++;
+		}
+	},
+	addButtons:function(parent,data){
+        buttons = parent.find('.buttons');
+        if(buttons.find('.add').length<=0) {
+            buttons.append('<img class="add" src="'+OC.imagePath('core','actions/play-add')+'"/>');
+        }
+        if(buttons.find('.play').length<=0) {
+            buttons.append('<img class="play" src="'+OC.imagePath('core','actions/play')+'"/>');
+        }
+		buttons.find('.add').unbind('click');
+		buttons.find('.add').click(function(event){
+            event.preventDefault();
+			PlayList.add(data,true);
+            PlayList.render();
+		});
+		buttons.find('.play').unbind('click');
+		buttons.find('.play').click(function(event){
+            event.preventDefault();
+			PlayList.add(data);
+			PlayList.play(0,0);
+            PlayList.render();
+		});
+	},
+	find:function(artistName,albumName,songName){
+		if(songName){
+			return Collection.findSong(artistName,albumName,songName);
+		}else if(albumName){
+			return Collection.findAlbum(artistName,albumName);
+		}else{
+			return Collection.findArtist(artistName);
+		}
+	},
+	findArtist:function(name){
+		for(var i=0;i<Collection.artists.length;i++){
+			if(Collection.artists[i].artist_name==name){
+				return Collection.artists[i];
+			}
+		}
+	},
+	findAlbum:function(artistName,albumName){
+		var artist=Collection.findArtist(artistName);
+		if(artist){
+			for(var i=0;i<artist.albums.length;i++){
+				if(artist.albums[i].album_name==albumName){
+					return artist.albums[i];
+				}
+			}
+		}
+	},
+	findSong:function(artistName,albumName,songName){
+		var album=Collection.findAlbum(artistName,albumName);
+		if(album){
+			for(var i=0;i<album.songs.length;i++){
+				if(album.songs[i].song_name==songName){
+					return album.songs[i];
+				}
+			}
+		}
+	},
+	addSong:function(song){
+		var artist=false
+		var album=false;
+		for(var i=0;i<Collection.artists.length;i++){
+			if(Collection.artists[i].artist_id==song.song_artist){
+				artist=Collection.artists[i];
+				for(var j=0;j<artist.albums.length;j++){
+					if(artist.albums[j].album_id==song.song_album){
+						album=artist.albums[j];
+						break;
+					}
+				}
+				break;
+			}
+		}
+		if(!artist){
+			artist={artist_id:song.song_artist,artist_name:song.artist,albums:[]};
+			Collection.artists.push(artist);
+			if(!Collection.parent || Collection.parent.is(":visible")){
+				Collection.display();
+			}
+			
+		}
+		if(!album){
+			album={album_id:song.song_album,album_name:song.album,album_artist:song.song_artist,songs:[]};
+			artist.albums.push(album)
+		}
+		album.songs.push(song)
+	}
+}
+
+$(document).ready(function(){
+	Collection.parent=$('#collection');
+	Collection.load();
+	Collection.parent.hide();
+	$('#scan input.start').click(function(){
+		$('#scan input.start').hide();
+		$('#scan input.stop').show();
+		$('#scan input.stop').click(function(){
+			Scanner.toggle();
+		});
+		Scanner.scanCollection();
+	});
+});
diff --git a/apps/media/js/jquery.jplayer.min.js b/apps/media/js/jquery.jplayer.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..9ba8b0c45c77b73b6d5beb51f9fae826150282d9
--- /dev/null
+++ b/apps/media/js/jquery.jplayer.min.js
@@ -0,0 +1,2 @@
+
+(function($,undefined){$.fn.jPlayer=function(options){var name="jPlayer",isMethodCall=typeof options==="string",args=Array.prototype.slice.call(arguments,1),returnValue=this;options=!isMethodCall&&args.length?$.extend.apply(null,[true,options].concat(args)):options;if(isMethodCall&&options.charAt(0)==="_")return returnValue;if(isMethodCall){this.each(function(){var instance=$.data(this,name),methodValue=instance&&$.isFunction(instance[options])?instance[options].apply(instance,args):instance;if(methodValue!==instance&&methodValue!==undefined){returnValue=methodValue;return false}})}else this.each(function(){var instance=$.data(this,name);if(instance){instance.option(options||{})}else $.data(this,name,new $.jPlayer(options,this))});return returnValue};$.jPlayer=function(options,element){if(arguments.length){this.element=$(element);this.options=$.extend(true,{},this.options,options);var self=this;this.element.bind("remove.jPlayer",function(){self.destroy()});this._init()}};$.jPlayer.emulateMethods="load play pause";$.jPlayer.emulateStatus="src readyState networkState currentTime duration paused ended playbackRate";$.jPlayer.emulateOptions="muted volume";$.jPlayer.reservedEvent="ready flashreset resize repeat error warning";$.jPlayer.event={ready:"jPlayer_ready",flashreset:"jPlayer_flashreset",resize:"jPlayer_resize",repeat:"jPlayer_repeat",error:"jPlayer_error",warning:"jPlayer_warning",loadstart:"jPlayer_loadstart",progress:"jPlayer_progress",suspend:"jPlayer_suspend",abort:"jPlayer_abort",emptied:"jPlayer_emptied",stalled:"jPlayer_stalled",play:"jPlayer_play",pause:"jPlayer_pause",loadedmetadata:"jPlayer_loadedmetadata",loadeddata:"jPlayer_loadeddata",waiting:"jPlayer_waiting",playing:"jPlayer_playing",canplay:"jPlayer_canplay",canplaythrough:"jPlayer_canplaythrough",seeking:"jPlayer_seeking",seeked:"jPlayer_seeked",timeupdate:"jPlayer_timeupdate",ended:"jPlayer_ended",ratechange:"jPlayer_ratechange",durationchange:"jPlayer_durationchange",volumechange:"jPlayer_volumechange"};$.jPlayer.htmlEvent=["loadstart","abort","emptied","stalled","loadedmetadata","loadeddata","canplay","canplaythrough","ratechange"];$.jPlayer.pause=function(){$.each($.jPlayer.prototype.instances,function(i,element){if(element.data("jPlayer").status.srcSet)element.jPlayer("pause")})};$.jPlayer.timeFormat={showHour:false,showMin:true,showSec:true,padHour:false,padMin:true,padSec:true,sepHour:":",sepMin:":",sepSec:""};$.jPlayer.convertTime=function(s){var myTime=new Date(s*1e3),hour=myTime.getUTCHours(),min=myTime.getUTCMinutes(),sec=myTime.getUTCSeconds(),strHour=($.jPlayer.timeFormat.padHour&&hour<10)?"0"+hour:hour,strMin=($.jPlayer.timeFormat.padMin&&min<10)?"0"+min:min,strSec=($.jPlayer.timeFormat.padSec&&sec<10)?"0"+sec:sec;return(($.jPlayer.timeFormat.showHour)?strHour+$.jPlayer.timeFormat.sepHour:"")+(($.jPlayer.timeFormat.showMin)?strMin+$.jPlayer.timeFormat.sepMin:"")+(($.jPlayer.timeFormat.showSec)?strSec+$.jPlayer.timeFormat.sepSec:"")};$.jPlayer.uaBrowser=function(userAgent){var ua=userAgent.toLowerCase(),rwebkit=/(webkit)[ \/]([\w.]+)/,ropera=/(opera)(?:.*version)?[ \/]([\w.]+)/,rmsie=/(msie) ([\w.]+)/,rmozilla=/(mozilla)(?:.*? rv:([\w.]+))?/,match=rwebkit.exec(ua)||ropera.exec(ua)||rmsie.exec(ua)||ua.indexOf("compatible")<0&&rmozilla.exec(ua)||[];return{browser:match[1]||"",version:match[2]||"0"}};$.jPlayer.uaPlatform=function(userAgent){var ua=userAgent.toLowerCase(),rplatform=/(ipad|iphone|ipod|android|blackberry|playbook|windows ce|webos)/,rtablet=/(ipad|playbook)/,randroid=/(android)/,rmobile=/(mobile)/,platform=rplatform.exec(ua)||[],tablet=rtablet.exec(ua)||!rmobile.exec(ua)&&randroid.exec(ua)||[];return{platform:platform[1]||"",tablet:tablet[1]||""}};$.jPlayer.browser={};$.jPlayer.platform={};var browserMatch=$.jPlayer.uaBrowser(navigator.userAgent);if(browserMatch.browser){$.jPlayer.browser[browserMatch.browser]=true;$.jPlayer.browser.version=browserMatch.version};var platformMatch=$.jPlayer.uaPlatform(navigator.userAgent);if(platformMatch.platform){$.jPlayer.platform[platformMatch.platform]=true;$.jPlayer.platform.mobile=!platformMatch.tablet;$.jPlayer.platform.tablet=!!platformMatch.tablet};$.jPlayer.prototype={count:0,version:{script:"2.0.23",needFlash:"2.0.9",flash:"unknown"},options:{swfPath:"js",solution:"html, flash",supplied:"mp3",preload:'metadata',volume:0.8,muted:false,wmode:"opaque",backgroundColor:"#000000",cssSelectorAncestor:"#jp_container_1",cssSelector:{videoPlay:".jp-video-play",play:".jp-play",pause:".jp-pause",stop:".jp-stop",seekBar:".jp-seek-bar",playBar:".jp-play-bar",mute:".jp-mute",unmute:".jp-unmute",volumeBar:".jp-volume-bar",volumeBarValue:".jp-volume-bar-value",volumeMax:".jp-volume-max",currentTime:".jp-current-time",duration:".jp-duration",fullScreen:".jp-full-screen",restoreScreen:".jp-restore-screen",repeat:".jp-repeat",repeatOff:".jp-repeat-off",gui:".jp-gui"},fullScreen:false,autohide:{restored:false,full:true,fadeIn:200,fadeOut:600,hold:1e3},loop:false,repeat:function(event){if(event.jPlayer.options.loop){$(this).unbind(".jPlayerRepeat").bind($.jPlayer.event.ended+".jPlayer.jPlayerRepeat",function(){$(this).jPlayer("play")})}else $(this).unbind(".jPlayerRepeat")},idPrefix:"jp",noConflict:"jQuery",emulateHtml:false,errorAlerts:false,warningAlerts:false},optionsAudio:{size:{width:"0px",height:"0px",cssClass:""},sizeFull:{width:"0px",height:"0px",cssClass:""}},optionsVideo:{size:{width:"480px",height:"270px",cssClass:"jp-video-270p"},sizeFull:{width:"100%",height:"100%",cssClass:"jp-video-full"}},instances:{},status:{src:"",media:{},paused:true,format:{},formatType:"",waitForPlay:true,waitForLoad:true,srcSet:false,video:false,seekPercent:0,currentPercentRelative:0,currentPercentAbsolute:0,currentTime:0,duration:0,readyState:0,networkState:0,playbackRate:1,ended:0},internal:{ready:false},solution:{html:true,flash:true},format:{mp3:{codec:'audio/mpeg; codecs="mp3"',flashCanPlay:true,media:'audio'},m4a:{codec:'audio/mp4; codecs="mp4a.40.2"',flashCanPlay:true,media:'audio'},oga:{codec:'audio/ogg; codecs="vorbis"',flashCanPlay:false,media:'audio'},wav:{codec:'audio/wav; codecs="1"',flashCanPlay:false,media:'audio'},webma:{codec:'audio/webm; codecs="vorbis"',flashCanPlay:false,media:'audio'},fla:{codec:'audio/x-flv',flashCanPlay:true,media:'audio'},m4v:{codec:'video/mp4; codecs="avc1.42E01E, mp4a.40.2"',flashCanPlay:true,media:'video'},ogv:{codec:'video/ogg; codecs="theora, vorbis"',flashCanPlay:false,media:'video'},webmv:{codec:'video/webm; codecs="vorbis, vp8"',flashCanPlay:false,media:'video'},flv:{codec:'video/x-flv',flashCanPlay:true,media:'video'}},_init:function(){var self=this;this.element.empty();this.status=$.extend({},this.status);this.internal=$.extend({},this.internal);this.internal.domNode=this.element.get(0);this.formats=[];this.solutions=[];this.require={};this.htmlElement={};this.html={};this.html.audio={};this.html.video={};this.flash={};this.css={};this.css.cs={};this.css.jq={};this.ancestorJq=[];this.options.volume=this._limitValue(this.options.volume,0,1);$.each(this.options.supplied.toLowerCase().split(","),function(index1,value1){var format=value1.replace(/^\s+|\s+$/g,"");if(self.format[format]){var dupFound=false;$.each(self.formats,function(index2,value2){if(format===value2){dupFound=true;return false}});if(!dupFound)self.formats.push(format)}});$.each(this.options.solution.toLowerCase().split(","),function(index1,value1){var solution=value1.replace(/^\s+|\s+$/g,"");if(self.solution[solution]){var dupFound=false;$.each(self.solutions,function(index2,value2){if(solution===value2){dupFound=true;return false}});if(!dupFound)self.solutions.push(solution)}});this.internal.instance="jp_"+this.count;this.instances[this.internal.instance]=this.element;if(!this.element.attr("id"))this.element.attr("id",this.options.idPrefix+"_jplayer_"+this.count);this.internal.self=$.extend({},{id:this.element.attr("id"),jq:this.element});this.internal.audio=$.extend({},{id:this.options.idPrefix+"_audio_"+this.count,jq:undefined});this.internal.video=$.extend({},{id:this.options.idPrefix+"_video_"+this.count,jq:undefined});this.internal.flash=$.extend({},{id:this.options.idPrefix+"_flash_"+this.count,jq:undefined,swf:this.options.swfPath+((this.options.swfPath!==""&&this.options.swfPath.slice(-1)!=="/")?"/":"")+"Jplayer.swf"});this.internal.poster=$.extend({},{id:this.options.idPrefix+"_poster_"+this.count,jq:undefined});$.each($.jPlayer.event,function(eventName,eventType){if(self.options[eventName]!==undefined){self.element.bind(eventType+".jPlayer",self.options[eventName]);self.options[eventName]=undefined}});this.require.audio=false;this.require.video=false;$.each(this.formats,function(priority,format){self.require[self.format[format].media]=true});if(this.require.video){this.options=$.extend(true,{},this.optionsVideo,this.options)}else this.options=$.extend(true,{},this.optionsAudio,this.options);this._setSize();this.htmlElement.poster=document.createElement('img');this.htmlElement.poster.id=this.internal.poster.id;this.htmlElement.poster.onload=function(){if(!self.status.video||self.status.waitForPlay)self.internal.poster.jq.show()};this.element.append(this.htmlElement.poster);this.internal.poster.jq=$("#"+this.internal.poster.id);this.internal.poster.jq.css({width:this.status.width,height:this.status.height});this.internal.poster.jq.hide();this.html.audio.available=false;if(this.require.audio){this.htmlElement.audio=document.createElement('audio');this.htmlElement.audio.id=this.internal.audio.id;this.html.audio.available=!!this.htmlElement.audio.canPlayType};this.html.video.available=false;if(this.require.video){this.htmlElement.video=document.createElement('video');this.htmlElement.video.id=this.internal.video.id;this.html.video.available=!!this.htmlElement.video.canPlayType};this.flash.available=this._checkForFlash(10);this.html.canPlay={};this.flash.canPlay={};$.each(this.formats,function(priority,format){self.html.canPlay[format]=self.html[self.format[format].media].available&&""!==self.htmlElement[self.format[format].media].canPlayType(self.format[format].codec);self.flash.canPlay[format]=self.format[format].flashCanPlay&&self.flash.available});this.html.desired=false;this.flash.desired=false;$.each(this.solutions,function(solutionPriority,solution){if(solutionPriority===0){self[solution].desired=true}else{var audioCanPlay=false,videoCanPlay=false;$.each(self.formats,function(formatPriority,format){if(self[self.solutions[0]].canPlay[format])if(self.format[format].media==='video'){videoCanPlay=true}else audioCanPlay=true});self[solution].desired=(self.require.audio&&!audioCanPlay)||(self.require.video&&!videoCanPlay)}});this.html.support={};this.flash.support={};$.each(this.formats,function(priority,format){self.html.support[format]=self.html.canPlay[format]&&self.html.desired;self.flash.support[format]=self.flash.canPlay[format]&&self.flash.desired});this.html.used=false;this.flash.used=false;$.each(this.solutions,function(solutionPriority,solution){$.each(self.formats,function(formatPriority,format){if(self[solution].support[format]){self[solution].used=true;return false}})});this.html.active=false;this.html.audio.gate=false;this.html.video.gate=false;this.flash.active=false;this.flash.gate=false;this._cssSelectorAncestor(this.options.cssSelectorAncestor);if(!(this.html.used||this.flash.used))this._error({type:$.jPlayer.error.NO_SOLUTION,context:"{solution:'"+this.options.solution+"', supplied:'"+this.options.supplied+"'}",message:$.jPlayer.errorMsg.NO_SOLUTION,hint:$.jPlayer.errorHint.NO_SOLUTION});if(this.flash.used){var htmlObj,flashVars='jQuery='+encodeURI(this.options.noConflict)+'&id='+encodeURI(this.internal.self.id)+'&vol='+this.options.volume+'&muted='+this.options.muted;if($.browser.msie&&Number($.browser.version)<=8){var objStr='<object id="'+this.internal.flash.id+'" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="0" height="0"></object>',paramStr=['<param name="movie" value="'+this.internal.flash.swf+'" />','<param name="FlashVars" value="'+flashVars+'" />','<param name="allowScriptAccess" value="always" />','<param name="bgcolor" value="'+this.options.backgroundColor+'" />','<param name="wmode" value="'+this.options.wmode+'" />'];htmlObj=document.createElement(objStr);for(var i=0;i<paramStr.length;i++)htmlObj.appendChild(document.createElement(paramStr[i]))}else{var createParam=function(el,n,v){var p=document.createElement("param");p.setAttribute("name",n);p.setAttribute("value",v);el.appendChild(p)};htmlObj=document.createElement("object");htmlObj.setAttribute("id",this.internal.flash.id);htmlObj.setAttribute("data",this.internal.flash.swf);htmlObj.setAttribute("type","application/x-shockwave-flash");htmlObj.setAttribute("width","1");htmlObj.setAttribute("height","1");createParam(htmlObj,"flashvars",flashVars);createParam(htmlObj,"allowscriptaccess","always");createParam(htmlObj,"bgcolor",this.options.backgroundColor);createParam(htmlObj,"wmode",this.options.wmode)};this.element.append(htmlObj);this.internal.flash.jq=$(htmlObj)};if(this.html.used){if(this.html.audio.available){this._addHtmlEventListeners(this.htmlElement.audio,this.html.audio);this.element.append(this.htmlElement.audio);this.internal.audio.jq=$("#"+this.internal.audio.id)};if(this.html.video.available){this._addHtmlEventListeners(this.htmlElement.video,this.html.video);this.element.append(this.htmlElement.video);this.internal.video.jq=$("#"+this.internal.video.id);this.internal.video.jq.css({width:'0px',height:'0px'})}};if(this.options.emulateHtml)this._emulateHtmlBridge();if(this.html.used&&!this.flash.used)setTimeout(function(){self.internal.ready=true;self.version.flash="n/a";self._trigger($.jPlayer.event.repeat);self._trigger($.jPlayer.event.ready)},100);this._updateInterface();this._updateButtons(false);this._updateAutohide();this._updateVolume(this.options.volume);this._updateMute(this.options.muted);if(this.css.jq.videoPlay.length)this.css.jq.videoPlay.hide();$.jPlayer.prototype.count++},destroy:function(){this._resetStatus();this._updateInterface();this._seeked();if(this.css.jq.currentTime.length)this.css.jq.currentTime.text("");if(this.css.jq.duration.length)this.css.jq.duration.text("");if(this.status.srcSet)this.pause();$.each(this.css.jq,function(fn,jq){if(jq.length)jq.unbind(".jPlayer")});if(this.options.emulateHtml)this._destroyHtmlBridge();this.element.removeData("jPlayer");this.element.unbind(".jPlayer");this.element.empty();delete this.instances[this.internal.instance]},enable:function(){},disable:function(){},_addHtmlEventListeners:function(mediaElement,entity){var self=this;mediaElement.preload=this.options.preload;mediaElement.muted=this.options.muted;mediaElement.volume=this.options.volume;mediaElement.addEventListener("progress",function(){if(entity.gate&&!self.status.waitForLoad){self._getHtmlStatus(mediaElement);self._updateInterface();self._trigger($.jPlayer.event.progress)}},false);mediaElement.addEventListener("timeupdate",function(){if(entity.gate&&!self.status.waitForLoad){self._getHtmlStatus(mediaElement);self._updateInterface();self._trigger($.jPlayer.event.timeupdate)}},false);mediaElement.addEventListener("durationchange",function(){if(entity.gate&&!self.status.waitForLoad){self.status.duration=this.duration;self._getHtmlStatus(mediaElement);self._updateInterface();self._trigger($.jPlayer.event.durationchange)}},false);mediaElement.addEventListener("play",function(){if(entity.gate&&!self.status.waitForLoad){self._updateButtons(true);self._trigger($.jPlayer.event.play)}},false);mediaElement.addEventListener("playing",function(){if(entity.gate&&!self.status.waitForLoad){self._updateButtons(true);self._seeked();self._trigger($.jPlayer.event.playing)}},false);mediaElement.addEventListener("pause",function(){if(entity.gate&&!self.status.waitForLoad){self._updateButtons(false);self._trigger($.jPlayer.event.pause)}},false);mediaElement.addEventListener("waiting",function(){if(entity.gate&&!self.status.waitForLoad){self._seeking();self._trigger($.jPlayer.event.waiting)}},false);mediaElement.addEventListener("seeking",function(){if(entity.gate&&!self.status.waitForLoad){self._seeking();self._trigger($.jPlayer.event.seeking)}},false);mediaElement.addEventListener("seeked",function(){if(entity.gate&&!self.status.waitForLoad){self._seeked();self._trigger($.jPlayer.event.seeked)}},false);mediaElement.addEventListener("volumechange",function(){if(entity.gate&&!self.status.waitForLoad){self.options.volume=mediaElement.volume;self.options.muted=mediaElement.muted;self._updateMute();self._updateVolume();self._trigger($.jPlayer.event.volumechange)}},false);mediaElement.addEventListener("suspend",function(){if(entity.gate&&!self.status.waitForLoad){self._seeked();self._trigger($.jPlayer.event.suspend)}},false);mediaElement.addEventListener("ended",function(){if(entity.gate&&!self.status.waitForLoad){if(!$.jPlayer.browser.webkit)self.htmlElement.media.currentTime=0;self.htmlElement.media.pause();self._updateButtons(false);self._getHtmlStatus(mediaElement,true);self._updateInterface();self._trigger($.jPlayer.event.ended)}},false);mediaElement.addEventListener("error",function(){if(entity.gate&&!self.status.waitForLoad){self._updateButtons(false);self._seeked();if(self.status.srcSet){clearTimeout(self.internal.htmlDlyCmdId);self.status.waitForLoad=true;self.status.waitForPlay=true;if(self.status.video)self.internal.video.jq.css({width:'0px',height:'0px'});if(self._validString(self.status.media.poster))self.internal.poster.jq.show();if(self.css.jq.videoPlay.length)self.css.jq.videoPlay.show();self._error({type:$.jPlayer.error.URL,context:self.status.src,message:$.jPlayer.errorMsg.URL,hint:$.jPlayer.errorHint.URL})}}},false);$.each($.jPlayer.htmlEvent,function(i,eventType){mediaElement.addEventListener(this,function(){if(entity.gate&&!self.status.waitForLoad)self._trigger($.jPlayer.event[eventType])},false)})},_getHtmlStatus:function(media,override){var ct=0,d=0,cpa=0,sp=0,cpr=0;if(media.duration)this.status.duration=media.duration;ct=media.currentTime;cpa=(this.status.duration>0)?100*ct/this.status.duration:0;if((typeof media.seekable==="object")&&(media.seekable.length>0)){sp=(this.status.duration>0)?100*media.seekable.end(media.seekable.length-1)/this.status.duration:100;cpr=100*media.currentTime/media.seekable.end(media.seekable.length-1)}else{sp=100;cpr=cpa};if(override){ct=0;cpr=0;cpa=0};this.status.seekPercent=sp;this.status.currentPercentRelative=cpr;this.status.currentPercentAbsolute=cpa;this.status.currentTime=ct;this.status.readyState=media.readyState;this.status.networkState=media.networkState;this.status.playbackRate=media.playbackRate;this.status.ended=media.ended},_resetStatus:function(){this.status=$.extend({},this.status,$.jPlayer.prototype.status)},_trigger:function(eventType,error,warning){var event=$.Event(eventType);event.jPlayer={};event.jPlayer.version=$.extend({},this.version);event.jPlayer.options=$.extend(true,{},this.options);event.jPlayer.status=$.extend(true,{},this.status);event.jPlayer.html=$.extend(true,{},this.html);event.jPlayer.flash=$.extend(true,{},this.flash);if(error)event.jPlayer.error=$.extend({},error);if(warning)event.jPlayer.warning=$.extend({},warning);this.element.trigger(event)},jPlayerFlashEvent:function(eventType,status){if(eventType===$.jPlayer.event.ready)if(!this.internal.ready){this.internal.ready=true;this.internal.flash.jq.css({width:'0px',height:'0px'});this.version.flash=status.version;if(this.version.needFlash!==this.version.flash)this._error({type:$.jPlayer.error.VERSION,context:this.version.flash,message:$.jPlayer.errorMsg.VERSION+this.version.flash,hint:$.jPlayer.errorHint.VERSION});this._trigger($.jPlayer.event.repeat);this._trigger(eventType)}else if(this.flash.gate){if(this.status.srcSet){var currentTime=this.status.currentTime,paused=this.status.paused;this.setMedia(this.status.media);if(currentTime>0)if(paused){this.pause(currentTime)}else this.play(currentTime)};this._trigger($.jPlayer.event.flashreset)};if(this.flash.gate)switch(eventType){case $.jPlayer.event.progress:this._getFlashStatus(status);this._updateInterface();this._trigger(eventType);break;case $.jPlayer.event.timeupdate:this._getFlashStatus(status);this._updateInterface();this._trigger(eventType);break;case $.jPlayer.event.play:this._seeked();this._updateButtons(true);this._trigger(eventType);break;case $.jPlayer.event.pause:this._updateButtons(false);this._trigger(eventType);break;case $.jPlayer.event.ended:this._updateButtons(false);this._trigger(eventType);break;case $.jPlayer.event.error:this.status.waitForLoad=true;this.status.waitForPlay=true;if(this.status.video)this.internal.flash.jq.css({width:'0px',height:'0px'});if(this._validString(this.status.media.poster))this.internal.poster.jq.show();if(this.css.jq.videoPlay.length)this.css.jq.videoPlay.show();if(this.status.video){this._flash_setVideo(this.status.media)}else this._flash_setAudio(this.status.media);this._error({type:$.jPlayer.error.URL,context:status.src,message:$.jPlayer.errorMsg.URL,hint:$.jPlayer.errorHint.URL});break;case $.jPlayer.event.seeking:this._seeking();this._trigger(eventType);break;case $.jPlayer.event.seeked:this._seeked();this._trigger(eventType);break;case $.jPlayer.event.ready:break;default:this._trigger(eventType)};return false},_getFlashStatus:function(status){this.status.seekPercent=status.seekPercent;this.status.currentPercentRelative=status.currentPercentRelative;this.status.currentPercentAbsolute=status.currentPercentAbsolute;this.status.currentTime=status.currentTime;this.status.duration=status.duration;this.status.readyState=4;this.status.networkState=0;this.status.playbackRate=1;this.status.ended=false},_updateButtons:function(playing){if(playing!==undefined){this.status.paused=!playing;if(this.css.jq.play.length&&this.css.jq.pause.length)if(playing){this.css.jq.play.hide();this.css.jq.pause.show()}else{this.css.jq.play.show();this.css.jq.pause.hide()}};if(this.css.jq.restoreScreen.length&&this.css.jq.fullScreen.length)if(this.options.fullScreen){this.css.jq.fullScreen.hide();this.css.jq.restoreScreen.show()}else{this.css.jq.fullScreen.show();this.css.jq.restoreScreen.hide()};if(this.css.jq.repeat.length&&this.css.jq.repeatOff.length)if(this.options.loop){this.css.jq.repeat.hide();this.css.jq.repeatOff.show()}else{this.css.jq.repeat.show();this.css.jq.repeatOff.hide()}},_updateInterface:function(){if(this.css.jq.seekBar.length)this.css.jq.seekBar.width(this.status.seekPercent+"%");if(this.css.jq.playBar.length)this.css.jq.playBar.width(this.status.currentPercentRelative+"%");if(this.css.jq.currentTime.length)this.css.jq.currentTime.text($.jPlayer.convertTime(this.status.currentTime));if(this.css.jq.duration.length)this.css.jq.duration.text($.jPlayer.convertTime(this.status.duration))},_seeking:function(){if(this.css.jq.seekBar.length)this.css.jq.seekBar.addClass("jp-seeking-bg")},_seeked:function(){if(this.css.jq.seekBar.length)this.css.jq.seekBar.removeClass("jp-seeking-bg")},setMedia:function(media){var self=this;this._seeked();clearTimeout(this.internal.htmlDlyCmdId);var audioGate=this.html.audio.gate,videoGate=this.html.video.gate,supported=false;$.each(this.formats,function(formatPriority,format){var isVideo=self.format[format].media==='video';$.each(self.solutions,function(solutionPriority,solution){if(self[solution].support[format]&&self._validString(media[format])){var isHtml=solution==='html';if(isVideo){if(isHtml){self.html.audio.gate=false;self.html.video.gate=true;self.flash.gate=false}else{self.html.audio.gate=false;self.html.video.gate=false;self.flash.gate=true}}else if(isHtml){self.html.audio.gate=true;self.html.video.gate=false;self.flash.gate=false}else{self.html.audio.gate=false;self.html.video.gate=false;self.flash.gate=true};if(self.flash.active||(self.html.active&&self.flash.gate)||(audioGate===self.html.audio.gate&&videoGate===self.html.video.gate)){self.clearMedia()}else if(audioGate!==self.html.audio.gate&&videoGate!==self.html.video.gate){self._html_pause();if(self.status.video)self.internal.video.jq.css({width:'0px',height:'0px'});self._resetStatus()};if(isVideo){if(isHtml){self._html_setVideo(media);self.html.active=true;self.flash.active=false}else{self._flash_setVideo(media);self.html.active=false;self.flash.active=true};if(self.css.jq.videoPlay.length)self.css.jq.videoPlay.show();self.status.video=true}else{if(isHtml){self._html_setAudio(media);self.html.active=true;self.flash.active=false}else{self._flash_setAudio(media);self.html.active=false;self.flash.active=true};if(self.css.jq.videoPlay.length)self.css.jq.videoPlay.hide();self.status.video=false};supported=true;return false}});if(supported)return false});if(supported){if(this._validString(media.poster)){if(this.htmlElement.poster.src!==media.poster){this.htmlElement.poster.src=media.poster}else this.internal.poster.jq.show()}else this.internal.poster.jq.hide();this.status.srcSet=true;this.status.media=$.extend({},media);this._updateButtons(false);this._updateInterface()}else{if(this.status.srcSet&&!this.status.waitForPlay)this.pause();this.html.audio.gate=false;this.html.video.gate=false;this.flash.gate=false;this.html.active=false;this.flash.active=false;this._resetStatus();this._updateInterface();this._updateButtons(false);this.internal.poster.jq.hide();if(this.html.used&&this.require.video)this.internal.video.jq.css({width:'0px',height:'0px'});if(this.flash.used)this.internal.flash.jq.css({width:'0px',height:'0px'});this._error({type:$.jPlayer.error.NO_SUPPORT,context:"{supplied:'"+this.options.supplied+"'}",message:$.jPlayer.errorMsg.NO_SUPPORT,hint:$.jPlayer.errorHint.NO_SUPPORT})}},clearMedia:function(){this._resetStatus();this._updateButtons(false);this.internal.poster.jq.hide();clearTimeout(this.internal.htmlDlyCmdId);if(this.html.active){this._html_clearMedia()}else if(this.flash.active)this._flash_clearMedia()},load:function(){if(this.status.srcSet){if(this.html.active){this._html_load()}else if(this.flash.active)this._flash_load()}else this._urlNotSetError("load")},play:function(time){time=(typeof time==="number")?time:NaN;if(this.status.srcSet){if(this.html.active){this._html_play(time)}else if(this.flash.active)this._flash_play(time)}else this._urlNotSetError("play")},videoPlay:function(e){this.play()},pause:function(time){time=(typeof time==="number")?time:NaN;if(this.status.srcSet){if(this.html.active){this._html_pause(time)}else if(this.flash.active)this._flash_pause(time)}else this._urlNotSetError("pause")},pauseOthers:function(){var self=this;$.each(this.instances,function(i,element){if(self.element!==element)if(element.data("jPlayer").status.srcSet)element.jPlayer("pause")})},stop:function(){if(this.status.srcSet){if(this.html.active){this._html_pause(0)}else if(this.flash.active)this._flash_pause(0)}else this._urlNotSetError("stop")},playHead:function(p){p=this._limitValue(p,0,100);if(this.status.srcSet){if(this.html.active){this._html_playHead(p)}else if(this.flash.active)this._flash_playHead(p)}else this._urlNotSetError("playHead")},_muted:function(muted){this.options.muted=muted;if(this.html.used)this._html_mute(muted);if(this.flash.used)this._flash_mute(muted);if(this.flash.gate){this._updateMute(muted);this._updateVolume(this.options.volume);this._trigger($.jPlayer.event.volumechange)}},mute:function(mute){mute=mute===undefined?true:!!mute;this._muted(mute)},unmute:function(unmute){unmute=unmute===undefined?true:!!unmute;this._muted(!unmute)},_updateMute:function(mute){if(mute===undefined)mute=this.options.muted;if(this.css.jq.mute.length&&this.css.jq.unmute.length)if(mute){this.css.jq.mute.hide();this.css.jq.unmute.show()}else{this.css.jq.mute.show();this.css.jq.unmute.hide()}},volume:function(v){v=this._limitValue(v,0,1);this.options.volume=v;if(this.html.used)this._html_volume(v);if(this.flash.used)this._flash_volume(v);if(this.flash.gate){this._updateVolume(v);this._trigger($.jPlayer.event.volumechange)}},volumeBar:function(e){if(this.css.jq.volumeBar.length){var offset=this.css.jq.volumeBar.offset(),x=e.pageX-offset.left,w=this.css.jq.volumeBar.width(),v=x/w;this.volume(v)};if(this.options.muted)this._muted(false)},volumeBarValue:function(e){this.volumeBar(e)},_updateVolume:function(v){if(v===undefined)v=this.options.volume;v=this.options.muted?0:v;if(this.css.jq.volumeBarValue.length)this.css.jq.volumeBarValue.width((v*100)+"%")},volumeMax:function(){this.volume(1);if(this.options.muted)this._muted(false)},_cssSelectorAncestor:function(ancestor){var self=this;this.options.cssSelectorAncestor=ancestor;this._removeUiClass();this.ancestorJq=ancestor?$(ancestor):[];if(ancestor&&this.ancestorJq.length!==1)this._warning({type:$.jPlayer.warning.CSS_SELECTOR_COUNT,context:ancestor,message:$.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.ancestorJq.length+" found for cssSelectorAncestor.",hint:$.jPlayer.warningHint.CSS_SELECTOR_COUNT});this._addUiClass();$.each(this.options.cssSelector,function(fn,cssSel){self._cssSelector(fn,cssSel)})},_cssSelector:function(fn,cssSel){var self=this;if(typeof cssSel==='string'){if($.jPlayer.prototype.options.cssSelector[fn]){if(this.css.jq[fn]&&this.css.jq[fn].length)this.css.jq[fn].unbind(".jPlayer");this.options.cssSelector[fn]=cssSel;this.css.cs[fn]=this.options.cssSelectorAncestor+" "+cssSel;if(cssSel){this.css.jq[fn]=$(this.css.cs[fn])}else this.css.jq[fn]=[];if(this.css.jq[fn].length){var handler=function(e){self[fn](e);$(this).blur();return false};this.css.jq[fn].bind("click.jPlayer",handler)};if(cssSel&&this.css.jq[fn].length!==1)this._warning({type:$.jPlayer.warning.CSS_SELECTOR_COUNT,context:this.css.cs[fn],message:$.jPlayer.warningMsg.CSS_SELECTOR_COUNT+this.css.jq[fn].length+" found for "+fn+" method.",hint:$.jPlayer.warningHint.CSS_SELECTOR_COUNT})}else this._warning({type:$.jPlayer.warning.CSS_SELECTOR_METHOD,context:fn,message:$.jPlayer.warningMsg.CSS_SELECTOR_METHOD,hint:$.jPlayer.warningHint.CSS_SELECTOR_METHOD})}else this._warning({type:$.jPlayer.warning.CSS_SELECTOR_STRING,context:cssSel,message:$.jPlayer.warningMsg.CSS_SELECTOR_STRING,hint:$.jPlayer.warningHint.CSS_SELECTOR_STRING})},seekBar:function(e){if(this.css.jq.seekBar){var offset=this.css.jq.seekBar.offset(),x=e.pageX-offset.left,w=this.css.jq.seekBar.width(),p=100*x/w;this.playHead(p)}},playBar:function(e){this.seekBar(e)},repeat:function(){this._loop(true)},repeatOff:function(){this._loop(false)},_loop:function(loop){if(this.options.loop!==loop){this.options.loop=loop;this._updateButtons();this._trigger($.jPlayer.event.repeat)}},currentTime:function(e){},duration:function(e){},gui:function(e){},option:function(key,value){var options=key;if(arguments.length===0)return $.extend(true,{},this.options);if(typeof key==="string"){var keys=key.split(".");if(value===undefined){var opt=$.extend(true,{},this.options);for(var i=0;i<keys.length;i++)if(opt[keys[i]]!==undefined){opt=opt[keys[i]]}else{this._warning({type:$.jPlayer.warning.OPTION_KEY,context:key,message:$.jPlayer.warningMsg.OPTION_KEY,hint:$.jPlayer.warningHint.OPTION_KEY});return undefined};return opt};options={};var opts=options;for(var j=0;j<keys.length;j++)if(j<keys.length-1){opts[keys[j]]={};opts=opts[keys[j]]}else opts[keys[j]]=value};this._setOptions(options);return this},_setOptions:function(options){var self=this;$.each(options,function(key,value){self._setOption(key,value)});return this},_setOption:function(key,value){var self=this;switch(key){case"volume":this.volume(value);break;case"muted":this._muted(value);break;case"cssSelectorAncestor":this._cssSelectorAncestor(value);break;case"cssSelector":$.each(value,function(fn,cssSel){self._cssSelector(fn,cssSel)});break;case"fullScreen":if(this.options[key]!==value){this._removeUiClass();this.options[key]=value;this._refreshSize()};break;case"size":if(!this.options.fullScreen&&this.options[key].cssClass!==value.cssClass)this._removeUiClass();this.options[key]=$.extend({},this.options[key],value);this._refreshSize();break;case"sizeFull":if(this.options.fullScreen&&this.options[key].cssClass!==value.cssClass)this._removeUiClass();this.options[key]=$.extend({},this.options[key],value);this._refreshSize();break;case"autohide":this.options[key]=$.extend({},this.options[key],value);this._updateAutohide();break;case"loop":this._loop(value);break;case"emulateHtml":if(this.options[key]!==value){this.options[key]=value;if(value){this._emulateHtmlBridge()}else this._destroyHtmlBridge()};break};return this},_refreshSize:function(){this._setSize();this._addUiClass();this._updateSize();this._updateButtons();this._updateAutohide();this._trigger($.jPlayer.event.resize)},_setSize:function(){if(this.options.fullScreen){this.status.width=this.options.sizeFull.width;this.status.height=this.options.sizeFull.height;this.status.cssClass=this.options.sizeFull.cssClass}else{this.status.width=this.options.size.width;this.status.height=this.options.size.height;this.status.cssClass=this.options.size.cssClass};this.element.css({width:this.status.width,height:this.status.height})},_addUiClass:function(){if(this.ancestorJq.length)this.ancestorJq.addClass(this.status.cssClass)},_removeUiClass:function(){if(this.ancestorJq.length)this.ancestorJq.removeClass(this.status.cssClass)},_updateSize:function(){this.internal.poster.jq.css({width:this.status.width,height:this.status.height});if(!this.status.waitForPlay)if(this.html.active&&this.status.video){this.internal.video.jq.css({width:this.status.width,height:this.status.height})}else if(this.flash.active)this.internal.flash.jq.css({width:this.status.width,height:this.status.height})},_updateAutohide:function(){var self=this,event="mousemove.jPlayer",namespace=".jPlayerAutohide",eventType=event+namespace,handler=function(){self.css.jq.gui.fadeIn(self.options.autohide.fadeIn,function(){clearTimeout(self.internal.autohideId);self.internal.autohideId=setTimeout(function(){self.css.jq.gui.fadeOut(self.options.autohide.fadeOut)},self.options.autohide.hold)})};clearTimeout(this.internal.autohideId);this.element.unbind(namespace);if(this.css.jq.gui.length){this.css.jq.gui.unbind(namespace);if(this.options.fullScreen&&this.options.autohide.full||!this.options.fullScreen&&this.options.autohide.restored){this.element.bind(eventType,handler);this.css.jq.gui.bind(eventType,handler);this.css.jq.gui.hide()}else this.css.jq.gui.stop(true,true).show()}},fullScreen:function(){this._setOption("fullScreen",true)},restoreScreen:function(){this._setOption("fullScreen",false)},_html_initMedia:function(){if(this.status.srcSet&&!this.status.waitForPlay)this.htmlElement.media.pause();if(this.options.preload!=='none')this._html_load();this._trigger($.jPlayer.event.timeupdate)},_html_setAudio:function(media){var self=this;$.each(this.formats,function(priority,format){if(self.html.support[format]&&media[format]){self.status.src=media[format];self.status.format[format]=true;self.status.formatType=format;return false}});this.htmlElement.media=this.htmlElement.audio;this._html_initMedia()},_html_setVideo:function(media){var self=this;$.each(this.formats,function(priority,format){if(self.html.support[format]&&media[format]){self.status.src=media[format];self.status.format[format]=true;self.status.formatType=format;return false}});this.htmlElement.media=this.htmlElement.video;this._html_initMedia()},_html_clearMedia:function(){if(this.htmlElement.media){if(this.htmlElement.media.id===this.internal.video.id)this.internal.video.jq.css({width:'0px',height:'0px'});this.htmlElement.media.pause();this.htmlElement.media.src="";this.htmlElement.media.load()}},_html_load:function(){if(this.status.waitForLoad){this.status.waitForLoad=false;this.htmlElement.media.src=this.status.src;this.htmlElement.media.load()};clearTimeout(this.internal.htmlDlyCmdId)},_html_play:function(time){var self=this;this._html_load();this.htmlElement.media.play();if(!isNaN(time))try{this.htmlElement.media.currentTime=time}catch(err){this.internal.htmlDlyCmdId=setTimeout(function(){self.play(time)},100);return};this._html_checkWaitForPlay()},_html_pause:function(time){var self=this;if(time>0){this._html_load()}else clearTimeout(this.internal.htmlDlyCmdId);this.htmlElement.media.pause();if(!isNaN(time))try{this.htmlElement.media.currentTime=time}catch(err){this.internal.htmlDlyCmdId=setTimeout(function(){self.pause(time)},100);return};if(time>0)this._html_checkWaitForPlay()},_html_playHead:function(percent){var self=this;this._html_load();try{if((typeof this.htmlElement.media.seekable==="object")&&(this.htmlElement.media.seekable.length>0)){this.htmlElement.media.currentTime=percent*this.htmlElement.media.seekable.end(this.htmlElement.media.seekable.length-1)/100}else if(this.htmlElement.media.duration>0&&!isNaN(this.htmlElement.media.duration)){this.htmlElement.media.currentTime=percent*this.htmlElement.media.duration/100}else throw"e"}catch(err){this.internal.htmlDlyCmdId=setTimeout(function(){self.playHead(percent)},100);return};if(!this.status.waitForLoad)this._html_checkWaitForPlay()},_html_checkWaitForPlay:function(){if(this.status.waitForPlay){this.status.waitForPlay=false;if(this.css.jq.videoPlay.length)this.css.jq.videoPlay.hide();if(this.status.video){this.internal.poster.jq.hide();this.internal.video.jq.css({width:this.status.width,height:this.status.height})}}},_html_volume:function(v){if(this.html.audio.available)this.htmlElement.audio.volume=v;if(this.html.video.available)this.htmlElement.video.volume=v},_html_mute:function(m){if(this.html.audio.available)this.htmlElement.audio.muted=m;if(this.html.video.available)this.htmlElement.video.muted=m},_flash_setAudio:function(media){var self=this;try{$.each(this.formats,function(priority,format){if(self.flash.support[format]&&media[format]){switch(format){case"m4a":case"fla":self._getMovie().fl_setAudio_m4a(media[format]);break;case"mp3":self._getMovie().fl_setAudio_mp3(media[format]);break};self.status.src=media[format];self.status.format[format]=true;self.status.formatType=format;return false}});if(this.options.preload==='auto'){this._flash_load();this.status.waitForLoad=false}}catch(err){this._flashError(err)}},_flash_setVideo:function(media){var self=this;try{$.each(this.formats,function(priority,format){if(self.flash.support[format]&&media[format]){switch(format){case"m4v":case"flv":self._getMovie().fl_setVideo_m4v(media[format]);break};self.status.src=media[format];self.status.format[format]=true;self.status.formatType=format;return false}});if(this.options.preload==='auto'){this._flash_load();this.status.waitForLoad=false}}catch(err){this._flashError(err)}},_flash_clearMedia:function(){this.internal.flash.jq.css({width:'0px',height:'0px'});try{this._getMovie().fl_clearMedia()}catch(err){this._flashError(err)}},_flash_load:function(){try{this._getMovie().fl_load()}catch(err){this._flashError(err)};this.status.waitForLoad=false},_flash_play:function(time){try{this._getMovie().fl_play(time)}catch(err){this._flashError(err)};this.status.waitForLoad=false;this._flash_checkWaitForPlay()},_flash_pause:function(time){try{this._getMovie().fl_pause(time)}catch(err){this._flashError(err)};if(time>0){this.status.waitForLoad=false;this._flash_checkWaitForPlay()}},_flash_playHead:function(p){try{this._getMovie().fl_play_head(p)}catch(err){this._flashError(err)};if(!this.status.waitForLoad)this._flash_checkWaitForPlay()},_flash_checkWaitForPlay:function(){if(this.status.waitForPlay){this.status.waitForPlay=false;if(this.css.jq.videoPlay.length)this.css.jq.videoPlay.hide();if(this.status.video){this.internal.poster.jq.hide();this.internal.flash.jq.css({width:this.status.width,height:this.status.height})}}},_flash_volume:function(v){try{this._getMovie().fl_volume(v)}catch(err){this._flashError(err)}},_flash_mute:function(m){try{this._getMovie().fl_mute(m)}catch(err){this._flashError(err)}},_getMovie:function(){return document[this.internal.flash.id]},_checkForFlash:function(version){var flashIsInstalled=false,flash;if(window.ActiveXObject){try{flash=new ActiveXObject(("ShockwaveFlash.ShockwaveFlash."+version));flashIsInstalled=true}catch(e){}}else if(navigator.plugins&&navigator.mimeTypes.length>0){flash=navigator.plugins["Shockwave Flash"];if(flash){var flashVersion=navigator.plugins["Shockwave Flash"].description.replace(/.*\s(\d+\.\d+).*/,"$1");if(flashVersion>=version)flashIsInstalled=true}};return flashIsInstalled},_validString:function(url){return(url&&typeof url==="string")},_limitValue:function(value,min,max){return(value<min)?min:((value>max)?max:value)},_urlNotSetError:function(context){this._error({type:$.jPlayer.error.URL_NOT_SET,context:context,message:$.jPlayer.errorMsg.URL_NOT_SET,hint:$.jPlayer.errorHint.URL_NOT_SET})},_flashError:function(error){var errorType;if(!this.internal.ready){errorType="FLASH"}else errorType="FLASH_DISABLED";this._error({type:$.jPlayer.error[errorType],context:this.internal.flash.swf,message:$.jPlayer.errorMsg[errorType]+error.message,hint:$.jPlayer.errorHint[errorType]})},_error:function(error){this._trigger($.jPlayer.event.error,error);if(this.options.errorAlerts)this._alert("Error!"+(error.message?"\n\n"+error.message:"")+(error.hint?"\n\n"+error.hint:"")+"\n\nContext: "+error.context)},_warning:function(warning){this._trigger($.jPlayer.event.warning,undefined,warning);if(this.options.warningAlerts)this._alert("Warning!"+(warning.message?"\n\n"+warning.message:"")+(warning.hint?"\n\n"+warning.hint:"")+"\n\nContext: "+warning.context)},_alert:function(message){alert("jPlayer "+this.version.script+" : id='"+this.internal.self.id+"' : "+message)},_emulateHtmlBridge:function(){var self=this,methods=$.jPlayer.emulateMethods;$.each($.jPlayer.emulateMethods.split(/\s+/g),function(i,name){self.internal.domNode[name]=function(arg){self[name](arg)}});$.each($.jPlayer.event,function(eventName,eventType){var nativeEvent=true;$.each($.jPlayer.reservedEvent.split(/\s+/g),function(i,name){if(name===eventName){nativeEvent=false;return false}});if(nativeEvent)self.element.bind(eventType+".jPlayer.jPlayerHtml",function(){self._emulateHtmlUpdate();var domEvent=document.createEvent("Event");domEvent.initEvent(eventName,false,true);self.internal.domNode.dispatchEvent(domEvent)})})},_emulateHtmlUpdate:function(){var self=this;$.each($.jPlayer.emulateStatus.split(/\s+/g),function(i,name){self.internal.domNode[name]=self.status[name]});$.each($.jPlayer.emulateOptions.split(/\s+/g),function(i,name){self.internal.domNode[name]=self.options[name]})},_destroyHtmlBridge:function(){var self=this;this.element.unbind(".jPlayerHtml");var emulated=$.jPlayer.emulateMethods+" "+$.jPlayer.emulateStatus+" "+$.jPlayer.emulateOptions;$.each(emulated.split(/\s+/g),function(i,name){delete self.internal.domNode[name]})}};$.jPlayer.error={FLASH:"e_flash",FLASH_DISABLED:"e_flash_disabled",NO_SOLUTION:"e_no_solution",NO_SUPPORT:"e_no_support",URL:"e_url",URL_NOT_SET:"e_url_not_set",VERSION:"e_version"};$.jPlayer.errorMsg={FLASH:"jPlayer's Flash fallback is not configured correctly, or a command was issued before the jPlayer Ready event. Details: ",FLASH_DISABLED:"jPlayer's Flash fallback has been disabled by the browser due to the CSS rules you have used. Details: ",NO_SOLUTION:"No solution can be found by jPlayer in this browser. Neither HTML nor Flash can be used.",NO_SUPPORT:"It is not possible to play any media format provided in setMedia() on this browser using your current options.",URL:"Media URL could not be loaded.",URL_NOT_SET:"Attempt to issue media playback commands, while no media url is set.",VERSION:"jPlayer "+$.jPlayer.prototype.version.script+" needs Jplayer.swf version "+$.jPlayer.prototype.version.needFlash+" but found "};$.jPlayer.errorHint={FLASH:"Check your swfPath option and that Jplayer.swf is there.",FLASH_DISABLED:"Check that you have not display:none; the jPlayer entity or any ancestor.",NO_SOLUTION:"Review the jPlayer options: support and supplied.",NO_SUPPORT:"Video or audio formats defined in the supplied option are missing.",URL:"Check media URL is valid.",URL_NOT_SET:"Use setMedia() to set the media URL.",VERSION:"Update jPlayer files."};$.jPlayer.warning={CSS_SELECTOR_COUNT:"e_css_selector_count",CSS_SELECTOR_METHOD:"e_css_selector_method",CSS_SELECTOR_STRING:"e_css_selector_string",OPTION_KEY:"e_option_key"};$.jPlayer.warningMsg={CSS_SELECTOR_COUNT:"The number of css selectors found did not equal one: ",CSS_SELECTOR_METHOD:"The methodName given in jPlayer('cssSelector') is not a valid jPlayer method.",CSS_SELECTOR_STRING:"The methodCssSelector given in jPlayer('cssSelector') is not a String or is empty.",OPTION_KEY:"The option requested in jPlayer('option') is undefined."};$.jPlayer.warningHint={CSS_SELECTOR_COUNT:"Check your css selector and the ancestor.",CSS_SELECTOR_METHOD:"Check your method name.",CSS_SELECTOR_STRING:"Check your css selector is a string.",OPTION_KEY:"Check your option name."}})(jQuery)
diff --git a/apps/media/js/loader.js b/apps/media/js/loader.js
new file mode 100644
index 0000000000000000000000000000000000000000..ef8647eeca5c18dcbaa5a3f23ea9dcbf8d493dd6
--- /dev/null
+++ b/apps/media/js/loader.js
@@ -0,0 +1,60 @@
+function musicTypeFromFile(file){
+	var extention=file.substr(file.indexOf('.')+1);
+	if(extention=='ogg'){
+		return 'oga'
+	}
+	//TODO check for more specific cases
+	return extention;
+}
+
+function playAudio(filename){
+	loadPlayer(musicTypeFromFile(filename),function(){
+		PlayList.add($('#dir').val()+'/'+filename);
+		PlayList.play(PlayList.items.length-1);
+	});
+}
+
+function addAudio(filename){
+	loadPlayer(musicTypeFromFile(filename),function(){
+		PlayList.add($('#dir').val()+'/'+filename);
+	});
+}
+
+function loadPlayer(type,ready){
+	if(!loadPlayer.done){
+		OC.addScript('media','jquery.jplayer.min',function(){
+			OC.addScript('media','player',function(){
+				$('body').append($('<div id="playerPlaceholder"/>'))
+				$('#playerPlaceholder').append($('<div/>')).load(OC.filePath('media','templates','player.php'),function(){
+					loadPlayer.done=true;
+					PlayList.init(type,ready);
+				});
+			});
+		});
+		OC.addStyle('media','player');
+	}else{
+		ready();
+	}
+}
+
+$(document).ready(function() {
+	loadPlayer.done=false
+
+// 	FileActions.register('audio','Add to playlist','',addAudio);
+// 	FileActions.register('application/ogg','Add to playlist','',addAudio);
+
+	if(typeof FileActions!=='undefined'){
+		FileActions.register('audio','Play','',playAudio);
+		FileActions.register('application/ogg','','Play',playAudio);
+		FileActions.setDefault('audio','Play');
+		FileActions.setDefault('application/ogg','Play');
+	}
+	var oc_current_user=OC.currentUser;
+	if(typeof PlayList==='undefined'){
+		if(typeof localStorage !== 'undefined' && localStorage){
+			if(localStorage.hasOwnProperty(oc_current_user+'oc_playlist_items') && localStorage.getItem(oc_current_user+'oc_playlist_items')!='[]' && localStorage.getItem(oc_current_user+'oc_playlist_active')!='true'){
+				loadPlayer();
+			}
+		}
+	}
+});
\ No newline at end of file
diff --git a/apps/media/js/music.js b/apps/media/js/music.js
new file mode 100644
index 0000000000000000000000000000000000000000..c04c579d1ca04805d164a3f16b1d8f9c4b1cce98
--- /dev/null
+++ b/apps/media/js/music.js
@@ -0,0 +1,51 @@
+$(document).ready(function(){
+	OC.search.customResults.Music=function(row,item){
+		var parts=item.link.substr(item.link.indexOf('#')+1).split('&');
+		var data={};
+		for(var i=0;i<parts.length;i++){
+			var itemParts=parts[i].split('=');
+			data[itemParts[0]]=itemParts[1].replace(/\+/g,' ');
+		}
+		var media=Collection.find(data.artist,data.album,data.song);
+		var a=row.find('a');
+		a.attr('href','#');
+		a.click(function(){
+			var oldSize=PlayList.items.length;
+			PlayList.add(media);
+			PlayList.play(oldSize);
+			PlayList.render();
+		});
+		var button=$('<input type="button" title="'+t('media','Add to playlist')+'" class="add"></input>');
+		button.css('background-image','url('+OC.imagePath('core','actions/play-add')+')')
+		button.click(function(event){
+			event.stopPropagation();
+			PlayList.add(media);
+			PlayList.render();
+		});
+		row.find('div.name').append(button);
+	}
+	Collection.display();
+});
+
+
+
+function getUrlVars(){
+	var vars = [], hash;
+	var hashes = window.location.href.slice(window.location.href.indexOf('#') + 1).split('&');
+	for(var i = 0; i < hashes.length; i++)
+	{
+		hash = hashes[i].split('=');
+		vars.push(hash[0]);
+		vars[hash[0]] = hash[1].replace(/\+/g,' ');
+	}
+	return vars;
+}
+
+function musicTypeFromFile(file){
+	var extention=file.split('.').pop();
+	if(extention=='ogg'){
+		return 'oga'
+	}
+	//TODO check for more specific cases
+	return extention;
+}
\ No newline at end of file
diff --git a/apps/media/js/player.js b/apps/media/js/player.js
new file mode 100644
index 0000000000000000000000000000000000000000..f696b87bbde5ec2bb2b0909440bd2021b2de84bb
--- /dev/null
+++ b/apps/media/js/player.js
@@ -0,0 +1,211 @@
+var PlayList={
+	urlBase:OC.linkTo('media','ajax/api.php')+'?action=play&path=',
+	current:-1,
+	items:[],
+	player:null,
+	volume:0.8,
+	active:false,
+	next:function(){
+		var items=PlayList.items;
+		var next=PlayList.current+1;
+		if(next>=items.length){
+			next=0;
+		}
+		PlayList.play(next);
+		PlayList.render();
+	},
+	previous:function(){
+		var items=PlayList.items;
+		var next=PlayList.current-1;
+		if(next<0){
+			next=items.length-1;
+		}
+		PlayList.play(next);
+		PlayList.render();
+	},
+	play:function(index,time,ready){
+		var items=PlayList.items;
+		if(index==null){
+			index=PlayList.current;
+		}
+		if(index>-1 && index<items.length){
+			PlayList.current=index;
+			if(PlayList.player){
+				if(PlayList.player.data('jPlayer').options.supplied!=items[index].type){//the the audio type changes we need to reinitialize jplayer
+					PlayList.player.jPlayer("play",time);
+                    localStorage.setItem(oc_current_user+'oc_playlist_time',time);
+					PlayList.player.jPlayer("destroy");
+                    PlayList.save(); // so that the init don't lose the playlist
+					PlayList.init(items[index].type,null); // init calls load that calls play
+				}else{
+					PlayList.player.jPlayer("setMedia", items[PlayList.current]);
+					items[index].playcount++;
+					PlayList.player.jPlayer("play",time);
+					if(index>0){
+						var previous=index-1;
+					}else{
+						var previous=items.length-1;
+					}
+					if(index+1<items.length){
+						var next=index+1;
+					}else{
+						var next=0;
+					}
+					$('.jp-next').attr('title',items[next].name);
+					$('.jp-previous').attr('title',items[previous].name);
+					if (typeof Collection !== 'undefined') {
+						Collection.registerPlay();
+					}
+					if(ready){
+						ready();
+					}
+				}
+			}else{
+				localStorage.setItem(oc_current_user+'oc_playlist_time',time);
+				localStorage.setItem(oc_current_user+'oc_playlist_playing','true');
+                PlayList.save(); // so that the init don't lose the playlist
+				PlayList.init(items[index].type,null); // init calls load that calls play
+			}
+		}
+	},
+	init:function(type,ready){
+		if(!PlayList.player){
+			$(".jp-previous").click(function() {
+				PlayList.previous();
+				$(this).blur();
+				PlayList.render();
+				return false;
+			});
+			$(".jp-next").click(function() {
+				PlayList.next();
+				$(this).blur();
+				PlayList.render();
+				return false;
+			});
+			PlayList.player=$('#controls div.player');
+		}
+		$(PlayList.player).jPlayer({
+			ended:PlayList.next,
+			pause:function(){
+				localStorage.setItem(oc_current_user+'oc_playlist_playing','false');
+			},
+			play:function(){
+				localStorage.setItem(oc_current_user+'oc_playlist_playing','true');
+			},
+			supplied:type,
+			ready:function(){
+				PlayList.load();
+				if(ready){
+					ready();
+				}
+			},
+			volume:PlayList.volume,
+			cssSelectorAncestor:'#controls',
+			swfPath:OC.linkTo('media','js'),
+		});
+	},
+	add:function(song,dontReset){
+		if(!dontReset){
+			PlayList.items=[];//clear the playlist
+		}
+		if(!song){
+			return;
+		}
+		if(song.substr){//we are passed a string, asume it's a url to a song
+			PlayList.addFile(song,true);
+		}
+		if(song.albums){//a artist object was passed, add all albums inside it
+			$.each(song.albums,function(index,album){
+				PlayList.add(album,true);
+			});
+		} else if(song.songs){//a album object was passed, add all songs inside it
+			$.each(song.songs,function(index,song){
+				PlayList.add(song,true);
+			});
+		}
+		if(song.path){
+			var type=musicTypeFromFile(song.path);
+			var item={name:song.name,type:type,artist:song.artist,album:song.album,length:song.length,playcount:song.playCount};
+			item[type]=PlayList.urlBase+encodeURIComponent(song.path);
+            PlayList.items.push(item);
+		}
+	},
+	addFile:function(path){
+		var type=musicTypeFromFile(path);
+		var item={name:'unknown',artist:'unknown',album:'unknwon',type:type};
+		$.getJSON(OC.filePath('media','ajax','api.php')+'?action=get_path_info&path='+encodeURIComponent(path),function(song){
+			item.name=song.song_name;
+			item.artist=song.artist;
+			item.album=song.album;
+		});
+		item[type]=PlayList.urlBase+encodeURIComponent(path);
+		PlayList.items.push(item);
+	},
+	remove:function(index){
+		PlayList.items.splice(index,1);
+		PlayList.render();
+	},
+	render:function(){},
+	playing:function(){
+		if(!PlayList.player){
+			return false;
+		}else{
+			return !PlayList.player.data("jPlayer").status.paused;
+		}
+	},
+	save:function(){
+		if(typeof localStorage !== 'undefined' && localStorage){
+			localStorage.setItem(oc_current_user+'oc_playlist_items',JSON.stringify(PlayList.items));
+			localStorage.setItem(oc_current_user+'oc_playlist_current',PlayList.current);
+            if(PlayList.player) {
+                if(PlayList.player.data('jPlayer')) {
+                    var time=Math.round(PlayList.player.data('jPlayer').status.currentTime);
+                    localStorage.setItem(oc_current_user+'oc_playlist_time',time);
+                    var volume=PlayList.player.data('jPlayer').options.volume*100;
+                    localStorage.setItem(oc_current_user+'oc_playlist_volume',volume);
+                }
+            }
+			if(PlayList.active){
+				localStorage.setItem(oc_current_user+'oc_playlist_active','false');
+			}
+		}
+	},
+	load:function(){
+		if(typeof localStorage !== 'undefined' && localStorage){
+			PlayList.active=true;
+			localStorage.setItem(oc_current_user+'oc_playlist_active','true');
+			if(localStorage.hasOwnProperty(oc_current_user+'oc_playlist_items')){
+				PlayList.items=JSON.parse(localStorage.getItem(oc_current_user+'oc_playlist_items'));
+				if(PlayList.items.length>0){
+					PlayList.current=parseInt(localStorage.getItem(oc_current_user+'oc_playlist_current'));
+					var time=parseInt(localStorage.getItem(oc_current_user+'oc_playlist_time'));
+					if(localStorage.hasOwnProperty(oc_current_user+'oc_playlist_volume')){
+						var volume=localStorage.getItem(oc_current_user+'oc_playlist_volume');
+						PlayList.volume=volume/100;
+						$('.jp-volume-bar-value').css('width',volume+'%');
+						if(PlayList.player.data('jPlayer')){
+							PlayList.player.jPlayer("option",'volume',volume/100);
+						}
+					}
+					if(JSON.parse(localStorage.getItem(oc_current_user+'oc_playlist_playing'))){
+						PlayList.play(null,time);
+					}else{
+						PlayList.play(null,time,function(){
+							PlayList.player.jPlayer("pause");
+						});
+					}
+					PlayList.render();
+				}
+			}
+		}
+	}
+}
+
+$(document).ready(function(){
+	$(window).bind('beforeunload', function (){
+		PlayList.save();
+	});
+
+	$('jp-previous').tipsy({gravity:'n', fade:true, live:true});
+	$('jp-next').tipsy({gravity:'n', fade:true, live:true});
+})
diff --git a/apps/media/js/playlist.js b/apps/media/js/playlist.js
new file mode 100644
index 0000000000000000000000000000000000000000..cb7f24522a4751bda0d891cd04f47d1794399c7f
--- /dev/null
+++ b/apps/media/js/playlist.js
@@ -0,0 +1,42 @@
+PlayList.render=function(){
+	$('#playlist').show();
+	PlayList.parent.empty();
+	for(var i=0;i<PlayList.items.length;i++){
+		var item=PlayList.items[i];
+		var li=$('<li/>');
+		li.append(item.name);
+		var img=$('<img class="remove svg action" src="'+OC.imagePath('core','actions/delete')+'"/>');
+		img.click(function(event){
+			event.stopPropagation();
+			PlayList.remove($(this).parent().data('index'));
+		});
+		li.click(function(event){
+			PlayList.play($(this).data('index'));
+		});
+		li.append(img)
+		li.data('index',i);
+		li.addClass('song');
+		PlayList.parent.append(li);
+	}
+}
+PlayList.getSelected=function(){
+	return $('tbody td.name input:checkbox:checked').parent().parent();
+}
+PlayList.hide=function(){
+	$('#playlist').hide();
+}
+
+$(document).ready(function(){
+	PlayList.parent=$('#leftcontent');
+	$('#selectAll').click(function(){
+		if($(this).attr('checked')){
+			// Check all
+			$('#leftcontent li.song input:checkbox').attr('checked', true);
+			$('#leftcontent li.song input:checkbox').parent().addClass('selected');
+		}else{
+			// Uncheck all
+			$('#leftcontent li.song input:checkbox').attr('checked', false);
+			$('#leftcontent li.song input:checkbox').parent().removeClass('selected');
+		}
+	});
+});
diff --git a/apps/media/js/scanner.js b/apps/media/js/scanner.js
new file mode 100644
index 0000000000000000000000000000000000000000..0ebf408e702070d809bded61874aee436090dd3c
--- /dev/null
+++ b/apps/media/js/scanner.js
@@ -0,0 +1,78 @@
+Scanner={
+	songsFound:0,
+	songsScanned:0,
+	songsChecked:0,
+	startTime:null,
+	endTime:null,
+	stopScanning:false,
+	currentIndex:-1,
+	songs:[],
+	findSongs:function(ready){
+		$.getJSON(OC.linkTo('media','ajax/api.php')+'?action=find_music',function(songs){
+			Scanner.songsFound=songs.length;
+			Scanner.currentIndex=-1
+			if(ready){
+				ready(songs)
+			}
+		});
+	},
+	scanFile:function(path,ready){
+		path=encodeURIComponent(path);
+		$.getJSON(OC.linkTo('media','ajax/api.php')+'?action=get_path_info&path='+path,function(song){
+			Scanner.songsChecked++;
+			if(ready){
+				ready(song);
+			}
+			if(song){//do this after the ready call so we dont hold up the next ajax call
+				var artistId=song.song_artist;
+				Scanner.songsScanned++;
+				$('#scan span.songCount').text(Scanner.songsScanned);
+				var progress=(Scanner.songsChecked/Scanner.songsFound)*100;
+				$('#scanprogressbar').progressbar('value',progress)
+				Collection.addSong(song);
+			}
+		});
+	},
+	scanCollection:function(ready){
+		$('#scanprogressbar').progressbar({
+			value:0,
+		});
+		Scanner.songsChecked=0;
+		Scanner.songsScanned=0;
+		Scanner.startTime=new Date().getTime()/1000;
+		Scanner.findSongs(function(songs){
+			Scanner.songs=songs;
+			Scanner.start();
+		});
+	},
+	stop:function(){
+		Scanner.stopScanning=true;
+	},
+	start:function(ready){
+		Scanner.stopScanning=false;
+		$('#scancount').show();
+		var scanSong=function(){
+			Scanner.currentIndex++;
+			if(!Scanner.stopScanning && Scanner.currentIndex<Scanner.songs.length){
+				Scanner.scanFile(Scanner.songs[Scanner.currentIndex],scanSong)
+			}else{
+				Scanner.endTime=new Date().getTime()/1000;
+				if(ready){
+					ready();
+				}
+			}
+		}
+		scanSong();
+		scanSong();
+	},
+	toggle:function(){
+		if(Scanner.stopScanning){
+			Scanner.start();
+			$('#scan input.stop').val(t('media','Pause'));
+		}else{
+			Scanner.stop();
+			$('#scan input.stop').val(t('media','Resume'));
+		}
+	}
+
+}
diff --git a/apps/media/l10n/ca.php b/apps/media/l10n/ca.php
new file mode 100644
index 0000000000000000000000000000000000000000..f0e24d8514d53f9edda13aca468f2105a73b5a5c
--- /dev/null
+++ b/apps/media/l10n/ca.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Música",
+"Play" => "Reprodueix",
+"Pause" => "Pausa",
+"Previous" => "Anterior",
+"Next" => "Següent",
+"Mute" => "Mut",
+"Unmute" => "Activa el so",
+"Songs scanned" => "Cançons escanejades",
+"Rescan Collection" => "Escaneja de nou la col·lecció",
+"Artist" => "Artista",
+"Album" => "Àlbum",
+"Title" => "Títol"
+);
diff --git a/apps/media/l10n/da.php b/apps/media/l10n/da.php
new file mode 100644
index 0000000000000000000000000000000000000000..64cdc59ded542115464ac1ff2fffeee4080d01e4
--- /dev/null
+++ b/apps/media/l10n/da.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Musik",
+"Pause" => "Pause",
+"Songs scanned" => "Sange skannet",
+"Rescan Collection" => "Genskan Samling",
+"Artist" => "Kunstner",
+"Album" => "Album",
+"Title" => "Titel"
+);
diff --git a/apps/media/l10n/de.php b/apps/media/l10n/de.php
new file mode 100644
index 0000000000000000000000000000000000000000..7a87f6dcb35b37388e068fafb89e1994473f5ca4
--- /dev/null
+++ b/apps/media/l10n/de.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Musik",
+"Pause" => "Pause",
+"Songs scanned" => "Lieder gescannt",
+"Rescan Collection" => "Sammlung scannen",
+"Artist" => "Künstler",
+"Album" => "Album",
+"Title" => "Titel"
+);
diff --git a/apps/media/l10n/el.php b/apps/media/l10n/el.php
new file mode 100644
index 0000000000000000000000000000000000000000..6d4b781b78faf612cc2e7d53ac05af4ee1026c91
--- /dev/null
+++ b/apps/media/l10n/el.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Μουσική",
+"Play" => "Αναπαραγωγή",
+"Pause" => "Παύση",
+"Previous" => "Προηγούμενο",
+"Next" => "Επόμενο",
+"Mute" => "Σίγαση",
+"Unmute" => "Επαναφορά ήχου",
+"Songs scanned" => "Σαρωμένα τραγούγια",
+"Rescan Collection" => "Επανασάρωση συλλογής",
+"Artist" => "Καλλιτέχνης",
+"Album" => "Άλμπουμ",
+"Title" => "Τίτλος"
+);
diff --git a/apps/media/l10n/es.php b/apps/media/l10n/es.php
new file mode 100644
index 0000000000000000000000000000000000000000..b97fb0bf157319d22398d9b71382af19e12c3bb9
--- /dev/null
+++ b/apps/media/l10n/es.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Música",
+"Play" => "Reproducir",
+"Pause" => "Pausa",
+"Previous" => "Anterior",
+"Next" => "Siguiente",
+"Mute" => "Silenciar",
+"Unmute" => "Sonar",
+"Songs scanned" => "Canciones encontradas",
+"Rescan Collection" => "Buscar música nueva",
+"Artist" => "Artista",
+"Album" => "Álbum",
+"Title" => "Título"
+);
diff --git a/apps/media/l10n/fr.php b/apps/media/l10n/fr.php
new file mode 100644
index 0000000000000000000000000000000000000000..3e4e0f19283ec4544613faf7137a12a72dcfaa70
--- /dev/null
+++ b/apps/media/l10n/fr.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Musique",
+"Pause" => "Pause",
+"Songs scanned" => "Pistes scannées",
+"Rescan Collection" => "Réanalyser la Collection",
+"Artist" => "Artiste",
+"Album" => "Album",
+"Title" => "Titre"
+);
diff --git a/apps/media/l10n/id.php b/apps/media/l10n/id.php
new file mode 100644
index 0000000000000000000000000000000000000000..7127f85fbf012cd9061acf500ac9a2654bc8ad54
--- /dev/null
+++ b/apps/media/l10n/id.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Musik",
+"Pause" => "Jeda",
+"Songs scanned" => "Lagu-lagu yang telah dipindai",
+"Rescan Collection" => "Pindai ulang Koleksi",
+"Artist" => "Artis",
+"Album" => "Album",
+"Title" => "Judul"
+);
diff --git a/apps/media/l10n/it.php b/apps/media/l10n/it.php
new file mode 100644
index 0000000000000000000000000000000000000000..f0d9c606e7417006cc91b8e021f66456442f620d
--- /dev/null
+++ b/apps/media/l10n/it.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Musica",
+"Play" => "Play",
+"Pause" => "Pausa",
+"Previous" => "Precedente",
+"Next" => "Successiva",
+"Mute" => "Disattiva audio",
+"Unmute" => "Riattiva audio",
+"Songs scanned" => "Canzoni analizzate",
+"Rescan Collection" => "Rianalizza colezione",
+"Artist" => "Artista",
+"Album" => "Album",
+"Title" => "Titolo"
+);
diff --git a/apps/media/l10n/nl.php b/apps/media/l10n/nl.php
new file mode 100644
index 0000000000000000000000000000000000000000..7ae0a761af06b2ca2a36a626b1734f716770616f
--- /dev/null
+++ b/apps/media/l10n/nl.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Muziek",
+"Pause" => "Pauze",
+"Songs scanned" => "nummers gescanned",
+"Rescan Collection" => "Collectie opnieuw scannen",
+"Artist" => "Artiest",
+"Album" => "Album",
+"Title" => "Titel"
+);
diff --git a/apps/media/l10n/pl.php b/apps/media/l10n/pl.php
new file mode 100644
index 0000000000000000000000000000000000000000..bb74d6d578ac26060294148329aa1423da267cec
--- /dev/null
+++ b/apps/media/l10n/pl.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Muzyka",
+"Pause" => "Zatrzymaj",
+"Songs scanned" => "Przeskanowane utwory",
+"Rescan Collection" => "Przeskanuj kolekcjÄ™",
+"Artist" => "Artysta",
+"Album" => "Album",
+"Title" => "Tytuł"
+);
diff --git a/apps/media/l10n/pt_BR.php b/apps/media/l10n/pt_BR.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac5b1c22d7defa6f64aa6da8df007d3e729fedc1
--- /dev/null
+++ b/apps/media/l10n/pt_BR.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Música",
+"Play" => "Tocar",
+"Pause" => "Pausa",
+"Previous" => "Anterior",
+"Next" => "Próximo",
+"Mute" => "Mudo",
+"Unmute" => "Não Mudo",
+"Songs scanned" => "Músicas encontradas",
+"Rescan Collection" => "Atualizar a Coleção",
+"Artist" => "Artista",
+"Album" => "Álbum",
+"Title" => "Título"
+);
diff --git a/apps/media/l10n/sv.php b/apps/media/l10n/sv.php
new file mode 100644
index 0000000000000000000000000000000000000000..b3a7f18f7b225b1fc053181815eabb9c774a8055
--- /dev/null
+++ b/apps/media/l10n/sv.php
@@ -0,0 +1,9 @@
+<?php $TRANSLATIONS = array(
+"Music" => "Musik",
+"Pause" => "Paus",
+"Songs scanned" => "Skannade låtar",
+"Rescan Collection" => "Sök igenom samlingen",
+"Artist" => "Artist",
+"Album" => "Album",
+"Title" => "Titel"
+);
diff --git a/apps/media/l10n/xgettextfiles b/apps/media/l10n/xgettextfiles
new file mode 100644
index 0000000000000000000000000000000000000000..39a310a4537ee23f5eca14a616e70b60d8f94417
--- /dev/null
+++ b/apps/media/l10n/xgettextfiles
@@ -0,0 +1,6 @@
+../appinfo/app.php
+../templates/music.php
+../js/scanner.js
+../js/collection.js
+../js/music.js
+../js/playlist.js
diff --git a/apps/media/lib_ampache.php b/apps/media/lib_ampache.php
new file mode 100644
index 0000000000000000000000000000000000000000..a0123f87c5164db75ad4e5406ddb89b9a578c05e
--- /dev/null
+++ b/apps/media/lib_ampache.php
@@ -0,0 +1,371 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//implementation of ampache's xml api
+class OC_MEDIA_AMPACHE{
+	
+	/**
+	* do the initial handshake
+	* @param array params
+	*/
+	public static function handshake($params){
+		$auth=(isset($params['auth']))?$params['auth']:false;
+		$user=(isset($params['user']))?$params['user']:false;
+		$time=(isset($params['timestamp']))?$params['timestamp']:false;
+		$now=time();
+		if($now-$time>(10*60)){
+			echo("<root>
+	<error code='400'>timestamp is more then 10 minutes old</error>
+</root>");
+		}
+		if($auth and $user and $time){
+			$query=OC_DB::prepare("SELECT user_id, user_password_sha256 from *PREFIX*media_users WHERE user_id=?");
+			$users=$query->execute(array($user))->fetchAll();
+			if(count($users)>0){
+				$pass=$users[0]['user_password_sha256'];
+				$key=hash('sha256',$time.$pass);
+				if($key==$auth){
+					$token=hash('sha256','oc_media_'.$key);
+					OC_MEDIA_COLLECTION::$uid=$users[0]['user_id'];
+					$date=date('c');//todo proper update/add/clean dates
+					$songs=OC_MEDIA_COLLECTION::getSongCount();
+					$artists=OC_MEDIA_COLLECTION::getArtistCount();
+					$albums=OC_MEDIA_COLLECTION::getAlbumCount();
+					$query=OC_DB::prepare("INSERT INTO *PREFIX*media_sessions (`session_id`, `token`, `user_id`, `start`) VALUES (NULL, ?, ?, now());");
+					$query->execute(array($token,$user));
+					$expire=date('c',time()+600);
+					echo("<root>
+	<auth>$token</auth>
+	<version>350001</version>
+	<update>$date</update>
+	<add>$date</add>
+	<clean>$date</clean>
+	<songs>$songs</songs>
+	<artists>$artists</artists>
+	<albums>$albums</albums>\
+	<session_length>600</session_length>
+	<session_expire>$expire</session_expire>
+	<tags>0</tags>
+	<videos>0</videos>
+</root>");
+					return;
+				}
+			}
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+		}else{
+			echo("<root>
+	<error code='400'>Missing arguments</error>
+</root>");
+		}
+	}
+	
+	public static function ping($params){
+		if(isset($params['auth'])){
+			if(self::checkAuth($params['auth'])){
+				self::updateAuth($params['auth']);
+			}else{
+				echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+				return;
+			}
+		}
+		echo('<root>');
+		echo('<version>350001</version>');
+		echo('</root>');
+	}
+	
+	public static function checkAuth($auth){
+		if(is_array($auth)){
+			if(isset($auth['auth'])){
+				$auth=$auth['auth'];
+			}else{
+				return false;
+			}
+		}
+		//remove old sessions
+		$query=OC_DB::prepare("DELETE from *PREFIX*media_sessions WHERE start<(NOW()-600)");
+		$query->execute();
+		
+		$query=OC_DB::prepare("SELECT user_id from *PREFIX*media_sessions WHERE token=?");
+		$users=$query->execute(array($auth))->fetchAll();
+		if(count($users)>0){
+			OC_MEDIA_COLLECTION::$uid=$users[0]['user_id'];
+			return $users[0]['user_id'];
+		}else{
+			return false;
+		}
+	}
+	
+	public static function updateAuth($auth){
+		$query=OC_DB::prepare("UPDATE *PREFIX*media_sessions SET start=CURRENT_TIMESTAMP WHERE token=?");
+		$query->execute(array($auth));
+	}
+	
+	private static function printArtist($artist){
+		$albums=count(OC_MEDIA_COLLECTION::getAlbums($artist['artist_id']));
+		$songs=count(OC_MEDIA_COLLECTION::getSongs($artist['artist_id']));
+		$id=$artist['artist_id'];
+		$name=utf8_decode(htmlentities($artist['artist_name']));
+		echo("\t<artist id='$id'>\n");
+		echo("\t\t<name>$name</name>\n");
+		echo("\t\t<albums>$albums</albums>\n");
+		echo("\t\t<songs>$songs</songs>\n");
+		echo("\t\t<rating>0</rating>\n");
+		echo("\t\t<preciserating>0</preciserating>\n");
+		echo("\t</artist>\n");
+	}
+	
+	private static function printAlbum($album,$artistName=false){
+		if(!$artistName){
+			$artistName=OC_MEDIA_COLLECTION::getArtistName($album['album_artist']);
+		}
+		$artistName=utf8_decode(htmlentities($artistName));
+		$songs=count(OC_MEDIA_COLLECTION::getSongs($album['album_artist'],$album['album_id']));
+		$id=$album['album_id'];
+		$name=utf8_decode(htmlentities($album['album_name']));
+		$artist=$album['album_artist'];
+		echo("\t<album id='$id'>\n");
+		echo("\t\t<name>$name</name>\n");
+		echo("\t\t<artist id='$artist'>$artistName</artist>\n");
+		echo("\t\t<tracks>$songs</tracks>\n");
+		echo("\t\t<rating>0</rating>\n");
+		echo("\t\t<preciserating>0</preciserating>\n");
+		echo("\t</album>\n");
+	}
+	
+	private static function printSong($song,$artistName=false,$albumName=false){
+		global $WEBROOT;
+		if(!$artistName){
+			$artistName=OC_MEDIA_COLLECTION::getArtistName($song['song_artist']);
+		}
+		if(!$albumName){
+			$albumName=OC_MEDIA_COLLECTION::getAlbumName($song['song_album']);
+		}
+		$artistName=utf8_decode(htmlentities($artistName));
+		$albumName=utf8_decode(htmlentities($albumName));
+		if (isset($_SERVER['HTTPS'])) {
+			$PROTO="https://";
+		} else {
+			$PROTO="http://";
+		}
+		$id=$song['song_id'];
+		$name=utf8_decode(htmlentities($song['song_name']));
+		$artist=$song['song_artist'];
+		$album=$song['song_album'];
+		echo("\t<song id='$id'>\n");
+		echo("\t\t<title>$name</title>\n");
+		echo("\t\t<artist id='$artist'>$artistName</artist>\n");
+		echo("\t\t<album id='$album'>$albumName</album>\n");
+		$url="$PROTO{$_SERVER["HTTP_HOST"]}$WEBROOT/apps/media/server/xml.server.php?action=play&song=$id&auth={$_GET['auth']}";
+		$url=htmlentities($url);
+		echo("\t\t<url>$url</url>\n");
+		echo("\t\t<time>{$song['song_length']}</time>\n");
+		echo("\t\t<track>{$song['song_track']}</track>\n");
+		echo("\t\t<size>{$song['song_size']}</size>\n");
+		echo("\t\t<art></art>\n");
+		echo("\t\t<rating>0</rating>\n");
+		echo("\t\t<preciserating>0</preciserating>\n");
+		echo("\t</song>\n");
+	}
+	
+	public static function artists($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$filter=isset($params['filter'])?$params['filter']:'';
+		$exact=isset($params['exact'])?($params['exact']=='true'):false;
+		$artists=OC_MEDIA_COLLECTION::getArtists($filter,$exact);
+		error_log('artists found: '.print_r($artists,true));
+		echo('<root>');
+		foreach($artists as $artist){
+			self::printArtist($artist);
+		}
+		echo('</root>');
+	}
+	
+	public static function artist_songs($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$filter=isset($params['filter'])?$params['filter']:'';
+		$songs=OC_MEDIA_COLLECTION::getSongs($filter);
+		$artist=OC_MEDIA_COLLECTION::getArtistName($filter);
+		echo('<root>');
+		foreach($songs as $song){
+			self::printSong($song,$artist);
+		}
+		echo('</root>');
+	}
+	
+	public static function artist_albums($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		global $SITEROOT;
+		$filter=$params['filter'];
+		$albums=OC_MEDIA_COLLECTION::getAlbums($filter);
+		$artist=OC_MEDIA_COLLECTION::getArtistName($filter);
+		echo('<root>');
+		foreach($albums as $album){
+			self::printAlbum($album,$artist);
+		}
+		echo('</root>');
+	}
+	
+	public static function albums($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$filter=isset($params['filter'])?$params['filter']:'';
+		$exact=isset($params['exact'])?($params['exact']=='true'):false;
+		$albums=OC_MEDIA_COLLECTION::getAlbums(0,$filter,$exact);
+		echo('<root>');
+		foreach($albums as $album){
+			self::printAlbum($album,$artist);
+		}
+		echo('</root>');
+	}
+	
+	public static function album_songs($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$songs=OC_MEDIA_COLLECTION::getSongs(0,$params['filter']);
+		if(count($songs)>0){
+			$artist=OC_MEDIA_COLLECTION::getArtistName($songs[0]['song_artist']);
+		}
+		echo('<root>');
+		foreach($songs as $song){
+			self::printSong($song,$artist);
+		}
+		echo('</root>');
+	}
+	
+	public static function songs($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$filter=isset($params['filter'])?$params['filter']:'';
+		$exact=isset($params['exact'])?($params['exact']=='true'):false;
+		$songs=OC_MEDIA_COLLECTION::getSongs(0,0,$filter,$exact);
+		echo('<root>');
+		foreach($songs as $song){
+			self::printSong($song);
+		}
+		echo('</root>');
+	}
+	
+	public static function song($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		if($song=OC_MEDIA_COLLECTION::getSong($params['filter'])){
+			echo('<root>');
+			self::printSong($song);
+			echo('</root>');
+		}
+	}
+	
+	public static function play($params){
+		$username=!self::checkAuth($params);
+		if($username){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		if($song=OC_MEDIA_COLLECTION::getSong($params['song'])){
+			OC_Util::setupFS($song["song_user"]);
+
+			header('Content-type: '.OC_Filesystem::getMimeType($song['song_path']));
+			header('Content-Length: '.$song['song_size']);
+			OC_Filesystem::readfile($song['song_path']);
+		}
+	}
+	
+	public static function url_to_song($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$url=$params['url'];
+		$songId=substr($url,strrpos($url,'song=')+5);
+		if($song=OC_MEDIA_COLLECTION::getSong($songId)){
+			echo('<root>');
+			self::printSong($song);
+			echo('</root>');
+		}
+	}
+	
+	public static function search_songs($params){
+		if(!self::checkAuth($params)){
+			echo("<root>
+	<error code='400'>Invalid login</error>
+</root>");
+			return;
+		}
+		$filter=$params['filter'];
+		$artists=OC_MEDIA_COLLECTION::getArtists($filter);
+		$albums=OC_MEDIA_COLLECTION::getAlbums(0,$filter);
+		$songs=OC_MEDIA_COLLECTION::getSongs(0,0,$filter);
+		foreach($artists as $artist){
+			$songs=array_merge($songs,OC_MEDIA_COLLECTION::getSongs($artist['artist_id']));
+		}
+		foreach($albums as $album){
+			$songs=array_merge($songs,OC_MEDIA_COLLECTION::getSongs($album['album_artist'],$album['album_id']));
+		}
+		echo('<root>');
+		foreach($songs as $song){
+			self::printSong($song);
+		}
+		echo('</root>');
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/lib_collection.php b/apps/media/lib_collection.php
new file mode 100644
index 0000000000000000000000000000000000000000..273ea2494f8589a328220de93ac12619bec7e2f6
--- /dev/null
+++ b/apps/media/lib_collection.php
@@ -0,0 +1,369 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+
+//class for managing a music collection
+class OC_MEDIA_COLLECTION{
+	public static $uid;
+	private static $artistIdCache=array();
+	private static $albumIdCache=array();
+	private static $songIdCache=array();
+	private static $queries=array();
+	
+	/**
+	* get the id of an artist (case-insensitive)
+	* @param string name
+	* @return int
+	*/
+	public static function getArtistId($name){
+		if(empty($name)){
+			return 0;
+		}
+		$name=strtolower($name);
+		if(isset(self::$artistIdCache[$name])){
+			return self::$artistIdCache[$name];
+		}else{
+			$query=OC_DB::prepare("SELECT artist_id FROM *PREFIX*media_artists WHERE artist_name LIKE ?");
+			$artists=$query->execute(array($name))->fetchAll();
+			if(is_array($artists) and isset($artists[0])){
+				self::$artistIdCache[$name]=$artists[0]['artist_id'];
+				return $artists[0]['artist_id'];
+			}else{
+				return 0;
+			}
+		}
+	}
+
+	/**
+	* get the id of an album (case-insensitive)
+	* @param string name
+	* @param int artistId
+	* @return int
+	*/
+	public static function getAlbumId($name,$artistId){
+		if(empty($name)){
+			return 0;
+		}
+		$name=strtolower($name);
+		if(!isset(self::$albumIdCache[$artistId])){
+			self::$albumIdCache[$artistId]=array();
+		}
+		if(isset(self::$albumIdCache[$artistId][$name])){
+			return self::$albumIdCache[$artistId][$name];
+		}else{
+			$query=OC_DB::prepare("SELECT album_id FROM *PREFIX*media_albums WHERE album_name LIKE ? AND album_artist=?");
+			$albums=$query->execute(array($name,$artistId))->fetchAll();
+			if(is_array($albums) and isset($albums[0])){
+				self::$albumIdCache[$artistId][$name]=$albums[0]['album_id'];
+				return $albums[0]['album_id'];
+			}else{
+				return 0;
+			}
+		}
+	}
+
+	/**
+	* get the id of an song (case-insensitive)
+	* @param string name
+	* @param int artistId
+	* @param int albumId
+	* @return int
+	*/
+	public static function getSongId($name,$artistId,$albumId){
+		if(empty($name)){
+			return 0;
+		}
+		$name=strtolower($name);
+		if(!isset(self::$albumIdCache[$artistId])){
+			self::$albumIdCache[$artistId]=array();
+		}
+		if(!isset(self::$albumIdCache[$artistId][$albumId])){
+			self::$albumIdCache[$artistId][$albumId]=array();
+		}
+		if(isset(self::$albumIdCache[$artistId][$albumId][$name])){
+			return self::$albumIdCache[$artistId][$albumId][$name];
+		}else{
+			$uid=$_SESSION['user_id'];
+			$query=OC_DB::prepare("SELECT song_id FROM *PREFIX*media_songs WHERE song_user=? AND song_name LIKE ? AND song_artist=? AND song_album=?");
+			$songs=$query->execute(array($uid,$name,$artistId,$albumId))->fetchAll();
+			if(is_array($songs) and isset($songs[0])){
+				self::$albumIdCache[$artistId][$albumId][$name]=$songs[0]['song_id'];
+				return $songs[0]['song_id'];
+			}else{
+				return 0;
+			}
+		}
+	}
+	
+	/**
+	* Get the list of artists that (optionally) match a search string
+	* @param string search optional
+	* @return array the list of artists found
+	*/
+	static public function getArtists($search='%',$exact=false){
+		if(!$exact and $search!='%'){
+			$search="%$search%";
+		}elseif($search==''){
+			$search='%';
+		}
+		$query=OC_DB::prepare("SELECT DISTINCT *PREFIX*media_artists.artist_name AS artist_name , *PREFIX*media_artists.artist_id AS artist_id FROM *PREFIX*media_artists
+			INNER JOIN *PREFIX*media_songs ON *PREFIX*media_artists.artist_id=*PREFIX*media_songs.song_artist WHERE artist_name LIKE ? AND *PREFIX*media_songs.song_user=?");
+		return $query->execute(array($search,self::$uid))->fetchAll();
+	}
+	
+	/**
+	* Add an artists to the database
+	* @param string name
+	* @return integer the artist_id of the added artist
+	*/
+	static public function addArtist($name){
+		$name=trim($name);
+		if($name==''){
+			return 0;
+		}
+		//check if the artist is already in the database
+		$artistId=self::getArtistId($name);
+		if($artistId!=0){
+			return $artistId;
+		}else{
+			$query=OC_DB::prepare("INSERT INTO `*PREFIX*media_artists` (`artist_id` ,`artist_name`) VALUES (NULL ,  ?)");
+			$result=$query->execute(array($name));
+			return self::getArtistId($name);;
+		}
+	}
+	
+	/**
+	* Get the list of albums that (optionally) match an artist and/or search string
+	* @param integer artist optional
+	* @param string search optional
+	* @return array the list of albums found
+	*/
+	static public function getAlbums($artist=0,$search='%',$exact=false){
+		$cmd="SELECT DISTINCT *PREFIX*media_albums.album_name AS album_name , *PREFIX*media_albums.album_artist AS album_artist , *PREFIX*media_albums.album_id AS album_id
+			FROM *PREFIX*media_albums INNER JOIN *PREFIX*media_songs ON *PREFIX*media_albums.album_id=*PREFIX*media_songs.song_album WHERE *PREFIX*media_songs.song_user=? ";
+		$params=array(self::$uid);
+		if($artist!=0){
+			$cmd.="AND *PREFIX*media_albums.album_artist = ? ";
+			array_push($params,$artist);
+		}
+		if($search!='%'){
+			$cmd.="AND *PREFIX*media_albums.album_name LIKE ? ";
+			if(!$exact){
+				$search="%$search%";
+			}
+			array_push($params,$search);
+		}
+		$query=OC_DB::prepare($cmd);
+		return $query->execute($params)->fetchAll();
+	}
+	
+	/**
+	* Add an album to the database
+	* @param string name
+	* @param integer artist
+	* @return integer the album_id of the added artist
+	*/
+	static public function addAlbum($name,$artist){
+		$name=trim($name);
+		if($name==''){
+			return 0;
+		}
+		//check if the album is already in the database
+		$albumId=self::getAlbumId($name,$artist);
+		if($albumId!=0){
+			return $albumId;
+		}else{
+			$query=OC_DB::prepare("INSERT INTO  `*PREFIX*media_albums` (`album_id` ,`album_name` ,`album_artist`) VALUES (NULL , ?, ?)");
+			$query->execute(array($name,$artist));
+			return self::getAlbumId($name,$artist);
+		}
+	}
+	
+	/**
+	* Get the list of songs that (optionally) match an artist and/or album and/or search string
+	* @param integer artist optional
+	* @param integer album optional
+	* @param string search optional
+	* @return array the list of songs found
+	*/
+	static public function getSongs($artist=0,$album=0,$search='',$exact=false){
+		$uid=self::$uid;
+		if(empty($uid)){
+			$uid=self::$uid=$_SESSION['user_id'];
+		}
+		$params=array($uid);
+		if($artist!=0){
+			$artistString="AND song_artist = ?";
+			array_push($params,$artist);
+		}else{
+			$artistString='';
+		}
+		if($album!=0){
+			$albumString="AND song_album = ?";
+			array_push($params,$album);
+		}else{
+			$albumString='';
+		}
+		if($search){
+			if(!$exact){
+				$search="%$search%";
+			}
+			$searchString ="AND song_name LIKE ?";
+			array_push($params,$search);
+		}else{
+			$searchString='';
+		}
+		$query=OC_DB::prepare("SELECT * FROM *PREFIX*media_songs WHERE song_user=? $artistString $albumString $searchString");
+		return $query->execute($params)->fetchAll();
+	}
+	
+	/**
+	* Add an song to the database
+	* @param string name
+	* @param string path
+	* @param integer artist
+	* @param integer album
+	* @return integer the song_id of the added artist
+	*/
+	static public function addSong($name,$path,$artist,$album,$length,$track,$size){
+		$name=trim($name);
+		$path=trim($path);
+		if($name=='' or $path==''){
+			return 0;
+		}
+		$uid=$_SESSION['user_id'];
+		//check if the song is already in the database
+		$songId=self::getSongId($name,$artist,$album);
+		if($songId!=0){
+			return $songId;
+		}else{
+			if(!isset(self::$queries['addsong'])){
+				$query=OC_DB::prepare("INSERT INTO  `*PREFIX*media_songs` (`song_name` ,`song_artist` ,`song_album` ,`song_path` ,`song_user`,`song_length`,`song_track`,`song_size`,`song_playcount`,`song_lastplayed`)
+				VALUES (?, ?, ?, ?,?,?,?,?,0,0)");
+				self::$queries['addsong']=$query;
+			}else{
+				$query=self::$queries['addsong'];
+			}
+			$query->execute(array($name,$artist,$album,$path,$uid,$length,$track,$size));
+			$songId=OC_DB::insertid();
+// 			self::setLastUpdated();
+			return self::getSongId($name,$artist,$album);
+		}
+	}
+	
+	public static function getSongCount(){
+		$query=OC_DB::prepare("SELECT COUNT(song_id) AS count FROM *PREFIX*media_songs");
+		$result=$query->execute()->fetchAll();
+		return $result[0]['count'];
+	}
+	
+	public static function getArtistCount(){
+		$query=OC_DB::prepare("SELECT COUNT(artist_id) AS count FROM *PREFIX*media_artists");
+		$result=$query->execute()->fetchAll();
+		return $result[0]['count'];
+	}
+	
+	public static function getAlbumCount(){
+		$query=OC_DB::prepare("SELECT COUNT(album_id) AS count FROM *PREFIX*media_albums");
+		$result=$query->execute()->fetchAll();
+		return $result[0]['count'];
+	}
+	
+	public static function getArtistName($artistId){
+		$query=OC_DB::prepare("SELECT artist_name FROM *PREFIX*media_artists WHERE artist_id=?");
+		$artist=$query->execute(array($artistId))->fetchAll();
+		if(count($artist)>0){
+			return $artist[0]['artist_name'];
+		}else{
+			return '';
+		}
+	}
+	
+	public static function getAlbumName($albumId){
+		$query=OC_DB::prepare("SELECT album_name FROM *PREFIX*media_albums WHERE album_id=?");
+		$album=$query->execute(array($albumId))->fetchAll();
+		if(count($album)>0){
+			return $album[0]['album_name'];
+		}else{
+			return '';
+		}
+	}
+	
+	public static function getSong($id){
+		$query=OC_DB::prepare("SELECT * FROM *PREFIX*media_songs WHERE song_id=?");
+		$song=$query->execute(array($id))->fetchAll();
+		if(count($song)>0){
+			return $song[0];
+		}else{
+			return '';
+		}
+	}
+	
+	/**
+	 * get the number of songs in a directory
+	 * @param string $path
+	 */
+	public static function getSongCountByPath($path){
+		$query=OC_DB::prepare("SELECT COUNT(song_id) AS count FROM *PREFIX*media_songs WHERE song_path LIKE ?");
+		$result=$query->execute(array("$path%"))->fetchAll();
+		return $result[0]['count'];
+	}
+
+	/**
+	 * remove a song from the database by path
+	 * @param string $path the path of the song
+	 *
+	 * if a path of a folder is passed, all songs stored in the folder will be removed from the database
+	 */
+	public static function deleteSongByPath($path){
+		$query=OC_DB::prepare("DELETE FROM *PREFIX*media_songs WHERE song_path LIKE ?");
+		$query->execute(array("$path%"));
+	}
+
+	/**
+	 * increase the play count of a song
+	 * @param int songId
+	 */
+	public static function registerPlay($songId){
+		$now=time();
+		$query=OC_DB::prepare('UPDATE *PREFIX*media_songs SET song_playcount=song_playcount+1, song_lastplayed=? WHERE song_id=? AND song_lastplayed<?');
+		$query->execute(array($now,$songId,$now-60));
+	}
+
+	/**
+	 * get the id of the song by path
+	 * @param string $path
+	 * @return int
+	 */
+	public static function getSongByPath($path){
+		$query=OC_DB::prepare("SELECT song_id FROM *PREFIX*media_songs WHERE song_path = ?");
+		$result=$query->execute(array($path))->fetchAll();
+		if(count($result)>0){
+			return $result[0]['song_id'];
+		}else{
+			return 0;
+		}
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/apps/media/lib_media.php b/apps/media/lib_media.php
new file mode 100644
index 0000000000000000000000000000000000000000..3086f84a93a6794b8f46524c6622d14b25927fc8
--- /dev/null
+++ b/apps/media/lib_media.php
@@ -0,0 +1,103 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 diconnectstributed 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+//we need to have the sha256 hash of passwords for ampache
+OC_Hook::connect('OC_User','post_login','OC_MEDIA','loginListener');
+
+//connect to the filesystem for auto updating
+OC_Hook::connect('OC_Filesystem','post_write','OC_MEDIA','updateFile');
+
+//listen for file deletions to clean the database if a song is deleted
+OC_Hook::connect('OC_Filesystem','delete','OC_MEDIA','deleteFile');
+
+class OC_MEDIA{
+	/**
+	 * get the sha256 hash of the password needed for ampache
+	 * @param array $params, parameters passed from OC_Hook
+	 */
+	public static function loginListener($params){
+		if(isset($_POST['user']) and $_POST['password']){
+			error_log('postlogin');
+			$name=$_POST['user'];
+			$query=OC_DB::prepare("SELECT user_id from *PREFIX*media_users WHERE user_id LIKE ?");
+			$uid=$query->execute(array($name))->fetchAll();
+			if(count($uid)==0){
+				$password=hash('sha256',$_POST['password']);
+				$query=OC_DB::prepare("INSERT INTO *PREFIX*media_users (user_id, user_password_sha256) VALUES (?, ?);");
+				$query->execute(array($name,$password));
+			}
+		}
+	}
+	
+	/**
+	 *
+	 */
+	public static function updateFile($params){
+		$path=$params['path'];
+		require_once 'lib_scanner.php';
+		require_once 'lib_collection.php';
+		//fix a bug where there were multiply '/' in front of the path, it should only be one
+		while($path[0]=='/'){
+			$path=substr($path,1);
+		}
+		$path='/'.$path;
+		OC_MEDIA_SCANNER::scanFile($path);
+	}
+
+	/**
+	 *
+	 */
+	public static function deleteFile($params){
+		$path=$params['path'];
+		require_once 'lib_collection.php';
+		OC_MEDIA_COLLECTION::deleteSongByPath($path);
+	}
+}
+
+class OC_MediaSearchProvider extends OC_Search_Provider{
+	function search($query){
+		require_once('lib_collection.php');
+		$artists=OC_MEDIA_COLLECTION::getArtists($query);
+		$albums=OC_MEDIA_COLLECTION::getAlbums(0,$query);
+		$songs=OC_MEDIA_COLLECTION::getSongs(0,0,$query);
+		$results=array();
+		foreach($artists as $artist){
+			$results[]=new OC_Search_Result($artist['artist_name'],'',OC_Helper::linkTo( 'apps/media', 'index.php#artist='.urlencode($artist['artist_name']) ),'Music');
+		}
+		foreach($albums as $album){
+			$artist=OC_MEDIA_COLLECTION::getArtistName($album['album_artist']);
+			$results[]=new OC_Search_Result($album['album_name'],'by '.$artist,OC_Helper::linkTo( 'apps/media', 'index.php#artist='.urlencode($artist).'&album='.urlencode($album['album_name']) ),'Music');
+		}
+		foreach($songs as $song){
+			$minutes=floor($song['song_length']/60);
+			$secconds=$song['song_length']%60;
+			$artist=OC_MEDIA_COLLECTION::getArtistName($song['song_artist']);
+			$album=OC_MEDIA_COLLECTION::getalbumName($song['song_album']);
+			$results[]=new OC_Search_Result($song['song_name'],"by $artist, in $album $minutes:$secconds",OC_Helper::linkTo( 'apps/media', 'index.php#artist='.urlencode($artist).'&album='.urlencode($album).'&song='.urlencode($song['song_name']) ),'Music');
+		}
+		return $results;
+	}
+}
+
+new OC_MediaSearchProvider();
+?>
diff --git a/apps/media/lib_scanner.php b/apps/media/lib_scanner.php
new file mode 100644
index 0000000000000000000000000000000000000000..c774c3c9fdb69ff45d1205266d0cec0904424b68
--- /dev/null
+++ b/apps/media/lib_scanner.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+require_once('getID3/getid3/getid3.php');
+
+//class for scanning directories for music
+class OC_MEDIA_SCANNER{
+	static private $getID3=false;
+	
+	//these are used to store which artists and albums we found, it can save a lot of addArtist/addAlbum calls
+	static private $artists=array();
+	static private $albums=array();//stored as "$artist/$album" to allow albums with the same name from different artists
+	static private $useMp3Info=null;
+	
+	/**
+	 * scan a folder for music
+	 * @param string $path
+	 * @return int the number of songs found
+	 */
+	public static function scanFolder($path){
+		if (OC_Filesystem::is_dir($path)) {
+			$songs=0;
+			if ($dh = OC_Filesystem::opendir($path)) {
+				while (($filename = readdir($dh)) !== false) {
+					if($filename<>'.' and $filename<>'..' and substr($filename,0,1)!='.'){
+						$file=$path.'/'.$filename;
+						if(OC_Filesystem::is_dir($file)){
+							$songs+=self::scanFolder($file);
+						}elseif(OC_Filesystem::is_file($file)){
+							$data=self::scanFile($file);
+							if($data){
+								$songs++;
+							}
+						}
+					}
+				}
+			}
+		}elseif(OC_Filesystem::is_file($path)){
+			$songs=1;
+			self::scanFile($path);
+		}else{
+			$songs=0;
+		}
+		return $songs;
+	}
+
+	/**
+	 * scan a file for music
+	 * @param string $path
+	 * @return boolean
+	 */
+	public static function scanFile($path){
+		if(is_null(self::$useMp3Info)){
+			self::$useMp3Info=OC_Helper::canExecute("mp3info");
+		}
+		$file=OC_Filesystem::getLocalFile($path);
+		if(substr($path,-3)=='mp3' and self::$useMp3Info){//use the command line tool id3info if possible
+			$output=array();
+			$size=filesize($file);
+			exec('mp3info -p "%a\n%l\n%t\n%n\n%S" "'.$file.'"',$output);
+			if(count($output)>4){
+				$artist=$output[0];
+				$album=$output[1];
+				$title=$output[2];
+				$track=$output[3];
+				$length=$output[4];
+			}else{
+				return; //invalid mp3 file
+			}
+		}else{
+			if(!self::isMusic($path)){
+				return;
+			}
+			if(!self::$getID3){
+				self::$getID3=@new getID3();
+			}
+			$data=@self::$getID3->analyze($file);
+			getid3_lib::CopyTagsToComments($data);
+			if(!isset($data['comments'])){
+				error_log("error reading id3 tags in '$file'");
+				return;
+			}
+			if(!isset($data['comments']['artist'])){
+				error_log("error reading artist tag in '$file'");
+				$artist='unknown';
+			}else{
+				$artist=stripslashes($data['comments']['artist'][0]);
+				$artist=utf8_encode($artist);
+			}
+			if(!isset($data['comments']['album'])){
+				error_log("error reading album tag in '$file'");
+				$album='unknown';
+			}else{
+				$album=stripslashes($data['comments']['album'][0]);
+				$album=utf8_encode($album);
+			}
+			if(!isset($data['comments']['title'])){
+				error_log("error reading title tag in '$file'");
+				$title='unknown';
+			}else{
+				$title=stripslashes($data['comments']['title'][0]);
+				$title=utf8_encode($title);
+			}
+			$size=$data['filesize'];
+			$track=(isset($data['comments']['track']))?$data['comments']['track'][0]:0;
+			$length=isset($data['playtime_seconds'])?round($data['playtime_seconds']):0;
+		}
+		if(!isset(self::$artists[$artist])){
+			$artistId=OC_MEDIA_COLLECTION::addArtist($artist);
+			self::$artists[$artist]=$artistId;
+		}else{
+			$artistId=self::$artists[$artist];
+		}
+		if(!isset(self::$albums[$artist.'/'.$album])){
+			$albumId=OC_MEDIA_COLLECTION::addAlbum($album,$artistId);
+			self::$albums[$artist.'/'.$album]=$albumId;
+		}else{
+			$albumId=self::$albums[$artist.'/'.$album];
+		}
+		$songId=OC_MEDIA_COLLECTION::addSong($title,$path,$artistId,$albumId,$length,$track,$size);
+		return (!($title=='unkown' && $artist=='unkown' && $album=='unkown'))?$songId:0;
+	}
+
+	/**
+	 * quick check if a song is a music file by checking the extention, not as good as a proper mimetype check but way faster
+	 * @param string $filename
+	 * @return bool
+	 */
+	public static function isMusic($filename){
+		$ext=substr($filename,strrpos($filename,'.')+1);
+		return $ext=='mp3' || $ext=='flac' || $ext=='m4a' || $ext=='ogg' || $ext=='oga';
+	}
+}
\ No newline at end of file
diff --git a/apps/media/server/xml.server.php b/apps/media/server/xml.server.php
new file mode 100644
index 0000000000000000000000000000000000000000..516ab740072d8db8bd4a26f3eb43066f307ceca8
--- /dev/null
+++ b/apps/media/server/xml.server.php
@@ -0,0 +1,75 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+* 
+* 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 Lesser General Public 
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+* 
+*/
+
+
+require_once('../../../lib/base.php');
+require_once('../lib_collection.php');
+require_once('../lib_ampache.php');
+
+$arguments=$_POST;
+if(!isset($_POST['action']) and isset($_GET['action'])){
+	$arguments=$_GET;
+}
+
+foreach($arguments as &$argument){
+	$argument=stripslashes($argument);
+}
+ob_clean();
+global $CONFIG_DATADIRECTORY;
+if(isset($arguments['action'])){
+	error_log($arguments['action']);
+	switch($arguments['action']){
+		case 'url_to_song':
+			OC_MEDIA_AMPACHE::url_to_song($arguments);
+			break;
+		case 'play':
+			OC_MEDIA_AMPACHE::play($arguments);
+			break;
+		case 'handshake':
+			OC_MEDIA_AMPACHE::handshake($arguments);
+			break;
+		case 'ping':
+			OC_MEDIA_AMPACHE::ping($arguments);
+			break;
+		case 'artists':
+			OC_MEDIA_AMPACHE::artists($arguments);
+			break;
+		case 'artist_songs':
+			OC_MEDIA_AMPACHE::artist_songs($arguments);
+			break;
+		case 'artist_albums':
+			OC_MEDIA_AMPACHE::artist_albums($arguments);
+			break;
+		case 'albums':
+			OC_MEDIA_AMPACHE::albums($arguments);
+			break;
+		case 'album_songs':
+			OC_MEDIA_AMPACHE::album_songs($arguments);
+			break;
+		case 'search_songs':
+			OC_MEDIA_AMPACHE::search_songs($arguments);
+			break;
+	}
+}
+
+?> 
diff --git a/apps/media/templates/music.php b/apps/media/templates/music.php
new file mode 100644
index 0000000000000000000000000000000000000000..9b40faa96191f0939b7bebfe875db1f74b51ec20
--- /dev/null
+++ b/apps/media/templates/music.php
@@ -0,0 +1,50 @@
+<div id="controls">
+	<ul class="jp-controls">
+		<li><a href="#" class="jp-play action"><img class="svg" alt="<?php echo $l->t('Play');?>" src="<?php echo image_path('core', 'actions/play-big.svg'); ?>" /></a></li>
+		<li><a href="#" class="jp-pause action"><img class="svg" alt="<?php echo $l->t('Pause');?>" src="<?php echo image_path('core', 'actions/pause-big.svg'); ?>" /></a></li>
+		<li><a href="#" class="jp-previous action"><img class="svg" alt="<?php echo $l->t('Previous');?>" src="<?php echo image_path('core', 'actions/play-previous.svg'); ?>" /></a></li>
+		<li><a href="#" class="jp-next action"><img class="svg" alt="<?php echo $l->t('Next');?>" src="<?php echo image_path('core', 'actions/play-next.svg'); ?>" /></a></li>
+		<li><a href="#" class="jp-mute action"><img class="svg" alt="<?php echo $l->t('Mute');?>" src="<?php echo image_path('core', 'actions/sound.svg'); ?>" /></a></li>
+		<li><a href="#" class="jp-unmute action"><img class="svg" alt="<?php echo $l->t('Unmute');?>" src="<?php echo image_path('core', 'actions/sound-off.svg'); ?>" /></a></li>
+	</ul>
+	<div class="jp-progress">
+		<div class="jp-seek-bar">
+			<div class="jp-play-bar"></div>
+		</div>
+	</div>
+	<div class="jp-current-time"></div>
+	<div class="jp-duration"></div>
+	<div class="jp-volume-bar">
+		<div class="jp-volume-bar-value"></div>
+	</div>
+
+	<div class="player" id="jp-player"></div>
+</div>
+
+<ul id="leftcontent"></ul>
+
+<div id="rightcontent">
+<div id="scan">
+	<p id="scancount" style="display:none"><span class="songCount">0</span> <?php echo $l->t('Songs scanned')?></p>
+	<input type="button" class="start" value="<?php echo $l->t('Rescan Collection')?>" />
+	<input type="button" class="stop" style="display:none" value="<?php echo $l->t('Pause')?>" />
+	<div id="scanprogressbar"></div>
+	
+</div>
+<table id="collection">
+	<thead>
+		<tr>
+			<th><?php echo $l->t('Artist')?></th>
+			<th><?php echo $l->t('Album')?></th>
+			<th><?php echo $l->t('Title')?></th>
+		</tr>
+	</thead>
+	<tbody>
+		<tr class="template">
+			<td class="artist"><a></a></td>
+			<td class="album"><a></a></td>
+			<td class="title"><a></a></td>
+		</tr>
+	</tbody>
+</table>
+</div>
diff --git a/apps/media/tomahawk.php b/apps/media/tomahawk.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf0c2c2a7563b13189c04b8c9e7d73974d82fd3c
--- /dev/null
+++ b/apps/media/tomahawk.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+* ownCloud - media plugin
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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 Lesser General Public
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+$_POST=$_GET; //debug
+
+require_once('../../lib/base.php');
+require_once('lib_collection.php');
+
+$user=isset($_POST['user'])?$_POST['user']:'';
+$pass=isset($_POST['pass'])?$_POST['pass']:'';
+if(OC_User::checkPassword($user,$pass)){
+	OC_Util::setupFS($user);
+	OC_MEDIA_COLLECTION::$uid=$user;
+}else{
+	exit;
+}
+
+if(isset($_POST['play']) and $_POST['play']=='true'){
+	if(!isset($_POST['song'])){
+		exit;
+	}
+	$song=OC_MEDIA_COLLECTION::getSong($_POST['song']);
+	$ftype=OC_Filesystem::getMimeType( $song['song_path'] );
+	header('Content-Type:'.$ftype);
+	header('Expires: 0');
+	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+	header('Pragma: public');
+	header('Content-Length: '.OC_Filesystem::filesize($song['song_path']));
+
+	OC_Filesystem::readfile($song['song_path']);
+}
+
+$artist=isset($_POST['artist'])?'%'.$_POST['artist'].'%':'';
+$album=isset($_POST['album'])?'%'.$_POST['album'].'%':'';
+$song=isset($_POST['song'])?$_POST['song']:'';
+
+$artist=OC_MEDIA_COLLECTION::getArtistId($artist);
+$album=OC_MEDIA_COLLECTION::getAlbumId($album,$artist);
+
+$songs=OC_MEDIA_COLLECTION::getSongs($artist,$album,$song);
+
+$baseUrl=OC_Util::getServerURL().OC_Helper::linkTo('media','tomahawk.php');
+
+$results=array();
+foreach($songs as $song) {
+	$results[] = (Object) array(
+		'artist' => OC_MEDIA_COLLECTION::getArtistName($song['song_artist']),
+		'album' => OC_MEDIA_COLLECTION::getAlbumName($song['song_album']),
+		'track' => $song['song_name'],
+		'source' => 'ownCloud',
+		'mimetype' => OC_Filesystem::getMimeType($song['song_path']),
+		'extension' => substr($song['song_path'],strrpos($song['song_path'],'.')),
+		'url' => $baseUrl.'?play=true&song='.$song['song_id'],
+		'bitrate' => round($song['song_id']/$song['song_length'],0),
+		'duration' => round($song['song_length'],0),
+		'size' => $song['song_size'],
+		'score' => (float)1.0
+	);
+}
+echo json_encode($results);
+?>
\ No newline at end of file
diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..7906241f79b819d9222937be58ee1aa0c15fe119
--- /dev/null
+++ b/apps/user_ldap/appinfo/app.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+* ownCloud - user_ldap
+*
+* @author Dominik Schmidt
+* @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
+* 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('apps/user_ldap/user_ldap.php');
+
+OC_APP::registerAdmin('user_ldap','settings');
+
+// define LDAP_DEFAULT_PORT
+define("OC_USER_BACKEND_LDAP_DEFAULT_PORT", 389);
+
+// register user backend
+OC_User::useBackend( "LDAP" );
+
+// add settings page to navigation
+$entry = array(
+	'id' => "user_ldap_settings",
+	'order'=>1,
+	'href' => OC_Helper::linkTo( "user_ldap", "settings.php" ),
+	'name' => 'LDAP'
+);
+// OC_App::addNavigationSubEntry( "core_users", $entry);
diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9a6ee1436fc5f43bbc7a706a2d049e0f55c14a03
--- /dev/null
+++ b/apps/user_ldap/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>user_ldap</id>
+	<name>LDAP user backend</name>
+	<description>Authenticate Users by LDAP</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Dominik Schmidt</author>
+	<require>2</require>
+</info>
diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..8dbd3c0462b7959a40ca60e8ce5987d05d6a2ddb
--- /dev/null
+++ b/apps/user_ldap/settings.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * ownCloud - user_ldap
+ *
+ * @author Dominik Schmidt
+ * @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
+ * 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/>.
+ *
+ */
+$params = array('ldap_host', 'ldap_port', 'ldap_dn', 'ldap_password', 'ldap_base', 'ldap_filter');
+
+foreach($params as $param){
+	if(isset($_POST[$param])){
+		OC_Appconfig::setValue('user_ldap', $param, $_POST[$param]);
+	}
+}
+
+// fill template
+$tmpl = new OC_Template( 'user_ldap', 'settings');
+foreach($params as $param){
+		$value = OC_Appconfig::getValue('user_ldap', $param,'');
+		$tmpl->assign($param, $value);
+}
+
+// ldap_port has a default value
+$tmpl->assign( 'ldap_port', OC_Appconfig::getValue('user_ldap', 'ldap_port', OC_USER_BACKEND_LDAP_DEFAULT_PORT));
+
+return $tmpl->fetchPage();
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..32e1b29dafb219d6c006ad73aa1eb552dc903067
--- /dev/null
+++ b/apps/user_ldap/templates/settings.php
@@ -0,0 +1,12 @@
+<form id="ldap" action="#" method="post">
+	<fieldset class="personalblock">
+		<legend><strong>LDAP</strong></legend>
+		<p><label for="ldap_host">Host<input type="text" id="ldap_host" name="ldap_host" value="<?php echo $_['ldap_host']; ?>"></label>
+		<label for="ldap_port">Port</label><input type="text" id="ldap_port" name="ldap_port" value="<?php echo $_['ldap_port']; ?>" /></p>
+		<p><label for="ldap_dn">Name</label><input type="text" id="ldap_dn" name="ldap_dn" value="<?php echo $_['ldap_dn']; ?>" />
+		<label for="ldap_password">Password</label><input type="password" id="ldap_password" name="ldap_password" value="<?php echo $_['ldap_password']; ?>" /></p>
+		<p><label for="ldap_base">Base</label><input type="text" id="ldap_base" name="ldap_base" value="<?php echo $_['ldap_base']; ?>" />
+		<label for="ldap_filter">Filter (use %uid placeholder)</label><input type="text" id="ldap_filter" name="ldap_filter" value="<?php echo $_['ldap_filter']; ?>" /></p>
+		<input type="submit" value="Save" />
+	</fieldset>
+</form>
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
new file mode 100644
index 0000000000000000000000000000000000000000..1154efc17b194e0810109fd22502e79c56e13295
--- /dev/null
+++ b/apps/user_ldap/user_ldap.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Dominik Schmidt
+ * @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
+ * 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/>.
+ *
+ */
+
+class OC_USER_LDAP extends OC_User_Backend {
+
+	protected $ds;
+	protected $configured = false;
+
+	// cached settings
+	protected $ldap_host;
+	protected $ldap_port;
+	protected $ldap_dn;
+	protected $ldap_password;
+	protected $ldap_base;
+	protected $ldap_filter;
+
+	function __construct() {
+		$this->ldap_host = OC_Appconfig::getValue('user_ldap', 'ldap_host','');
+		$this->ldap_port = OC_Appconfig::getValue('user_ldap', 'ldap_port', OC_USER_BACKEND_LDAP_DEFAULT_PORT	);
+		$this->ldap_dn = OC_Appconfig::getValue('user_ldap', 'ldap_dn','');
+		$this->ldap_password = OC_Appconfig::getValue('user_ldap', 'ldap_password','');
+		$this->ldap_base = OC_Appconfig::getValue('user_ldap', 'ldap_base','');
+		$this->ldap_filter = OC_Appconfig::getValue('user_ldap', 'ldap_filter','');
+
+		if( !empty($this->ldap_host)
+			&& !empty($this->ldap_port)
+			&& !empty($this->ldap_dn)
+			&& !empty($this->ldap_password)
+			&& !empty($this->ldap_base)
+			&& !empty($this->ldap_filter)
+		)
+		{
+			$this->configured = true;
+		}
+	}
+
+	function __destruct() {
+		// close the connection
+		if( $this->ds )
+			ldap_unbind($this->ds);
+	}
+
+	private function getDs() {
+		if(!$this->ds) {
+			$this->ds = ldap_connect( $this->ldap_host, $this->ldap_port );
+			   if(ldap_set_option($this->ds, LDAP_OPT_PROTOCOL_VERSION, 3))
+				 if(ldap_set_option($this->ds, LDAP_OPT_REFERRALS, 0))
+					  ldap_start_tls($this->ds);
+		}
+
+		// login
+		if(!empty($this->ldap_dn)) {
+			$ldap_login = @ldap_bind( $this->ds, $this->ldap_dn, $this->ldap_password );
+			if(!$ldap_login)
+				return false;
+		}
+
+		return $this->ds;
+	}
+
+	private function getDn( $uid ) {
+		if(!$this->configured)
+			return false;
+
+		// connect to server
+		$ds = $this->getDs();
+		if( !$ds )
+			return false;
+
+		// get dn
+		$filter = str_replace("%uid", $uid, $this->ldap_filter);
+		$sr = ldap_search( $this->getDs(), $this->ldap_base, $filter );
+		$entries = ldap_get_entries( $this->getDs(), $sr );
+
+		if( $entries["count"] == 0 )
+			return false;
+
+		return $entries[0]["dn"];
+	}
+	public function checkPassword( $uid, $password ) {
+		if(!$this->configured){
+			return false;
+		}
+		$dn = $this->getDn( $uid );
+		if( !$dn )
+			return false;
+
+		if (!@ldap_bind( $this->getDs(), $dn, $password ))
+			return false;
+		return $uid;
+	}
+
+	public function userExists( $uid ) {
+		if(!$this->configured){
+			return false;
+		}
+		$dn = $this->getDn($uid);
+		return !empty($dn);
+	}
+
+}
+
+?>
diff --git a/apps/user_openid/appinfo/app.php b/apps/user_openid/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..3cdf2664e8168b1d566f44688f217703dac0201c
--- /dev/null
+++ b/apps/user_openid/appinfo/app.php
@@ -0,0 +1,57 @@
+<?php
+
+//check if curl extension installed
+if (!in_array ('curl', get_loaded_extensions())){
+	return;
+}
+
+$urlBase=((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'];
+
+$userName='';
+if(strpos($_SERVER["REQUEST_URI"],'?') and !strpos($_SERVER["REQUEST_URI"],'=')){
+	if(strpos($_SERVER["REQUEST_URI"],'/?')){
+		$userName=substr($_SERVER["REQUEST_URI"],strpos($_SERVER["REQUEST_URI"],'/?')+2);
+	}elseif(strpos($_SERVER["REQUEST_URI"],'.php?')){
+		$userName=substr($_SERVER["REQUEST_URI"],strpos($_SERVER["REQUEST_URI"],'.php?')+5);
+	}
+}
+
+OC_Util::addHeader('link',array('rel'=>'openid.server', 'href'=>$urlBase.OC_Helper::linkTo( "user_openid", "user.php" ).'/'.$userName));
+OC_Util::addHeader('link',array('rel'=>'openid.delegate', 'href'=>$urlBase.OC_Helper::linkTo( "user_openid", "user.php" ).'/'.$userName));
+
+OC_APP::registerPersonal('user_openid','settings');
+
+require_once 'apps/user_openid/user_openid.php';
+
+//active the openid backend
+OC_User::useBackend('openid');
+
+//check for results from openid requests
+if(isset($_GET['openid_mode']) and $_GET['openid_mode'] == 'id_res'){
+	error_log('openid retured');
+	$openid = new SimpleOpenID;
+	$openid->SetIdentity($_GET['openid_identity']);
+	$openid_validation_result = $openid->ValidateWithServer();
+	if ($openid_validation_result == true){         // OK HERE KEY IS VALID
+		error_log('auth sucessfull');
+		global $WEBROOT;
+		$identity=$openid->GetIdentity();
+		error_log("auth as $identity");
+		$user=OC_USER_OPENID::findUserForIdentity($identity);
+		if($user){
+			$_SESSION['user_id']=$user;
+			header("Location: $WEBROOT");
+		}
+	}else if($openid->IsError() == true){            // ON THE WAY, WE GOT SOME ERROR
+		$error = $openid->GetError();
+		error_log("ERROR CODE: " . $error['code']);
+		error_log("ERROR DESCRIPTION: " . $error['description']);
+	}else{                                            // Signature Verification Failed
+		error_log("INVALID AUTHORIZATION");
+	}
+}else if (isset($_GET['openid_mode']) and $_GET['openid_mode'] == 'cancel'){ // User Canceled your Request
+	error_log("USER CANCELED REQUEST");
+	return false;
+}
+
+?>
diff --git a/apps/user_openid/appinfo/info.xml b/apps/user_openid/appinfo/info.xml
new file mode 100644
index 0000000000000000000000000000000000000000..32525009d616ed7bbb31000dc0ba4723ecd7f05a
--- /dev/null
+++ b/apps/user_openid/appinfo/info.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<info>
+	<id>user_openid</id>
+	<name>OpenID user backend</name>
+	<description>Allow login through OpenID</description>
+	<version>0.1</version>
+	<licence>AGPL</licence>
+	<author>Robin Appelman</author>
+	<require>2</require>
+</info>
\ No newline at end of file
diff --git a/apps/user_openid/class.openid.v3.php b/apps/user_openid/class.openid.v3.php
new file mode 100644
index 0000000000000000000000000000000000000000..8afb9e5b8175be25c28ed43aea6f8690250cd1cd
--- /dev/null
+++ b/apps/user_openid/class.openid.v3.php
@@ -0,0 +1,328 @@
+<?php
+/*
+	FREE TO USE
+        Under License: GPLv3
+	Simple OpenID PHP Class
+	
+	Some modifications by Eddie Roosenmaallen, eddie@roosenmaallen.com
+	
+-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+
+This Class was written to make easy for you to integrate OpenID on your website. 
+This is just a client, which checks for user's identity. This Class Requires CURL Module.
+It should be easy to use some other HTTP Request Method, but remember, often OpenID servers
+are using SSL.
+We need to be able to perform SSL Verification on the background to check for valid signature.
+
+HOW TO USE THIS CLASS:
+  STEP 1)
+	$openid = new SimpleOpenID;
+	:: SET IDENTITY ::
+		$openid->SetIdentity($_POST['openid_url']);
+	:: SET RETURN URL ::
+		$openid->SetApprovedURL('http://www.yoursite.com/return.php'); // Script which handles a response from OpenID Server
+	:: SET TRUST ROOT ::
+		$openid->SetTrustRoot('http://www.yoursite.com/');
+	:: FETCH SERVER URL FROM IDENTITY PAGE ::  [Note: It is recomended to cache this (Session, Cookie, Database)]
+		$openid->GetOpenIDServer(); // Returns false if server is not found
+	:: REDIRECT USER TO OPEN ID SERVER FOR APPROVAL ::
+	
+	:: (OPTIONAL) SET OPENID SERVER ::
+		$openid->SetOpenIDServer($server_url); // If you have cached previously this, you don't have to call GetOpenIDServer and set value this directly
+		
+	STEP 2)
+	Once user gets returned we must validate signature
+	:: VALIDATE REQUEST ::
+		true|false = $openid->ValidateWithServer();
+		
+	ERRORS:
+		array = $openid->GetError(); 	// Get latest Error code
+	
+	FIELDS:
+		OpenID allowes you to retreive a profile. To set what fields you'd like to get use (accepts either string or array):
+		$openid->SetRequiredFields(array('email','fullname','dob','gender','postcode','country','language','timezone'));
+		 or
+		$openid->SetOptionalFields('postcode');
+		
+IMPORTANT TIPS:
+OPENID as is now, is not trust system. It is a great single-sign on method. If you want to 
+store information about OpenID in your database for later use, make sure you handle url identities
+properly.
+  For example:
+	https://steve.myopenid.com/
+	https://steve.myopenid.com
+	http://steve.myopenid.com/
+	http://steve.myopenid.com
+	... are representing one single user. Some OpenIDs can be in format openidserver.com/users/user/ - keep this in mind when storing identities
+
+	To help you store an OpenID in your DB, you can use function:
+		$openid_db_safe = $openid->OpenID_Standarize($upenid);
+	This may not be comatible with current specs, but it works in current enviroment. Use this function to get openid
+	in one format like steve.myopenid.com (without trailing slashes and http/https).
+	Use output to insert Identity to database. Don't use this for validation - it may fail.
+
+*/
+
+class SimpleOpenID{
+	var $openid_url_identity;
+	var $URLs = array();
+	var $error = array();
+	var $fields = array(
+		'required'	 => array(),
+		'optional'	 => array(),
+	);
+	
+	function SimpleOpenID(){
+		if (!function_exists('curl_exec')) {
+			die('Error: Class SimpleOpenID requires curl extension to work');
+		}
+	}
+	function SetOpenIDServer($a){
+		$this->URLs['openid_server'] = $a;
+	}
+	function SetTrustRoot($a){
+		$this->URLs['trust_root'] = $a;
+	}
+	function SetCancelURL($a){
+		$this->URLs['cancel'] = $a;
+	}
+	function SetApprovedURL($a){
+		$this->URLs['approved'] = $a;
+	}
+	function SetRequiredFields($a){
+		if (is_array($a)){
+			$this->fields['required'] = $a;
+		}else{
+			$this->fields['required'][] = $a;
+		}
+	}
+	function SetOptionalFields($a){
+		if (is_array($a)){
+			$this->fields['optional'] = $a;
+		}else{
+			$this->fields['optional'][] = $a;
+		}
+	}
+	function SetIdentity($a){ 	// Set Identity URL
+ 			if ((stripos($a, 'http://') === false)
+ 			   && (stripos($a, 'https://') === false)){
+		 		$a = 'http://'.$a;
+		 	}
+/*
+			$u = parse_url(trim($a));
+			if (!isset($u['path'])){
+				$u['path'] = '/';
+			}else if(substr($u['path'],-1,1) == '/'){
+				$u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
+			}
+			if (isset($u['query'])){ // If there is a query string, then use identity as is
+				$identity = $a;
+			}else{
+				$identity = $u['scheme'] . '://' . $u['host'] . $u['path'];
+			}
+//*/
+			$this->openid_url_identity = $a;
+	}
+	function GetIdentity(){ 	// Get Identity
+		return $this->openid_url_identity;
+	}
+	function GetError(){
+		$e = $this->error;
+		return array('code'=>$e[0],'description'=>$e[1]);
+	}
+
+	function ErrorStore($code, $desc = null){
+		$errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.';
+		if ($desc == null){
+			$desc = $errs[$code];
+		}
+	   	$this->error = array($code,$desc);
+	}
+
+	function IsError(){
+		if (count($this->error) > 0){
+			return true;
+		}else{
+			return false;
+		}
+	}
+	
+	function splitResponse($response) {
+		$r = array();
+		$response = explode("\n", $response);
+		foreach($response as $line) {
+			$line = trim($line);
+			if ($line != "") {
+				list($key, $value) = explode(":", $line, 2);
+				$r[trim($key)] = trim($value);
+			}
+		}
+	 	return $r;
+	}
+	
+	function OpenID_Standarize($openid_identity = null){
+		if ($openid_identity === null)
+			$openid_identity = $this->openid_url_identity;
+
+		$u = parse_url(strtolower(trim($openid_identity)));
+		
+		if (!isset($u['path']) || ($u['path'] == '/')) {
+			$u['path'] = '';
+		}
+		if(substr($u['path'],-1,1) == '/'){
+			$u['path'] = substr($u['path'], 0, strlen($u['path'])-1);
+		}
+		if (isset($u['query'])){ // If there is a query string, then use identity as is
+			return $u['host'] . $u['path'] . '?' . $u['query'];
+		}else{
+			return $u['host'] . $u['path'];
+		}
+	}
+	
+	function array2url($arr){ // converts associated array to URL Query String
+		if (!is_array($arr)){
+			return false;
+		}
+		$query = '';
+		foreach($arr as $key => $value){
+			$query .= $key . "=" . $value . "&";
+		}
+		return $query;
+	}
+	function FSOCK_Request($url, $method="GET", $params = ""){
+		$fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds
+		if (!$fp) {
+			$this->ErrorStore('OPENID_SOCKETERROR', $errstr);
+		   	return false;
+		} else {
+			$request = $method . " /server HTTP/1.0\r\n";
+			$request .= "User-Agent: Simple OpenID PHP Class (http://www.phpclasses.org/simple_openid)\r\n";
+			$request .= "Connection: close\r\n\r\n";
+		   	fwrite($fp, $request);
+		   	stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds
+		   	$res = fread($fp, 2000);
+		   	$info = stream_get_meta_data($fp);
+		   	fclose($fp);
+		
+		   	if ($info['timed_out']) {
+		       $this->ErrorStore('OPENID_SOCKETTIMEOUT');
+		   	} else {
+		      	return $res;
+		   	}
+		}
+	}
+	function CURL_Request($url, $method="GET", $params = "") { // Remember, SSL MUST BE SUPPORTED
+			if (is_array($params)) $params = $this->array2url($params);
+			$curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : ""));
+			curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
+			curl_setopt($curl, CURLOPT_HEADER, false);
+			curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
+			curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET"));
+			curl_setopt($curl, CURLOPT_POST, ($method == "POST"));
+			if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
+			curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
+			$response = curl_exec($curl);
+			
+			if (curl_errno($curl) == 0){
+				$response;
+			}else{
+				$this->ErrorStore('OPENID_CURL', curl_error($curl));
+			}
+			return $response;
+	}
+	
+	 function HTML2OpenIDServer($content) {
+		$get = array();
+		
+		// Get details of their OpenID server and (optional) delegate
+		preg_match_all('/<link[^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
+		preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*\/?>/i', $content, $matches2);
+		$servers = array_merge($matches1[2], $matches2[1]);
+		
+		preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1);
+		
+		preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2);
+		
+		$delegates = array_merge($matches1[1], $matches2[1]);
+		
+		$ret = array($servers, $delegates);
+		return $ret;
+	}
+	
+	function GetOpenIDServer(){
+		$response = $this->CURL_Request($this->openid_url_identity);
+		list($servers, $delegates) = $this->HTML2OpenIDServer($response);
+		if (count($servers) == 0){
+			$this->ErrorStore('OPENID_NOSERVERSFOUND');
+			return false;
+		}
+		if (isset($delegates[0])
+		  && ($delegates[0] != "")){
+			$this->SetIdentity($delegates[0]);
+		}
+		$this->SetOpenIDServer($servers[0]);
+		return $servers[0];
+	}
+	
+	function GetRedirectURL(){
+		$params = array();
+		$params['openid.return_to'] = urlencode($this->URLs['approved']);
+		$params['openid.mode'] = 'checkid_setup';
+		$params['openid.identity'] = urlencode($this->openid_url_identity);
+		$params['openid.trust_root'] = urlencode($this->URLs['trust_root']);
+		
+		if (isset($this->fields['required'])
+		  && (count($this->fields['required']) > 0)) {
+			$params['openid.sreg.required'] = implode(',',$this->fields['required']);
+		}
+		if (isset($this->fields['optional'])
+		  && (count($this->fields['optional']) > 0)) {
+			$params['openid.sreg.optional'] = implode(',',$this->fields['optional']);
+		}
+		return $this->URLs['openid_server'] . "?". $this->array2url($params);
+	}
+	
+	function Redirect(){
+		$redirect_to = $this->GetRedirectURL();
+		if (headers_sent()){ // Use JavaScript to redirect if content has been previously sent (not recommended, but safe)
+			echo '<script language="JavaScript" type="text/javascript">window.location=\'';
+			echo $redirect_to;
+			echo '\';</script>';
+		}else{	// Default Header Redirect
+			header('Location: ' . $redirect_to);
+		}
+	}
+	
+	function ValidateWithServer(){
+		$params = array(
+			'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']),
+			'openid.signed' => urlencode($_GET['openid_signed']),
+			'openid.sig' => urlencode($_GET['openid_sig'])
+		);
+		// Send only required parameters to confirm validity
+		$arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed']));
+		for ($i=0; $i<count($arr_signed); $i++){
+			$s = str_replace('sreg_','sreg.', $arr_signed[$i]);
+			$c = $_GET['openid_' . $arr_signed[$i]];
+			// if ($c != ""){
+				$params['openid.' . $s] = urlencode($c);
+			// }
+		}
+		$params['openid.mode'] = "check_authentication";
+
+		$openid_server = $this->GetOpenIDServer();
+		if ($openid_server == false){
+			return false;
+		}
+		$response = $this->CURL_Request($openid_server,'POST',$params);
+		$data = $this->splitResponse($response);
+
+		if ($data['is_valid'] == "true") {
+			return true;
+		}else{
+			return false;
+		}
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/apps/user_openid/js/settings.js b/apps/user_openid/js/settings.js
new file mode 100644
index 0000000000000000000000000000000000000000..cfecd7b1cb4d0c7c81843b44a06de0e0399633d4
--- /dev/null
+++ b/apps/user_openid/js/settings.js
@@ -0,0 +1,20 @@
+$(document).ready(function(){
+	$('#openidform #identity').blur(function(event){
+		event.preventDefault();
+		var post = $( "#openidform" ).serialize();
+		$.post( 'ajax/openid.php', post, function(data){
+			if( data.status == "success" ){
+			}else{
+				alert('error while setting OpenID');
+			}
+		});
+	});
+
+	// reset value when edited, workaround because of .select() not working with disabled inputs
+	$('#openid').focus(function(event){
+		openidValue = $('#openid').val();
+	});
+	$('#openid').blur(function(event){
+		$('#openid').val(openidValue);
+	});
+});
diff --git a/apps/user_openid/phpmyid.php b/apps/user_openid/phpmyid.php
new file mode 100644
index 0000000000000000000000000000000000000000..24fab44ca7aa0281d33834bb804c33e9fa7c53ec
--- /dev/null
+++ b/apps/user_openid/phpmyid.php
@@ -0,0 +1,1735 @@
+<?php
+// PLEASE DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING!
+
+/**
+ * phpMyID - A standalone, single user, OpenID Identity Provider
+ *
+ * @package phpMyID
+ * @author CJ Niemira <siege (at) siege (dot) org>
+ * @copyright 2006-2008
+ * @license http://www.gnu.org/licenses/gpl.html GNU Public License
+ * @url http://siege.org/projects/phpMyID
+ * @version 0.9
+ */
+
+/**
+ * Set a constant to indicate that phpMyID is running
+ */
+define('PHPMYID_STARTED', true);
+
+/**
+ * List the known types and modes
+ * @name $known
+ * @global array $GLOBALS['known']
+ */
+$GLOBALS['known'] = array(
+	'assoc_types'	=> array('HMAC-SHA1'),
+
+	'openid_modes'	=> array('accept',
+				 'associate',
+				 'authorize',
+				 'cancel',
+				 'checkid_immediate',
+				 'checkid_setup',
+				 'check_authentication',
+				 'error',
+				 'id_res',
+				 'login',
+				 'logout',
+			 	 'test'),
+
+	'session_types'	=> array('',
+				 'DH-SHA1'),
+
+	'bigmath_types' => array('DH-SHA1'),
+);
+
+/**
+ * Defined by OpenID spec
+ * @name $g
+ * @global integer $GLOBALS['g']
+ */
+$GLOBALS['g'] = 2;
+
+/**
+ * Defined by OpenID spec
+ * @name $p
+ * @global integer $GLOBALS['p']
+ */
+$GLOBALS['p'] = '155172898181473697471232257763715539915724801966915404479707' .
+'7953140576293785419175806512274236981889937278161526466314385615958256881888' .
+'8995127215884267541995034125870655654980358010487053768147672651325574704076' .
+'5857479291291572334510643245094715007229621094194349783925984760375594985848' .
+'253359305585439638443';
+
+
+// Runmode functions
+
+/**
+ * Allow the user to accept trust on a URL
+ * @global array $profile
+ */
+function accept_mode () {
+	global $profile;
+
+	// this is a user session
+	user_session();
+
+	// the user needs refresh urls in their session to access this mode
+	if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_url']))
+		error_500('You may not access this mode directly.');
+
+	// has the user accepted the trust_root?
+	$accepted = @strlen($_REQUEST['accepted'])
+			? $_REQUEST['accepted']
+			: null;
+
+	// if so, refresh back to post_accept_url
+	if ($accepted === 'yes') {
+		$_SESSION['accepted_url'] = $_SESSION['unaccepted_url'];
+		wrap_redirect($_SESSION['post_accept_url']);
+
+	// if they rejected it, return to the client
+	} elseif ($accepted === 'no') {
+		wrap_redirect($_SESSION['cancel_accept_url']);
+	}
+
+	// if neither, offer the trust request
+	$q = strpos($profile['req_url'], '?') ? '&' : '?';
+	$yes = $profile['req_url'] . $q . 'accepted=yes';
+	$no  = $profile['req_url'] . $q . 'accepted=no';
+
+	wrap_html('The client site you are attempting to log into has requested that you trust the following URL:<br/><b>' . $_SESSION['unaccepted_url'] . '</b><br/><br/>Do you wish to continue?<br/><a href="' . $yes . '">Yes</a> | <a href="' . $no . '">No</a>');
+}
+
+/** * Perform an association with a consumer
+ * @global array $known
+ * @global array $profile
+ * @global integer $g
+ * @global integer $p
+ */
+function associate_mode () {
+	global $g, $known, $p, $profile;
+
+	// Validate the request
+	if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'associate')
+		error_400();
+
+	// Get the request options, using defaults as necessary
+	$assoc_type = (@strlen($_REQUEST['openid_assoc_type'])
+		    && in_array($_REQUEST['openid_assoc_type'], $known['assoc_types']))
+			? $_REQUEST['openid_assoc_type']
+			: 'HMAC-SHA1';
+
+	$session_type = (@strlen($_REQUEST['openid_session_type'])
+		      && in_array($_REQUEST['openid_session_type'], $known['session_types']))
+			? $_REQUEST['openid_session_type']
+			: '';
+
+	$dh_modulus = (@strlen($_REQUEST['openid_dh_modulus']))
+		? long(base64_decode($_REQUEST['openid_dh_modulus']))
+		: ($session_type == 'DH-SHA1'
+			? $p
+			: null);
+
+	$dh_gen = (@strlen($_REQUEST['openid_dh_gen']))
+		? long(base64_decode($_REQUEST['openid_dh_gen']))
+		: ($session_type == 'DH-SHA1'
+			? $g
+			: null);
+
+	$dh_consumer_public = (@strlen($_REQUEST['openid_dh_consumer_public']))
+		? $_REQUEST['openid_dh_consumer_public']
+		: ($session_type == 'DH-SHA1'
+			? error_post('dh_consumer_public was not specified')
+			: null);
+
+	$lifetime = time() + $profile['lifetime'];
+
+	// Create standard keys
+	$keys = array(
+		'assoc_type' => $assoc_type,
+		'expires_in' => $profile['lifetime']
+	);
+
+	// If I can't handle bigmath, default to plaintext sessions
+	if (in_array($session_type, $known['bigmath_types']) && $profile['use_bigmath'] === false)
+		$session_type = null;
+
+	// Add response keys based on the session type
+	switch ($session_type) {
+		case 'DH-SHA1':
+			// Create the associate id and shared secret now
+			list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
+
+			// Compute the Diffie-Hellman stuff
+			$private_key = random($dh_modulus);
+			$public_key = bmpowmod($dh_gen, $private_key, $dh_modulus);
+			$remote_key = long(base64_decode($dh_consumer_public));
+			$ss = bmpowmod($remote_key, $private_key, $dh_modulus);
+
+			$keys['assoc_handle'] = $assoc_handle;
+			$keys['session_type'] = $session_type;
+			$keys['dh_server_public'] = base64_encode(bin($public_key));
+			$keys['enc_mac_key'] = base64_encode(x_or(sha1_20(bin($ss)), $shared_secret));
+
+			break;
+
+		default:
+			// Create the associate id and shared secret now
+			list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
+
+			$keys['assoc_handle'] = $assoc_handle;
+			$keys['mac_key'] = base64_encode($shared_secret);
+	}
+
+	// Return the keys
+	wrap_kv($keys);
+}
+
+
+/**
+ * Perform a user authorization
+ * @global array $profile
+ */
+function authorize_mode () {
+	global $profile;
+	global $USERNAME;
+	global $IDENTITY;
+
+	// this is a user session
+
+	// the user needs refresh urls in their session to access this mode
+	if (! isset($_SESSION['post_auth_url']) || ! isset($_SESSION['cancel_auth_url']))
+		error_500('You may not access this mode directly.');
+
+	$profile['idp_url']=$IDENTITY;
+	if (isset($_SERVER['PHP_AUTH_USER']) && $profile['authorized'] === false && $_SERVER['PHP_AUTH_USER']==$USERNAME) {
+		if (OC_User::checkPassword($USERNAME, $_SERVER['PHP_AUTH_PW'])) {// successful login!
+			// return to the refresh url if they get in
+			$_SESSION['openid_auth']=true;
+			$_SESSION['openid_user']=$USERNAME;
+			wrap_redirect($_SESSION['post_auth_url']);
+
+		// failed login
+		} else {
+			$_SESSION['failures']++;
+			debug('Login failed');
+			debug('Fail count: ' . $_SESSION['failures']);
+		}
+
+	}
+
+	// if we get this far the user is not authorized, so send the headers
+	$uid = uniqid(mt_rand(1,9));
+	$_SESSION['uniqid'] = $uid;
+
+// 	debug('Prompting user to log in. Stale? ' . $stale);
+	header('HTTP/1.0 401 Unauthorized');
+// 	header(sprintf('WWW-Authenticate: Digest qop="auth-int, auth", realm="%s", domain="%s", nonce="%s", opaque="%s", stale="%s", algorithm="MD5"', $profile['auth_realm'], $profile['auth_domain'], $uid, md5($profile['auth_realm']), $stale ? 'true' : 'false'));
+	header('WWW-Authenticate: Basic realm="ownCloud"');
+	$q = strpos($_SESSION['cancel_auth_url'], '?') ? '&' : '?';
+	wrap_refresh($_SESSION['cancel_auth_url'] . $q . 'openid.mode=cancel');
+// 	die('401 Unauthorized');
+}
+
+
+/**
+ *  Handle a consumer's request for cancellation.
+ */
+function cancel_mode () {
+	wrap_html('Request cancelled.');
+}
+
+
+/**
+ * Handle a consumer's request to see if the user is authenticated
+ */
+function check_authentication_mode () {
+	// Validate the request
+	if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'check_authentication')
+		error_400();
+
+	$assoc_handle = @strlen($_REQUEST['openid_assoc_handle'])
+		? $_REQUEST['openid_assoc_handle']
+		: error_post('Missing assoc_handle');
+
+	$sig = @strlen($_REQUEST['openid_sig'])
+		? $_REQUEST['openid_sig']
+		: error_post('Missing sig');
+
+	$signed = @strlen($_REQUEST['openid_signed'])
+		? $_REQUEST['openid_signed']
+		: error_post('Missing signed');
+
+	// Prepare the return keys
+	$keys = array(
+		'openid.mode' => 'id_res'
+	);
+
+	// Invalidate the assoc handle if we need to
+	if (@strlen($_REQUEST['openid_invalidate_handle'])) {
+		destroy_assoc_handle($_REQUEST['openid_invalidate_handle']);
+
+		$keys['invalidate_handle'] = $_REQUEST['openid_invalidate_handle'];
+	}
+
+	// Validate the sig by recreating the kv pair and signing
+	$_REQUEST['openid_mode'] = 'id_res';
+	$tokens = '';
+	foreach (explode(',', $signed) as $param) {
+		$post = preg_replace('/\./', '_', $param);
+		$tokens .= sprintf("%s:%s\n", $param, $_REQUEST['openid_' . $post]);
+	}
+
+	// Add the sreg stuff, if we've got it
+	if (isset($sreg_required)) {
+		foreach (explode(',', $sreg_required) as $key) {
+			if (! isset($sreg[$key]))
+				continue;
+			$skey = 'sreg.' . $key;
+
+			$tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]);
+			$keys[$skey] = $sreg[$key];
+			$fields[] = $skey;
+		}
+	}
+
+	// Look up the consumer's shared_secret and timeout
+	list ($shared_secret, $expires) = secret($assoc_handle);
+
+	// if I can't verify the assoc_handle, or if it's expired
+	if ($shared_secret == false || (is_numeric($expires) && $expires < time())) {
+		$keys['is_valid'] = 'false';
+
+	} else {
+		$ok = base64_encode(hmac($shared_secret, $tokens));
+		$keys['is_valid'] = ($sig == $ok) ? 'true' : 'false';
+	}
+
+	// Return the keys
+	wrap_kv($keys);
+}
+
+
+/**
+ * Handle a consumer's request to see if the end user is logged in
+ * @global array $known
+ * @global array $profile
+ * @global array $sreg
+ */
+function checkid ( $wait ) {
+	global $known, $profile, $sreg;
+	global $USERNAME;
+
+	// This is a user session
+	user_session();
+
+	// Get the options, use defaults as necessary
+	$return_to = isset($_REQUEST['openid_return_to'])
+		? $_REQUEST['openid_return_to']
+		: error_400('Missing return_to');
+
+	$identity = isset($_REQUEST['openid_identity'])
+			? $_REQUEST['openid_identity']
+			: error_get($return_to, 'Missing identity');
+
+	$assoc_handle = isset($_REQUEST['openid_assoc_handle'])
+			? $_REQUEST['openid_assoc_handle']
+			: null;
+
+	$trust_root = isset($_REQUEST['openid_trust_root'])
+			? $_REQUEST['openid_trust_root']
+			: $return_to;
+
+	$sreg_required = isset($_REQUEST['openid_sreg_required'])
+			? $_REQUEST['openid_sreg.required']
+			: '';
+
+	$sreg_optional = isset($_REQUEST['openid_sreg_optional'])
+			? $_REQUEST['openid_sreg.optional']
+			: '';
+
+	// determine the cancel url
+	$q = strpos($return_to, '?') ? '&' : '?';
+	$cancel_url = $return_to . $q . 'openid.mode=cancel';
+
+	// required and optional make no difference to us
+	$sreg_required .= ',' . $sreg_optional;
+	// do the trust_root analysis
+	if ($trust_root != $return_to) {
+		// the urls are not the same, be sure return decends from trust
+		if (! url_descends($return_to, $trust_root))
+			error_500('Invalid trust_root: "' . $trust_root . '"');
+
+	}
+
+	// transfer the user to the url accept mode if they're paranoid
+	if ($wait == 1 && isset($profile['paranoid']) && $profile['paranoid'] === true && (! isset($_SESSION['accepted_url']) || $_SESSION['accepted_url'] != $trust_root)) {
+		$_SESSION['cancel_accept_url'] = $cancel_url;
+		$_SESSION['post_accept_url'] = $profile['req_url'];
+		$_SESSION['unaccepted_url'] = $trust_root;
+
+		debug('Transferring to acceptance mode.');
+		debug('Cancel URL: ' . $_SESSION['cancel_accept_url']);
+		debug('Post URL: ' . $_SESSION['post_accept_url']);
+
+		$q = strpos($profile['idp_url'], '?') ? '&' : '?';
+		wrap_redirect($profile['idp_url'] . $q . 'openid.mode=accept');
+	}
+	
+	// make sure i am this identifier
+// 	if ($identity != $profile['idp_url']) {
+// 		debug("Invalid identity: $identity");
+// 		debug("IdP URL: " . $profile['idp_url']);
+// 		error_get($return_to, "Invalid identity: '$identity'");
+// 	}
+
+	// begin setting up return keys
+	$keys = array(
+		'mode' => 'id_res'
+	);
+
+	// if the user is not logged in, transfer to the authorization mode
+	if ($USERNAME=='' || $_SESSION['openid_auth'] === false || $USERNAME != $_SESSION['openid_user']) {
+		// users can only be logged in to one url at a time
+		$_SESSION['openid_user'] = null;
+		$_SESSION['auth_url'] = null;
+
+		if ($wait) {
+			unset($_SESSION['uniqid']);
+
+			$_SESSION['cancel_auth_url'] = $cancel_url;
+			$_SESSION['post_auth_url'] = $profile['req_url'];
+
+			debug('Transferring to authorization mode.');
+			debug('Cancel URL: ' . $_SESSION['cancel_auth_url']);
+			debug('Post URL: ' . $_SESSION['post_auth_url']);
+
+			$q = strpos($profile['idp_url'], '?') ? '&' : '?';
+			wrap_redirect($profile['idp_url'] . $q . 'openid.mode=authorize');
+		} else {
+			$keys['user_setup_url'] = $profile['idp_url'];
+		}
+
+	// the user is logged in
+	} else {
+		// remove the refresh URLs if set
+		unset($_SESSION['cancel_auth_url']);
+		unset($_SESSION['post_auth_url']);
+
+		// check the assoc handle
+		list($shared_secret, $expires) = secret($assoc_handle);
+
+		// if I can't verify the assoc_handle, or if it's expired
+		if ($shared_secret == false || (is_numeric($expires) && $expires < time())) {
+			debug("Session expired or missing key: $expires < " . time());
+			if ($assoc_handle != null) {
+				$keys['invalidate_handle'] = $assoc_handle;
+				destroy_assoc_handle($assoc_handle);
+			}
+
+			$lifetime = time() + $profile['lifetime'];
+			list ($assoc_handle, $shared_secret) = new_assoc($lifetime);
+		}
+
+		$keys['identity'] = $profile['idp_url'];
+		$keys['assoc_handle'] = $assoc_handle;
+		$keys['return_to'] = $return_to;
+
+		$fields = array_keys($keys);
+		$tokens = '';
+		foreach ($fields as $key)
+			$tokens .= sprintf("%s:%s\n", $key, $keys[$key]);
+
+		// add sreg keys
+		foreach (explode(',', $sreg_required) as $key) {
+			if (! isset($sreg[$key]))
+				continue;
+			$skey = 'sreg.' . $key;
+
+			$tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]);
+			$keys[$skey] = $sreg[$key];
+			$fields[] = $skey;
+		}
+
+		$keys['signed'] = implode(',', $fields);
+		$keys['sig'] = base64_encode(hmac($shared_secret, $tokens));
+	}
+
+	wrap_keyed_redirect($return_to, $keys);
+}
+
+
+/**
+ * Handle a consumer's request to see if the user is already logged in
+ */
+function checkid_immediate_mode () {
+	if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_immediate')
+		error_500();
+
+	checkid(false);
+}
+
+
+/**
+ * Handle a consumer's request to see if the user is logged in, but be willing
+ * to wait for them to perform a login if they're not
+ */
+function checkid_setup_mode () {
+	if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_setup')
+		error_500();
+
+	checkid(true);
+}
+
+
+/**
+ * Handle errors
+ */
+function error_mode () {
+	isset($_REQUEST['openid_error']) 
+		? wrap_html($_REQUEST['openid_error'])
+		: error_500();
+}
+
+
+/**
+ * Show a user if they are logged in or not
+ * @global array $profile
+ */
+function id_res_mode () {
+	global $profile;
+
+	user_session();
+
+	if ($profile['authorized'])
+		wrap_html('You are logged in as ' . $_SESSION['auth_username']);
+
+	wrap_html('You are not logged in');
+}
+
+
+/**
+ * Allow a user to perform a static login
+ * @global array $profile
+ */
+function login_mode () {
+	global $profile;
+
+	user_session();
+
+	if ($profile['authorized'])
+		id_res_mode();
+
+	$keys = array(
+		'mode' => 'checkid_setup',
+		'identity' => $profile['idp_url'],
+		'return_to' => $profile['idp_url']
+	);
+
+	wrap_keyed_redirect($profile['idp_url'], $keys);
+}
+
+
+/**
+ * Allow a user to perform a static logout
+ * @global array $profile
+ */
+function logout_mode () {
+	global $profile;
+
+	user_session();
+
+	if (! $profile['authorized'])
+		wrap_html('You were not logged in');
+
+	$_SESSION = array();
+	session_destroy();
+	debug('User session destroyed.');
+
+	header('HTTP/1.0 401 Unauthorized');
+	wrap_redirect($profile['idp_url']);
+}
+
+
+/**
+ * The default information screen
+ * @global array $profile
+ */
+function no_mode () {
+	global $USERNAME, $profile;
+	$tmpl = new OC_Template( 'user_openid', 'nomode', 'guest' );
+	if(substr($profile['req_url'],-1,1)!=='/'){//the identity should always end with a /
+		$profile['req_url'].='/';
+	}
+	$tmpl->addHeader('link',array('rel'=>'openid.server', 'href'=>$profile['req_url']));
+	$tmpl->addHeader('link',array('rel'=>'openid.delegate', 'href'=>$profile['idp_url']));
+	$tmpl->assign('user',$USERNAME);
+	$tmpl->printPage();
+}
+
+
+/**
+ * Testing for setup
+ * @global array $profile
+ */
+function test_mode () {
+	global $profile, $p, $g;
+
+	if ($profile['allow_test'] != true)
+		error_403();
+
+	@ini_set('max_execution_time', 180);
+
+	$test_expire = time() + 120;
+	$test_ss_enc = 'W7hvmld2yEYdDb0fHfSkKhQX+PM=';
+	$test_ss = base64_decode($test_ss_enc);
+	$test_token = "alpha:bravo\ncharlie:delta\necho:foxtrot";
+	$test_server_private = '11263846781670293092494395517924811173145217135753406847875706165886322533899689335716152496005807017390233667003995430954419468996805220211293016296351031812246187748601293733816011832462964410766956326501185504714561648498549481477143603650090931135412673422192550825523386522507656442905243832471167330268';
+	$test_client_public = base64_decode('AL63zqI5a5p8HdXZF5hFu8p+P9GOb816HcHuvNOhqrgkKdA3fO4XEzmldlb37nv3+xqMBgWj6gxT7vfuFerEZLBvuWyVvR7IOGZmx0BAByoq3fxYd3Fpe2Coxngs015vK37otmH8e83YyyGo5Qua/NAf13yz1PVuJ5Ctk7E+YdVc');
+
+	$res = array();
+
+	// bcmath
+	$res['bcmath'] = extension_loaded('bcmath')
+		? 'pass' : 'warn - not loaded';
+
+	// gmp
+	if ($profile['allow_gmp']) {
+		$res['gmp'] = extension_loaded('gmp')
+		? 'pass' : 'warn - not loaded';
+	} else {
+		$res['gmp'] = 'pass - n/a';
+	}
+
+	// sys_get_temp_dir
+	$res['logfile'] = is_writable($profile['logfile'])
+		? 'pass' : "warn - log is not writable";
+
+	// session & new_assoc
+	user_session();
+	list($test_assoc, $test_new_ss) = new_assoc($test_expire);
+	$res['session'] = ($test_assoc != session_id())
+		? 'pass' : 'fail';
+
+	// secret
+	@session_unregister('shared_secret');
+	list($check, $check2) = secret($test_assoc);
+	$res['secret'] = ($check == $test_new_ss)
+		? 'pass' : 'fail';
+
+	// expire
+	$res['expire'] = ($check2 <= $test_expire)
+		? 'pass' : 'fail';
+
+	// base64
+	$res['base64'] = (base64_encode($test_ss) == $test_ss_enc)
+		? 'pass' : 'fail';
+
+	// hmac
+	$test_sig = base64_decode('/VXgHvZAOdoz/OTa5+XJXzSGhjs=');
+	$check = hmac($test_ss, $test_token);
+	$res['hmac'] = ($check == $test_sig)
+		? 'pass' : sprintf("fail - '%s'", base64_encode($check));
+
+	if ($profile['use_bigmath']) {
+		// bigmath powmod
+		$test_server_public = '102773334773637418574009974502372885384288396853657336911033649141556441102566075470916498748591002884433213640712303846640842555822818660704173387461364443541327856226098159843042567251113889701110175072389560896826887426539315893475252988846151505416694218615764823146765717947374855806613410142231092856731';
+		$check = bmpowmod($g, $test_server_private, $p);
+		$res['bmpowmod-1'] = ($check == $test_server_public)
+			? 'pass' : sprintf("fail - '%s'", $check);
+
+		// long
+		$test_client_long = '133926731803116519408547886573524294471756220428015419404483437186057383311250738749035616354107518232016420809434801736658109316293127101479053449990587221774635063166689561125137927607200322073086097478667514042144489248048756916881344442393090205172004842481037581607299263456852036730858519133859409417564';
+		$res['long'] = (long($test_client_public) == $test_client_long)
+			? 'pass' : 'fail';
+
+		// bigmath powmod 2
+		$test_client_share = '19333275433742428703546496981182797556056709274486796259858099992516081822015362253491867310832140733686713353304595602619444380387600756677924791671971324290032515367930532292542300647858206600215875069588627551090223949962823532134061941805446571307168890255137575975911397744471376862555181588554632928402';
+		$check = bmpowmod($test_client_long, $test_server_private, $p);
+		$res['bmpowmod-2'] = ($check == $test_client_share)
+			? 'pass' : sprintf("fail - '%s'", $check);
+
+		// bin
+		$test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI=');
+		$check = bin($test_client_share);
+		$res['bin'] = ($check == $test_client_mac_s1)
+			? 'pass' : sprintf("fail - '%s'", base64_encode($check));
+
+	} else {
+		$res['bigmath'] = 'fail - big math functions are not available.';
+	}
+
+	// sha1_20
+	$test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI=');
+	$test_client_mac_s2 = base64_decode('0Mb2t9d/HvAZyuhbARJPYdx3+v4=');
+	$check = sha1_20($test_client_mac_s1);
+	$res['sha1_20'] = ($check == $test_client_mac_s2)
+		? 'pass' : sprintf("fail - '%s'", base64_encode($check));
+
+	// x_or
+	$test_client_mac_s3 = base64_decode('i36ZLYAJ1rYEx1VEHObrS8hgAg0=');
+	$check = x_or($test_client_mac_s2, $test_ss);
+	$res['x_or'] = ($check == $test_client_mac_s3)
+		? 'pass' : sprintf("fail - '%s'", base64_encode($check));
+
+	$out = "<table border=1 cellpadding=4>\n";
+	foreach ($res as $test => $stat) {
+		$code = substr($stat, 0, 4);
+		$color = ($code == 'pass') ? '#9f9'
+			: (($code == 'warn') ? '#ff9' : '#f99');
+		$out .= sprintf("<tr><th>%s</th><td style='background:%s'>%s</td></tr>\n", $test, $color, $stat);
+	}
+	$out .= "</table>";
+
+	wrap_html( $out );
+}
+
+
+// Support functions
+
+/**
+ * Prefix the keys of an array with  'openid.'
+ * @param array $array
+ * @return array
+ */
+function append_openid ($array) {
+	$keys = array_keys($array);
+	$vals = array_values($array);
+
+	$r = array();
+	for ($i=0; $i<sizeof($keys); $i++)
+		$r['openid.' . $keys[$i]] = $vals[$i];
+	return $r;
+}
+
+/**
+ * Create a big math addition function
+ * @param string $l
+ * @param string $r
+ * @return string
+ * @url http://www.icosaedro.it/bigint Inspired by
+ */
+function bmadd($l, $r) {
+	if (function_exists('bcadd'))
+		return bcadd($l, $r);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_add($l, $r));
+
+	$l = strval($l); $r = strval($r);
+	$ll = strlen($l); $rl = strlen($r);
+	if ($ll < $rl) {
+		$l = str_repeat("0", $rl-$ll) . $l;
+		$o = $rl;
+
+	} elseif ( $ll > $rl ) {
+		$r = str_repeat("0", $ll-$rl) . $r;
+		$o = $ll;
+
+	} else {
+		$o = $ll;
+	}
+
+	$v = '';
+	$carry = 0;
+
+	for ($i = $o-1; $i >= 0; $i--) {
+		$d = (int)$l[$i] + (int)$r[$i] + $carry;
+		if ($d <= 9) {
+			$carry = 0;
+
+		} else {
+			$carry = 1;
+			$d -= 10;
+		}
+		$v = (string) $d . $v;
+	}
+
+	if ($carry > 0)
+		$v = "1" . $v;
+
+	return $v;
+}
+
+/**
+ * Create a big math comparison function
+ * @param string $l
+ * @param string $r
+ * @return string
+ */
+function bmcomp($l, $r) {
+	if (function_exists('bccomp'))
+		return bccomp($l, $r);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_cmp($l, $r));
+
+	$l = strval($l); $r = strval($r);
+	$ll = strlen($l); $lr = strlen($r);
+	if ($ll != $lr)
+		return ($ll > $lr) ? 1 : -1;
+
+	return strcmp($l, $r);
+}
+
+/**
+ * Create a big math division function
+ * @param string $l
+ * @param string $r
+ * @param int $z
+ * @return string
+ * @url http://www.icosaedro.it/bigint Inspired by
+ */
+function bmdiv($l, $r, $z = 0) {
+	if (function_exists('bcdiv'))
+		return ($z == 0) ? bcdiv($l, $r) : bcmod($l, $r);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(($z == 0) ? gmp_div_q($l, $r) : gmp_mod($l, $r));
+
+	$l = strval($l); $r = strval($r);
+	$v = '0';
+
+	while (true) {
+		if( bmcomp($l, $r) < 0 )
+			break;
+
+		$delta = strlen($l) - strlen($r);
+		if ($delta >= 1) {
+			$zeroes = str_repeat("0", $delta);
+			$r2 = $r . $zeroes;
+
+			if (strcmp($l, $r2) >= 0) {
+				$v = bmadd($v, "1" . $zeroes);
+				$l = bmsub($l, $r2);
+
+			} else {
+				$zeroes = str_repeat("0", $delta - 1);
+				$v = bmadd($v, "1" . $zeroes);
+				$l = bmsub($l, $r . $zeroes);
+			}
+
+		} else {
+			$l = bmsub($l, $r);
+			$v = bmadd($v, "1");
+		}
+	}
+
+	return ($z == 0) ? $v : $l;
+}
+
+/**
+ * Create a big math multiplication function
+ * @param string $l
+ * @param string $r
+ * @return string
+ * @url http://www.icosaedro.it/bigint Inspired by
+ */
+function bmmul($l, $r) {
+	if (function_exists('bcmul'))
+		return bcmul($l, $r);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_mul($l, $r));
+
+	$l = strval($l); $r = strval($r);
+
+	$v = '0';
+	$z = '';
+
+	for( $i = strlen($r)-1; $i >= 0; $i-- ){
+		$bd = (int) $r[$i];
+		$carry = 0;
+		$p = "";
+		for( $j = strlen($l)-1; $j >= 0; $j-- ){
+			$ad = (int) $l[$j];
+			$pd = $ad * $bd + $carry;
+			if( $pd <= 9 ){
+				$carry = 0;
+			} else {
+				$carry = (int) ($pd / 10);
+				$pd = $pd % 10;
+			}
+			$p = (string) $pd . $p;
+		}
+		if( $carry > 0 )
+			$p = (string) $carry . $p;
+		$p = $p . $z;
+		$z .= "0";
+		$v = bmadd($v, $p);
+	}
+
+	return $v;
+}
+
+/**
+ * Create a big math modulus function
+ * @param string $value
+ * @param string $mod 
+ * @return string
+ */
+function bmmod( $value, $mod ) {
+	if (function_exists('bcmod'))
+		return bcmod($value, $mod);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_mod($value, $mod));
+
+	$r = bmdiv($value, $mod, 1);
+	return $r;
+}
+
+/**
+ * Create a big math power function
+ * @param string $value
+ * @param string $exponent
+ * @return string
+ */
+function bmpow ($value, $exponent) {
+	if (function_exists('bcpow'))
+		return bcpow($value, $exponent);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_pow($value, $exponent));
+
+	$r = '1';
+	while ($exponent) {
+		$r = bmmul($r, $value, 100);
+		$exponent--;
+	}
+	return (string)rtrim($r, '0.');
+}
+
+/**
+ * Create a big math 'powmod' function
+ * @param string $value
+ * @param string $exponent
+ * @param string $mod 
+ * @return string
+ * @url http://php.net/manual/en/function.bcpowmod.php#72704 Borrowed from
+ */
+function bmpowmod ($value, $exponent, $mod) {
+	if (function_exists('bcpowmod'))
+		return bcpowmod($value, $exponent, $mod);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_powm($value, $exponent, $mod));
+
+	$r = '';
+	while ($exponent != '0') {
+		$t = bmmod($exponent, '4096');
+		$r = substr("000000000000" . decbin(intval($t)), -12) . $r;
+		$exponent = bmdiv($exponent, '4096');
+	}
+
+	$r = preg_replace("!^0+!","",$r);
+
+	if ($r == '')
+		$r = '0';
+	$value = bmmod($value, $mod);
+	$erb = strrev($r);
+	$q = '1';
+	$a[0] = $value;
+
+	for ($i = 1; $i < strlen($erb); $i++) {
+		$a[$i] = bmmod( bmmul($a[$i-1], $a[$i-1]), $mod );
+	}
+
+	for ($i = 0; $i < strlen($erb); $i++) {
+		if ($erb[$i] == "1") {
+			$q = bmmod( bmmul($q, $a[$i]), $mod );
+		}
+	}
+
+	return($q);
+}
+
+/**
+ * Create a big math subtraction function
+ * @param string $l
+ * @param string $r
+ * @return string
+ * @url http://www.icosaedro.it/bigint Inspired by
+ */
+function bmsub($l, $r) {
+	if (function_exists('bcsub'))
+		return bcsub($l, $r);
+
+	global $profile;
+	if ($profile['use_gmp'])
+		return gmp_strval(gmp_sub($l, $r));
+
+
+	$l = strval($l); $r = strval($r);
+	$ll = strlen($l); $rl = strlen($r);
+
+	if ($ll < $rl) {
+		$l = str_repeat("0", $rl-$ll) . $l;
+		$o = $rl;
+	} elseif ( $ll > $rl ) {
+		$r = str_repeat("0", $ll-$rl) . (string)$r;
+		$o = $ll;
+	} else {
+		$o = $ll;
+	}
+
+	if (strcmp($l, $r) >= 0) {
+		$sign = '';
+	} else {
+		$x = $l; $l = $r; $r = $x;
+		$sign = '-';
+	}
+
+	$v = '';
+	$carry = 0;
+
+	for ($i = $o-1; $i >= 0; $i--) {
+		$d = ($l[$i] - $r[$i]) - $carry;
+		if ($d < 0) {
+			$carry = 1;
+			$d += 10;
+		} else {
+			$carry = 0;
+		}
+		$v = (string) $d . $v;
+	}
+
+	return $sign . ltrim($v, '0');
+}
+
+
+/**
+ * Get a binary value
+ * @param integer $n
+ * @return string
+ * @url http://openidenabled.com Borrowed from PHP-OpenID
+ */
+function bin ($n) {
+	$bytes = array();
+	while (bmcomp($n, 0) > 0) {
+		array_unshift($bytes, bmmod($n, 256));
+		$n = bmdiv($n, bmpow(2,8));
+	}
+
+	if ($bytes && ($bytes[0] > 127))
+		array_unshift($bytes, 0);
+
+	$b = '';
+	foreach ($bytes as $byte)
+		$b .= pack('C', $byte);
+
+	return $b;
+}
+
+
+/**
+ * Debug logging
+ * @param mixed $x
+ * @param string $m 
+ */
+function debug ($x, $m = null) {
+	global $profile;
+
+	if (! isset($profile['debug']) || $profile['debug'] === false)
+		return true;
+
+	if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile']))
+		error_500('Cannot write to debug log: ' . $profile['logfile']);
+
+	if (is_array($x)) {
+		ob_start();
+		print_r($x);
+		$x = $m . ($m != null ? "\n" : '') . ob_get_clean();
+
+	} else {
+		$x .= "\n";
+	}
+
+	error_log($x . "\n", 3, $profile['logfile']);
+}
+
+
+/**
+ * Destroy a consumer's assoc handle
+ * @param string $id
+ */
+function destroy_assoc_handle ( $id ) {
+	debug("Destroying session: $id");
+
+	$sid = session_id();
+	session_write_close();
+
+	session_id($id);
+	session_start();
+	session_destroy();
+
+	session_id($sid);
+	session_start();
+}
+
+
+/**
+ * Return an error message to the user
+ * @param string $message
+ */
+function error_400 ( $message = 'Bad Request' ) {
+	header("HTTP/1.1 400 Bad Request");
+	wrap_html($message);
+}
+
+
+/**
+ * Return an error message to the user
+ * @param string $message
+ */
+function error_403 ( $message = 'Forbidden' ) {
+	header("HTTP/1.1 403 Forbidden");
+	wrap_html($message);
+}
+
+
+/**
+ * Return an error message to the user
+ * @param string $message
+ */
+function error_500 ( $message = 'Internal Server Error' ) {
+	header("HTTP/1.1 500 Internal Server Error");
+	wrap_html($message);
+}
+
+
+/**
+ * Return an error message to the consumer
+ * @param string $message
+ */
+function error_get ( $url, $message = 'Bad Request') {
+	wrap_keyed_redirect($url, array('mode' => 'error', 'error' => $message));
+}
+
+
+/**
+ * Return an error message to the consumer
+ * @param string $message
+ */
+function error_post ( $message = 'Bad Request' ) {
+	header("HTTP/1.1 400 Bad Request");
+	echo ('error:' . $message);
+	exit(0);
+}
+
+
+/**
+ * Do an HMAC
+ * @param string $key
+ * @param string $data
+ * @param string $hash
+ * @return string
+ * @url http://php.net/manual/en/function.sha1.php#39492 Borrowed from
+ */
+function hmac($key, $data, $hash = 'sha1_20') {
+	$blocksize=64;
+
+	if (strlen($key) > $blocksize)
+		$key = $hash($key);
+
+	$key = str_pad($key, $blocksize,chr(0x00));
+	$ipad = str_repeat(chr(0x36),$blocksize);
+	$opad = str_repeat(chr(0x5c),$blocksize);
+
+	$h1 = $hash(($key ^ $ipad) . $data);
+	$hmac = $hash(($key ^ $opad) . $h1);
+	return $hmac;
+}
+
+
+if (! function_exists('http_build_query')) {
+/**
+ * Create function if missing
+ * @param array $array
+ * @return string
+ */
+function http_build_query ($array) {
+	$r = array();
+	foreach ($array as $key => $val)
+		$r[] = sprintf('%s=%s', urlencode($key), urlencode($val));
+	return implode('&', $r);
+}}
+
+
+/**
+ * Turn a binary back into a long
+ * @param string $b
+ * @return integer
+ * @url http://openidenabled.com Borrowed from PHP-OpenID
+ */
+function long($b) {
+	$bytes = array_merge(unpack('C*', $b));
+	$n = 0;
+	foreach ($bytes as $byte) {
+		$n = bmmul($n, bmpow(2,8));
+		$n = bmadd($n, $byte);
+	}
+	return $n;
+}
+
+
+/**
+ * Create a new consumer association
+ * @param integer $expiration
+ * @return array
+ */
+function new_assoc ( $expiration ) {
+	if (isset($_SESSION) && is_array($_SESSION)) {
+		$sid = session_id();
+		$dat = session_encode();
+		session_write_close();
+	}
+
+	session_start();
+	session_regenerate_id('false');
+
+	$id = session_id();
+	$shared_secret = new_secret();
+	debug('Started new assoc session: ' . $id);
+
+	$_SESSION = array();
+	$_SESSION['expiration'] = $expiration;
+	$_SESSION['shared_secret'] = base64_encode($shared_secret);
+
+	session_write_close();
+
+	if (isset($sid)) {
+		session_id($sid);
+		session_start();
+		$_SESSION = array();
+		session_decode($dat);
+	}
+
+	return array($id, $shared_secret);
+}
+
+
+/**
+ * Create a new shared secret
+ * @return string
+ */
+function new_secret () {
+	$r = '';
+	for($i=0; $i<20; $i++)
+		$r .= chr(mt_rand(0, 255));
+
+	debug("Generated new key: hash = '" . md5($r) . "', length = '" . strlen($r) . "'");
+	return $r;
+}
+
+
+/**
+ * Random number generation
+ * @param integer max
+ * @return integer
+ */
+function random ( $max ) {
+	if (strlen($max) < 4)
+		return mt_rand(1, $max - 1);
+
+	$r = '';
+	for($i=1; $i<strlen($max) - 1; $i++)
+		$r .= mt_rand(0,9);
+	$r .= mt_rand(1,9);
+
+	return $r;
+}
+
+/**
+ * Get the shared secret and expiration time for the specified assoc_handle
+ * @param string $handle assoc_handle to look up
+ * @return array (shared_secret, expiration_time)
+ */
+function secret ( $handle ) {
+	if (! preg_match('/^\w+$/', $handle))
+		return array(false, 0);
+
+	if (isset($_SESSION) && is_array($_SESSION)) {
+		$sid = session_id();
+		$dat = session_encode();
+		session_write_close();
+	}
+
+	session_id($handle);
+	session_start();
+	debug('Started session to acquire key: ' . session_id());
+
+	$secret = isset($_SESSION['shared_secret'])
+		? base64_decode($_SESSION['shared_secret'])
+		: false;
+
+	$expiration = isset($_SESSION['expiration'])
+		? $_SESSION['expiration']
+		: null;
+
+	session_write_close();
+
+	if (isset($sid)) {
+		session_id($sid);
+		session_start();
+		$_SESSION = array();
+		session_decode($dat);
+	}
+
+	debug("Found key: hash = '" . md5($secret) . "', length = '" . strlen($secret) . "', expiration = '$expiration'");
+	return array($secret, $expiration);
+}
+
+
+/**
+ * Do an internal self check
+ * @global array $profile
+ * @global array $sreg
+ */
+function self_check () {
+	global $profile, $sreg;
+
+// 	if (! isset($profile) || ! is_array($profile))
+// 		error_500('No configuration found, you shouldn\'t access this file directly.');
+
+	if (version_compare(phpversion(), '4.2.0', 'lt'))
+		error_500('The required minimum version of PHP is 4.2.0, you are running ' . phpversion());
+
+	$extension_r = array('session', 'pcre');
+	foreach ($extension_r as $x) {
+		if (! extension_loaded($x))
+			@dl($x);
+		if (! extension_loaded($x))
+			error_500("Required extension '$x' is missing.");
+	}
+
+// 	$extension_b = array('suhosin');
+// 	foreach ($extension_b as $x) {
+// 		if (extension_loaded($x) &! $profile["allow_$x"])
+// 			error_500("phpMyID is not compatible with '$x'");
+// 	}
+// 
+// 	$keys = array('auth_username', 'auth_password');
+// 	foreach ($keys as $key) {
+// 		if (! array_key_exists($key, $profile))
+// 			error_500("'$key' is missing from your profile.");
+// 	}
+
+	if (! isset($sreg) || ! is_array($sreg))
+		$sreg = array();
+}
+
+
+/**
+ * Do SHA1 20 byte encryption
+ * @param string $v
+ * @return string
+ * @url http://openidenabled.com Borrowed from PHP-OpenID
+ */
+function sha1_20 ($v) {
+	if (version_compare(phpversion(), '5.0.0', 'ge'))
+		return sha1($v, true);
+
+	$hex = sha1($v);
+	$r = '';
+	for ($i = 0; $i < 40; $i += 2) {
+		$hexcode = substr($hex, $i, 2);
+		$charcode = base_convert($hexcode, 16, 10);
+		$r .= chr($charcode);
+	}
+	return $r;
+}
+
+
+/**
+ * Look for the point of differentiation in two strings
+ * @param string $a
+ * @param string $b
+ * @return int
+ */
+function str_diff_at ($a, $b) {
+	if ($a == $b)
+		return -1;
+	$n = min(strlen($a), strlen($b));
+	for ($i = 0; $i < $n; $i++)
+		if ($a[$i] != $b[$i])
+			return $i;
+	return $n;
+}
+
+
+if (! function_exists('sys_get_temp_dir') && ini_get('open_basedir') == false) {
+/**
+ * Create function if missing
+ * @return string
+ */
+function sys_get_temp_dir () {
+	$keys = array('TMP', 'TMPDIR', 'TEMP');
+	foreach ($keys as $key) {
+		if (isset($_ENV[$key]) && is_dir($_ENV[$key]) && is_writable($_ENV[$key]))
+			return realpath($_ENV[$key]);
+	}
+
+	$tmp = tempnam(false, null);
+	if (file_exists($tmp)) {
+		$dir = realpath(dirname($tmp));
+		unlink($tmp);
+		return realpath($dir);
+	}
+
+	return realpath(dirname(__FILE__));
+}} elseif (! function_exists('sys_get_temp_dir')) {
+function sys_get_temp_dir () {
+	return realpath(dirname(__FILE__));
+}}
+
+
+/**
+ * Determine if a child URL actually decends from the parent, and that the
+ * parent is a good URL.
+ * THIS IS EXPERIMENTAL
+ * @param string $parent
+ * @param string $child
+ * @return bool
+ */
+function url_descends ( $child, $parent ) {
+	if ($child == $parent)
+		return true;
+
+	$keys = array();
+	$parts = array();
+
+	$req = array('scheme', 'host');
+	$bad = array('fragment', 'pass', 'user');
+
+	foreach (array('parent', 'child') as $name) {
+		$parts[$name] = @parse_url($$name);
+		if ($parts[$name] === false)
+			return false;
+
+		$keys[$name] = array_keys($parts[$name]);
+
+		if (array_intersect($keys[$name], $req) != $req)
+			return false;
+
+		if (array_intersect($keys[$name], $bad) != array())
+			return false;
+
+		if (! preg_match('/^https?$/i', strtolower($parts[$name]['scheme'])))
+			return false;
+
+		if (! array_key_exists('port', $parts[$name]))
+			$parts[$name]['port'] = (strtolower($parts[$name]['scheme']) == 'https') ? 443 : 80;
+
+		if (! array_key_exists('path', $parts[$name]))
+			$parts[$name]['path'] = '/';
+	}
+
+	// port and scheme must match
+	if ($parts['parent']['scheme'] != $parts['child']['scheme'] ||
+	    $parts['parent']['port'] != $parts['child']['port'])
+		return false;
+
+	// compare the hosts by reversing the strings
+	$cr_host = strtolower(strrev($parts['child']['host']));
+	$pr_host = strtolower(strrev($parts['parent']['host']));
+
+	$break = str_diff_at($cr_host, $pr_host);
+	if ($break >= 0 && ($pr_host[$break] != '*' || substr_count(substr($pr_host, 0, $break), '.') < 2))
+		return false;
+
+	// now compare the paths
+	$break = str_diff_at($parts['child']['path'], $parts['parent']['path']);
+	if ($break >= 0
+	   && ($break < strlen($parts['parent']['path']) && $parts['parent']['path'][$break] != '*')
+	   || ($break > strlen($parts['child']['path'])))
+		return false;
+
+	return true;
+}
+
+
+/**
+ * Create a user session
+ * @global array $profile
+ * @global array $proto
+ */
+function user_session () {
+	global $proto, $profile;
+
+	session_name('phpMyID_Server');
+	@session_start();
+
+	$profile['authorized'] = (isset($_SESSION['auth_username'])
+			    && $_SESSION['auth_username'] == $profile['auth_username'])
+			? true
+			: false;
+
+	debug('Started user session: ' . session_id() . ' Auth? ' . $profile['authorized']);
+}
+
+
+/**
+ * Return HTML
+ * @global string $charset
+ * @param string $message
+ */
+function wrap_html ( $message ) {
+	global $charset, $profile;
+	header('Content-Type: text/html; charset=' . $charset);
+	$html= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<title>phpMyID</title>
+<link rel="openid.server" href="' . $profile['req_url'] . '" />
+<link rel="openid.delegate" href="' . $profile['idp_url'] . '" />
+' . implode("\n", $profile['opt_headers']) . '
+<meta name="charset" content="' . $charset . '" />
+<meta name="robots" content="noindex,nofollow" />
+</head>
+<body>
+<p>' . $message . '</p>
+</body>
+</html>
+';
+	error_log($html);
+	echo $html;
+	exit(0);
+}
+
+
+/**
+ * Return a key-value pair in plain text
+ * @global string $charset
+ * @param array $keys
+ */
+function wrap_kv ( $keys ) {
+	global $charset;
+
+	debug($keys, 'Wrapped key/vals');
+	header('Content-Type: text/plain; charset=' . $charset);
+	foreach ($keys as $key => $value)
+		printf("%s:%s\n", $key, $value);
+
+	exit(0);
+}
+
+
+/**
+ * Redirect, with OpenID keys
+ * @param string $url
+ * @param array @keys
+ */
+function wrap_keyed_redirect ($url, $keys) {
+	$keys = append_openid($keys);
+	debug($keys, 'Location keys');
+
+	$q = strpos($url, '?') ? '&' : '?';
+	wrap_redirect($url . $q . http_build_query($keys));
+}
+
+
+/**
+ * Redirect the browser
+ * @global string $charset
+ * @param string $url
+ */
+function wrap_redirect ($url) {
+	header('HTTP/1.1 302 Found');
+	header('Location: ' . $url);
+	debug('Location: ' . $url);
+	exit(0);
+}
+
+/**
+ * Return an HTML refresh
+ * @global string $charset
+ * @param string $url
+ */
+function wrap_refresh ($url) {
+	global $charset;
+
+	header('Content-Type: text/html; charset=' . $charset);
+	echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+<title>phpMyID</title>
+<meta http-equiv="refresh" content="0;url=' . $url . '">
+</head>
+<body>
+<p>Redirecting to <a href="' . $url . '">' . $url . '</a></p>
+</body>
+</html>
+';
+
+	debug('Refresh: ' . $url);
+	exit(0);
+}
+
+
+/**
+ * Implement binary x_or
+ * @param string $a
+ * @param string $b
+ * @return string
+ */
+function x_or ($a, $b) {
+	$r = "";
+
+	for ($i = 0; $i < strlen($b); $i++)
+		$r .= $a[$i] ^ $b[$i];
+	debug("Xor size: " . strlen($r));
+	return $r;
+}
+
+
+
+/*
+ * App Initialization
+ */
+// Determine the charset to use
+$GLOBALS['charset'] = 'iso-8859-1';
+
+// Set the internal encoding
+if (function_exists('mb_internal_encoding'))
+	mb_internal_encoding($charset);
+
+// Avoid problems with non-default arg_separator.output settings
+// Credit for this goes to user 'prelog' on the forums
+ini_set('arg_separator.output', '&');
+
+// Do a check to be sure everything is set up correctly
+self_check();
+
+
+/**
+ * Determine the HTTP request port
+ * @name $port
+ * @global integer $GLOBALS['port']
+ */
+$GLOBALS['port'] = ((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' && $_SERVER['SERVER_PORT'] == 443)
+	  || $_SERVER['SERVER_PORT'] == 80)
+		? ''
+		: ':' . $_SERVER['SERVER_PORT'];
+
+
+/**
+ * Determine the HTTP request protocol
+ * @name $proto
+ * @global string $GLOBALS['proto']
+ */
+$GLOBALS['proto'] = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
+
+// Set the authorization state - DO NOT OVERRIDE
+$profile['authorized'] = false;
+
+global $IDENTITY;
+global $USERNAME;
+
+// Set a default IDP URL
+if (! array_key_exists('idp_url', $profile))
+	$profile['idp_url'] = $IDENTITY;
+
+//Determine the requested URL - DO NOT OVERRIDE
+$profile['req_url'] = sprintf("%s://%s%s",
+		      $proto,
+		      $_SERVER['HTTP_HOST'],
+// 		      $port,//host  already includes the path
+		      $_SERVER["REQUEST_URI"]);
+
+// $fullId='user.php/'.$USERNAME.'/';
+// $incompleteId='user.php/';
+
+// if(!strpos($profile['req_url'],$fullId)){
+// 	$profile['req_url']=str_replace($incompleteId,$fullId,$profile['req_url']);
+// }
+
+// error_log('inc id: '.$fullId);
+// error_log('req url: '.$profile['req_url']);
+
+// Set the default allowance for testing
+if (! array_key_exists('allow_test', $profile))
+	$profile['allow_test'] = false;
+
+// Set the default allowance for gmp
+if (! array_key_exists('allow_gmp', $profile))
+	$profile['allow_gmp'] = false;
+
+// Set the default force bigmath - BAD IDEA to override this
+if (! array_key_exists('force_bigmath', $profile))
+	$profile['force_bigmath'] = false;
+
+// Determine if GMP is usable
+$profile['use_gmp'] = (extension_loaded('gmp') && $profile['allow_gmp']) ? true : false;
+
+// Determine if I can perform big math functions
+$profile['use_bigmath'] = (extension_loaded('bcmath') || $profile['use_gmp'] || $profile['force_bigmath']) ? true : false;
+
+// Set a default authentication domain
+if (! array_key_exists('auth_domain', $profile))
+	$profile['auth_domain'] = $profile['req_url'] . ' ' . $profile['idp_url'];
+
+// Set a default authentication realm
+if (! array_key_exists('auth_realm', $profile))
+	$profile['auth_realm'] = 'ownCloud';
+
+// Determine the realm for digest authentication - DO NOT OVERRIDE
+$profile['php_realm'] = $profile['auth_realm'] . (ini_get('safe_mode') ? '-' . getmyuid() : '');
+
+// Set a default lifetime - the lesser of GC and cache time
+if (! array_key_exists('lifetime', $profile)) {
+	$sce = session_cache_expire() * 60;
+	$gcm = ini_get('session.gc_maxlifetime');
+	$profile['lifetime'] = $sce < $gcm ? $sce : $gcm;
+}
+
+// Set a default log file
+if (! array_key_exists('logfile', $profile))
+	$profile['logfile'] = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $profile['auth_realm'] . '.debug.log';
+
+
+/*
+ * Optional Initialization
+ */
+// Setup optional headers
+$profile['opt_headers'] = array();
+
+// Determine if I should add microid stuff
+if (array_key_exists('microid', $profile)) {
+	$hash = sha1($profile['idp_url']);
+	$values = is_array($profile['microid']) ? $profile['microid'] : array($profile['microid']);
+
+	foreach ($values as $microid) {
+		preg_match('/^([a-z]+)/i', $microid, $mtx);
+		$profile['opt_headers'][] = sprintf('<meta name="microid" content="%s+%s:sha1:%s" />', $mtx[1], $proto, sha1(sha1($microid) . $hash));
+	}
+}
+
+// Determine if I should add pavatar stuff
+if (array_key_exists('pavatar', $profile))
+	$profile['opt_headers'][] = sprintf('<link rel="pavatar" href="%s" />', $profile['pavatar']);
+
+
+/*
+ * Do it
+ */
+// Decide which runmode, based on user request or default
+$run_mode = (isset($_REQUEST['openid_mode'])
+	  && in_array($_REQUEST['openid_mode'], $known['openid_modes']))
+	? $_REQUEST['openid_mode']
+	: 'no';
+
+// Run in the determined runmode
+debug("Run mode: $run_mode at: " . time());
+debug($_REQUEST, 'Request params');
+call_user_func($run_mode . '_mode');
+?>
diff --git a/apps/user_openid/settings.php b/apps/user_openid/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..d85eaebb5ee7e688bc626a623fddbda020325424
--- /dev/null
+++ b/apps/user_openid/settings.php
@@ -0,0 +1,10 @@
+<?php
+
+$tmpl = new OC_Template( 'user_openid', 'settings');
+$identity=OC_Preferences::getValue(OC_User::getUser(),'user_openid','identity','');
+$tmpl->assign('identity',$identity);
+
+OC_Util::addScript('user_openid','settings');
+
+return $tmpl->fetchPage();
+?>
\ No newline at end of file
diff --git a/apps/user_openid/templates/nomode.php b/apps/user_openid/templates/nomode.php
new file mode 100644
index 0000000000000000000000000000000000000000..f85d28cdc9bd22d0904476827daa5bafc3a77228
--- /dev/null
+++ b/apps/user_openid/templates/nomode.php
@@ -0,0 +1,28 @@
+<?php
+
+global $profile;
+
+?>
+
+<div id="login">
+	<img src="<?php echo image_path("", "owncloud-logo-medium-white.png"); ?>" alt="ownCloud" />
+	<ul>
+		<li class='error'>
+			<div id="setup_form">
+				<p><?php echo($l->t('This is an OpenID server endpoint. For more information, see '));?><a href='http://openid.net/' title='openid.net'>http://openid.net/</a></p>
+				<?php if($_['user']):?>
+					<p><?php echo($l->t('Identity: <b>').$profile['idp_url']); ?></b></p>
+					<p><?php echo($l->t('Realm: <b>').$profile['php_realm']); ?></b></p>
+					<p><?php echo($l->t('User: <b>').$_['user']); ?></b>
+					<p><a href="<?php echo($profile['idp_url']); ?>?openid.mode=login"><?php echo($l->t('Login')); ?></a>
+					<?php if($profile['allow_test'] === true): ?>
+						<a href="<?php echo($profile['idp_url']); ?>?openid.mode=test">Test</a>
+					<?php endif; ?>
+				<?php else: ?>
+					<p><?php echo($l->t('Error: <b>No user Selected')); ?></p>
+				<?php endif; ?>
+			</div>
+		</li>
+	</ul>
+</div>
+
diff --git a/apps/user_openid/templates/settings.php b/apps/user_openid/templates/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..74f40fa3c101f1ed80716f755ef3a6729549797a
--- /dev/null
+++ b/apps/user_openid/templates/settings.php
@@ -0,0 +1,8 @@
+<form id="openidform">
+	<fieldset class="personalblock">
+		<label for="openid"><strong>OpenID</strong></label>
+		<input type="text" id="openid" value="<?php echo ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].OC::$WEBROOT.'/?'; echo OC_User::getUser(); ?>" title="<?php echo $l->t('you can authenticate to other sites with this address');?>" />
+		<label for="identity"><?php echo $l->t('Authorized');?></label>
+		<input type="text" name="identity" id="identity" value="<?php echo $_['identity']; ?>" placeholder="OpenID <?php echo $l->t('provider');?>" title="<?php echo $l->t('Wordpress, Identi.ca, Launchpad, &hellip;');?>" />
+	</fieldset>
+</form>
diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php
new file mode 100644
index 0000000000000000000000000000000000000000..3743d232b6d482066e1f70c9f3a6e054276cee88
--- /dev/null
+++ b/apps/user_openid/user.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2011 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+$USERNAME=substr($_SERVER["REQUEST_URI"],strpos($_SERVER["REQUEST_URI"],'.php/')+5);
+if(strpos($USERNAME,'?')!==false){
+	$USERNAME=substr($USERNAME,0,strpos($USERNAME,'?'));
+}
+if(substr($USERNAME,-1,1)=='/'){//openid sometimes add slashes to the username
+	$USERNAME=substr($USERNAME,0,-1);
+}
+
+
+if($USERNAME=='' and isset($_SERVER['PHP_AUTH_USER'])){
+	$USERNAME=$_SERVER['PHP_AUTH_USER'];
+}
+
+$RUNTIME_NOAPPS=true;
+$RUNTIME_NOAPPS=false;
+require_once '../../lib/base.php';
+
+if(!OC_User::userExists($USERNAME)){
+	error_log($USERNAME.' doesn\'t exist');
+	$USERNAME='';
+}
+global $WEBROOT;
+$IDENTITY=((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].$WEBROOT.'/apps/user_openid/user.php/'.$USERNAME;
+
+require_once 'phpmyid.php';
+
+
+?>
\ No newline at end of file
diff --git a/apps/user_openid/user_openid.php b/apps/user_openid/user_openid.php
new file mode 100644
index 0000000000000000000000000000000000000000..d9af94dcafa70dd006cea6b9a0cbbdb8d388bc97
--- /dev/null
+++ b/apps/user_openid/user_openid.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Robin Appelman
+ * @copyright 2011 Robin Appelman icewind1991gmailc.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/>.
+ *
+ */
+
+require_once('class.openid.v3.php');
+
+/**
+ * Class for user management in a SQL Database (e.g. MySQL, SQLite)
+ */
+class OC_USER_OPENID extends OC_User_Backend {
+	/**
+	 * @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 ){
+		global $WEBROOT;
+		// Get identity from user and redirect browser to OpenID Server
+		$openid = new SimpleOpenID;
+		$openid->SetIdentity($uid);
+		$openid->SetTrustRoot('http://' . $_SERVER["HTTP_HOST"]);
+		if ($openid->GetOpenIDServer()){
+			$openid->SetApprovedURL('http://' . $_SERVER["HTTP_HOST"] . $WEBROOT);      // Send Response from OpenID server to this script
+			$openid->Redirect();     // This will redirect user to OpenID Server
+			exit;
+		}else{
+			return false;
+		}
+		exit;
+	}
+	
+	/**
+	 * find the user that can be authenticated with an openid identity
+	 */
+	public static function findUserForIdentity($identity){
+		$query=OC_DB::prepare('SELECT userid FROM *PREFIX*preferences WHERE appid=? AND configkey=? AND configvalue=?');
+		$result=$query->execute(array('user_openid','identity',$identity))->fetchAll();
+		if(count($result)>0){
+			return $result[0]['userid'];
+		}else{
+			return false;
+		}
+	}
+}
+
+
+
+?>
\ No newline at end of file
diff --git a/config/config.sample.php b/config/config.sample.php
new file mode 100644
index 0000000000000000000000000000000000000000..5575340bc1be27455f72fcfdbcc8836dbd6bbc37
--- /dev/null
+++ b/config/config.sample.php
@@ -0,0 +1,15 @@
+<?php
+
+$CONFIG = array(
+"installed" => false,
+"dbtype" => "sqlite",
+"dbname" => "owncloud",
+"dbuser" => "",
+"dbpassword" => "",
+"dbhost" => "",
+"dbtableprefix" => "",
+"forcessl" => false,
+"enablebackup" => false,
+// "datadirectory" => ""
+);
+?>
diff --git a/core/ajax/grouplist.php b/core/ajax/grouplist.php
new file mode 100644
index 0000000000000000000000000000000000000000..d0d10f7a84e01dd65f1607b5a5cd610bb6e3bd40
--- /dev/null
+++ b/core/ajax/grouplist.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+* ownCloud - ajax group list
+*
+* @author Hans Bakker
+* @copyright 2011 hansmbakker+kde@gmail.com
+*
+* 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/>.
+*
+*/
+
+$RUNTIME_NOAPPS = TRUE; //no apps, yet
+require_once('../../lib/base.php');
+
+if(!OC_User::isLoggedIn()){
+	if(!isset($_SERVER['PHP_AUTH_USER'])){
+		header('WWW-Authenticate: Basic realm="ownCloud Server"');
+		header('HTTP/1.0 401 Unauthorized');
+		echo 'Valid credentials must be supplied';
+		exit();
+	} else {
+		if(!OC_User::checkPassword($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
+			exit();
+		}
+	}
+}
+
+$groups = array();
+
+foreach( OC_Group::getGroups() as $i ){
+       	// Do some more work here soon
+        $groups[] = array( "groupname" => $i );
+}
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+echo json_encode($groups);
+
+?>
diff --git a/core/ajax/translations.php b/core/ajax/translations.php
new file mode 100644
index 0000000000000000000000000000000000000000..adaf7dcb75800e5d604c4a7f66eef0bf253f3c54
--- /dev/null
+++ b/core/ajax/translations.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Jakob Sack
+* @copyright 2011 Jakob Sack kde@jakobsack.de
+*
+* 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/>.
+*
+*/
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+$app = $_POST["app"];
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+$l = new OC_L10N( $app );
+
+echo json_encode( array( 'status' => 'success', 'data' => $l->getTranslations()));
+?>
diff --git a/core/ajax/userlist.php b/core/ajax/userlist.php
new file mode 100644
index 0000000000000000000000000000000000000000..0485f514550a3aaa9cb686eef0214ecdb8e8e725
--- /dev/null
+++ b/core/ajax/userlist.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+* ownCloud - ajax user list
+*
+* @author Hans Bakker
+* @copyright 2011 hansmbakker+kde@gmail.com
+*
+* 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/>.
+*
+*/
+
+$RUNTIME_NOAPPS = TRUE; //no apps, yet
+require_once('../../lib/base.php');
+
+if(!OC_User::isLoggedIn()){
+        if(!isset($_SERVER['PHP_AUTH_USER'])){
+                header('WWW-Authenticate: Basic realm="ownCloud Server"');
+                header('HTTP/1.0 401 Unauthorized');
+                echo 'Valid credentials must be supplied';
+                exit();
+        } else {
+                if(!OC_User::checkPassword($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
+                        exit();
+                }
+        }
+}
+
+$users = array();
+
+foreach( OC_User::getUsers() as $i ){
+       	$users[] = array( "username" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ));
+}
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+echo json_encode($users);
+
+?>
diff --git a/core/ajax/validateuser.php b/core/ajax/validateuser.php
new file mode 100644
index 0000000000000000000000000000000000000000..032948fc331102fac4e7519b227db16bb4b390c1
--- /dev/null
+++ b/core/ajax/validateuser.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Hans Bakker
+* @copyright 2011 Hans Bakker hansmbakker+kde@gmail.com
+*
+* 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/>.
+*
+*/
+
+$RUNTIME_NOAPPS = TRUE; //no apps, yet
+require_once('../../lib/base.php');
+
+if(!isset($_SERVER['PHP_AUTH_USER'])){
+        header('WWW-Authenticate: Basic realm="ownCloud Server"');
+        header('HTTP/1.0 401 Unauthorized');
+        echo 'Valid credentials must be supplied';
+        exit();
+} else {
+	header("Content-Type: application/jsonrequest");
+        if(OC_User::checkPassword($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
+		echo json_encode(array("username" => $_SERVER["PHP_AUTH_USER"], "user_valid" => "true"));
+	} else {
+	        echo json_encode(array("username" => $_SERVER["PHP_AUTH_USER"], "user_valid" => "false"));
+	}
+}
+
+?>
diff --git a/core/css/images/no.png b/core/css/images/no.png
new file mode 100644
index 0000000000000000000000000000000000000000..fdc0f42125585d0c99e64deddc1e1aaade72a873
Binary files /dev/null and b/core/css/images/no.png differ
diff --git a/core/css/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png b/core/css/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png
new file mode 100644
index 0000000000000000000000000000000000000000..6348115e6be09c044cd20ead0202c3ab5b2f9c19
Binary files /dev/null and b/core/css/images/ui-bg_diagonals-thick_90_eeeeee_40x40.png differ
diff --git a/core/css/images/ui-bg_flat_15_cd0a0a_40x100.png b/core/css/images/ui-bg_flat_15_cd0a0a_40x100.png
new file mode 100644
index 0000000000000000000000000000000000000000..7680b5437cf820615e472dc4f9a18fd0ba8b7881
Binary files /dev/null and b/core/css/images/ui-bg_flat_15_cd0a0a_40x100.png differ
diff --git a/core/css/images/ui-bg_glass_100_e4f1fb_1x400.png b/core/css/images/ui-bg_glass_100_e4f1fb_1x400.png
new file mode 100644
index 0000000000000000000000000000000000000000..705a32ea35d4d0012bfd3b6a3ffe17a2aaf21bb5
Binary files /dev/null and b/core/css/images/ui-bg_glass_100_e4f1fb_1x400.png differ
diff --git a/core/css/images/ui-bg_glass_50_3baae3_1x400.png b/core/css/images/ui-bg_glass_50_3baae3_1x400.png
new file mode 100644
index 0000000000000000000000000000000000000000..baabca6baaff94ade4ecd5ddad28e35f52ea3af7
Binary files /dev/null and b/core/css/images/ui-bg_glass_50_3baae3_1x400.png differ
diff --git a/core/css/images/ui-bg_glass_80_d7ebf9_1x400.png b/core/css/images/ui-bg_glass_80_d7ebf9_1x400.png
new file mode 100644
index 0000000000000000000000000000000000000000..d9387e9507ef5b48483315affdcf7e69a6a5cbd1
Binary files /dev/null and b/core/css/images/ui-bg_glass_80_d7ebf9_1x400.png differ
diff --git a/core/css/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png b/core/css/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png
new file mode 100644
index 0000000000000000000000000000000000000000..28b566c2c29cc0f849995be62c10d7c292697803
Binary files /dev/null and b/core/css/images/ui-bg_highlight-hard_100_f2f5f7_1x100.png differ
diff --git a/core/css/images/ui-bg_highlight-hard_70_000000_1x100.png b/core/css/images/ui-bg_highlight-hard_70_000000_1x100.png
new file mode 100644
index 0000000000000000000000000000000000000000..d58829780430e138704e956a850c2913039fd4d9
Binary files /dev/null and b/core/css/images/ui-bg_highlight-hard_70_000000_1x100.png differ
diff --git a/core/css/images/ui-bg_highlight-soft_100_deedf7_1x100.png b/core/css/images/ui-bg_highlight-soft_100_deedf7_1x100.png
new file mode 100644
index 0000000000000000000000000000000000000000..2289d3c7d7b8e0892f5921de1af87a44b7059eb2
Binary files /dev/null and b/core/css/images/ui-bg_highlight-soft_100_deedf7_1x100.png differ
diff --git a/core/css/images/ui-bg_highlight-soft_25_ffef8f_1x100.png b/core/css/images/ui-bg_highlight-soft_25_ffef8f_1x100.png
new file mode 100644
index 0000000000000000000000000000000000000000..54aff0cb974c7bbde9bed3eb8a05c3b24f140965
Binary files /dev/null and b/core/css/images/ui-bg_highlight-soft_25_ffef8f_1x100.png differ
diff --git a/core/css/images/ui-icons_2694e8_256x240.png b/core/css/images/ui-icons_2694e8_256x240.png
new file mode 100644
index 0000000000000000000000000000000000000000..9d192c2f65905cc05b66db64ee396b66299e8fc5
Binary files /dev/null and b/core/css/images/ui-icons_2694e8_256x240.png differ
diff --git a/core/css/images/ui-icons_2e83ff_256x240.png b/core/css/images/ui-icons_2e83ff_256x240.png
new file mode 100644
index 0000000000000000000000000000000000000000..09d1cdc856c292c4ab6dd818c7543ac0828bd616
Binary files /dev/null and b/core/css/images/ui-icons_2e83ff_256x240.png differ
diff --git a/core/css/images/ui-icons_3d80b3_256x240.png b/core/css/images/ui-icons_3d80b3_256x240.png
new file mode 100644
index 0000000000000000000000000000000000000000..f13b206645b11f97fc59feaa18002b2002b118fa
Binary files /dev/null and b/core/css/images/ui-icons_3d80b3_256x240.png differ
diff --git a/core/css/images/ui-icons_72a7cf_256x240.png b/core/css/images/ui-icons_72a7cf_256x240.png
new file mode 100644
index 0000000000000000000000000000000000000000..0d20b7308331069ee2950fa64cb15f4a29ca55f3
Binary files /dev/null and b/core/css/images/ui-icons_72a7cf_256x240.png differ
diff --git a/core/css/images/ui-icons_ffffff_256x240.png b/core/css/images/ui-icons_ffffff_256x240.png
new file mode 100644
index 0000000000000000000000000000000000000000..42f8f992c727ddaa617da224a522e463df690387
Binary files /dev/null and b/core/css/images/ui-icons_ffffff_256x240.png differ
diff --git a/core/css/jquery-tipsy.css b/core/css/jquery-tipsy.css
new file mode 100644
index 0000000000000000000000000000000000000000..957385a269f21e00d114144dc4fc94b5fc578596
--- /dev/null
+++ b/core/css/jquery-tipsy.css
@@ -0,0 +1,25 @@
+.tipsy { font-size:10px; position:absolute; padding:5px; z-index:100000; }
+.tipsy-inner { background-color:#000; color:#FFF; max-width:200px; padding:5px 8px 4px 8px; text-align:center; }
+
+/* Rounded corners */
+.tipsy-inner { border-radius:3px; -moz-border-radius:3px; -webkit-border-radius:3px; }
+
+/* Uncomment for shadow */
+/*.tipsy-inner { box-shadow:0 0 5px #000000; -webkit-box-shadow:0 0 5px #000000; -moz-box-shadow:0 0 5px #000000; }*/
+
+.tipsy-arrow { position:absolute; width:0; height:0; border:5px solid transparent; }
+
+/* Rules to colour arrows */
+.tipsy-arrow-n { border-bottom-color:#000; }
+.tipsy-arrow-s { border-top-color:#000; }
+.tipsy-arrow-e { border-left-color:#000; }
+.tipsy-arrow-w { border-right-color:#000; }
+
+.tipsy-n .tipsy-arrow, .tipsy-nw .tipsy-arrow, .tipsy-ne .tipsy-arrow { top:0; border-top:none; }
+.tipsy-s .tipsy-arrow, .tipsy-sw .tipsy-arrow, .tipsy-se .tipsy-arrow { bottom:0; border-bottom:none; }
+.tipsy-n .tipsy-arrow, .tipsy-s .tipsy-arrow { left:50%; margin-left:-5px; }
+.tipsy-nw .tipsy-arrow, .tipsy-sw .tipsy-arrow { left:10px; }
+.tipsy-ne .tipsy-arrow, .tipsy-se .tipsy-arrow { right:10px; }
+.tipsy-e .tipsy-arrow, .tipsy-w .tipsy-arrow { top:50%; margin-top:-5px; }
+.tipsy-e .tipsy-arrow { right:0; border-right:none; }
+.tipsy-w .tipsy-arrow { left:0; border-left:none; }
diff --git a/core/css/jquery-ui-1.8.14.custom.css b/core/css/jquery-ui-1.8.14.custom.css
new file mode 100644
index 0000000000000000000000000000000000000000..fe310705756befa7e6c0e28fc7edd0d2e4323275
--- /dev/null
+++ b/core/css/jquery-ui-1.8.14.custom.css
@@ -0,0 +1,568 @@
+/*
+ * jQuery UI CSS Framework 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden { display: none; }
+.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); }
+.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; }
+.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+.ui-helper-clearfix { display: inline-block; }
+/* required comment for clearfix to work in Opera \*/
+* html .ui-helper-clearfix { height:1%; }
+.ui-helper-clearfix { display:block; }
+/* end clearfix */
+.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); }
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled { cursor: default !important; }
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
+
+
+/*
+ * jQuery UI CSS Framework 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Theming/API
+ *
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
+ */
+
+
+/* Component containers
+----------------------------------*/
+.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; }
+.ui-widget .ui-widget { font-size: 1em; }
+.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; }
+.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; }
+.ui-widget-content a { color: #333333; }
+.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; }
+.ui-widget-header a { color: #ffffff; }
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; }
+.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; }
+.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; }
+.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; }
+.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; }
+.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; }
+.ui-widget :active { outline: none; }
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight  {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; }
+.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; }
+.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; }
+.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; }
+.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; }
+.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; }
+.ui-priority-secondary, .ui-widget-content .ui-priority-secondary,  .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; }
+.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; }
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); }
+.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); }
+.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); }
+.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
+.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); }
+.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); }
+.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); }
+
+/* positioning */
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-off { background-position: -96px -144px; }
+.ui-icon-radio-on { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; -khtml-border-top-left-radius: 4px; border-top-left-radius: 4px; }
+.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; -khtml-border-top-right-radius: 4px; border-top-right-radius: 4px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; -khtml-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; }
+.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; -khtml-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; }
+
+/* Overlays */
+.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); }
+.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -khtml-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/*
+ * jQuery UI Resizable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizable#theming
+ */
+.ui-resizable { position: relative;}
+.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; }
+.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; }
+.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; }
+.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; }
+.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; }
+.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; }
+.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; }
+.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; }
+.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; }
+.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;}/*
+ * jQuery UI Selectable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectable#theming
+ */
+.ui-selectable-helper { position: absolute; z-index: 100; border:1px dotted black; }
+/*
+ * jQuery UI Accordion 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion#theming
+ */
+/* IE/Win - Fix animation bug - #4615 */
+.ui-accordion { width: 100%; }
+.ui-accordion .ui-accordion-header { cursor: pointer; position: relative; margin-top: 1px; zoom: 1; }
+.ui-accordion .ui-accordion-li-fix { display: inline; }
+.ui-accordion .ui-accordion-header-active { border-bottom: 0 !important; }
+.ui-accordion .ui-accordion-header a { display: block; font-size: 1em; padding: .5em .5em .5em .7em; }
+.ui-accordion-icons .ui-accordion-header a { padding-left: 2.2em; }
+.ui-accordion .ui-accordion-header .ui-icon { position: absolute; left: .5em; top: 50%; margin-top: -8px; }
+.ui-accordion .ui-accordion-content { padding: 1em 2.2em; border-top: 0; margin-top: -2px; position: relative; top: 1px; margin-bottom: 2px; overflow: auto; display: none; zoom: 1; }
+.ui-accordion .ui-accordion-content-active { display: block; }
+/*
+ * jQuery UI Autocomplete 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete#theming
+ */
+.ui-autocomplete { position: absolute; cursor: default; }	
+
+/* workarounds */
+* html .ui-autocomplete { width:1px; } /* without this, the menu expands to 100% in IE6 */
+
+/*
+ * jQuery UI Menu 1.8.14
+ *
+ * Copyright 2010, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Menu#theming
+ */
+.ui-menu {
+	list-style:none;
+	padding: 2px;
+	margin: 0;
+	display:block;
+	float: left;
+}
+.ui-menu .ui-menu {
+	margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+	margin:0;
+	padding: 0;
+	zoom: 1;
+	float: left;
+	clear: left;
+	width: 100%;
+}
+.ui-menu .ui-menu-item a {
+	text-decoration:none;
+	display:block;
+	padding:.2em .4em;
+	line-height:1.5;
+	zoom:1;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+	font-weight: normal;
+	margin: -1px;
+}
+/*
+ * jQuery UI Button 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button#theming
+ */
+.ui-button { display: inline-block; position: relative; padding: 0; margin-right: .1em; text-decoration: none !important; cursor: pointer; text-align: center; zoom: 1; overflow: visible; } /* the overflow property removes extra width in IE */
+.ui-button-icon-only { width: 2.2em; } /* to make room for the icon, a width needs to be set here */
+button.ui-button-icon-only { width: 2.4em; } /* button elements seem to need a little more width */
+.ui-button-icons-only { width: 3.4em; } 
+button.ui-button-icons-only { width: 3.7em; } 
+
+/*button text element */
+.ui-button .ui-button-text { display: block; line-height: 1.4;  }
+.ui-button-text-only .ui-button-text { padding: .4em 1em; }
+.ui-button-icon-only .ui-button-text, .ui-button-icons-only .ui-button-text { padding: .4em; text-indent: -9999999px; }
+.ui-button-text-icon-primary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 1em .4em 2.1em; }
+.ui-button-text-icon-secondary .ui-button-text, .ui-button-text-icons .ui-button-text { padding: .4em 2.1em .4em 1em; }
+.ui-button-text-icons .ui-button-text { padding-left: 2.1em; padding-right: 2.1em; }
+/* no icon support for input elements, provide padding by default */
+input.ui-button { padding: .4em 1em; }
+
+/*button icon element(s) */
+.ui-button-icon-only .ui-icon, .ui-button-text-icon-primary .ui-icon, .ui-button-text-icon-secondary .ui-icon, .ui-button-text-icons .ui-icon, .ui-button-icons-only .ui-icon { position: absolute; top: 50%; margin-top: -8px; }
+.ui-button-icon-only .ui-icon { left: 50%; margin-left: -8px; }
+.ui-button-text-icon-primary .ui-button-icon-primary, .ui-button-text-icons .ui-button-icon-primary, .ui-button-icons-only .ui-button-icon-primary { left: .5em; }
+.ui-button-text-icon-secondary .ui-button-icon-secondary, .ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+.ui-button-text-icons .ui-button-icon-secondary, .ui-button-icons-only .ui-button-icon-secondary { right: .5em; }
+
+/*button sets*/
+.ui-buttonset { margin-right: 7px; }
+.ui-buttonset .ui-button { margin-left: 0; margin-right: -.3em; }
+
+/* workarounds */
+button.ui-button::-moz-focus-inner { border: 0; padding: 0; } /* reset extra padding in Firefox */
+/*
+ * jQuery UI Dialog 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog#theming
+ */
+.ui-dialog { position: absolute; padding: .2em; width: 300px; overflow: hidden; }
+.ui-dialog .ui-dialog-titlebar { padding: .4em 1em; position: relative;  }
+.ui-dialog .ui-dialog-title { float: left; margin: .1em 16px .1em 0; } 
+.ui-dialog .ui-dialog-titlebar-close { position: absolute; right: .3em; top: 50%; width: 19px; margin: -10px 0 0 0; padding: 1px; height: 18px; }
+.ui-dialog .ui-dialog-titlebar-close span { display: block; margin: 1px; }
+.ui-dialog .ui-dialog-titlebar-close:hover, .ui-dialog .ui-dialog-titlebar-close:focus { padding: 0; }
+.ui-dialog .ui-dialog-content { position: relative; border: 0; padding: .5em 1em; background: none; overflow: auto; zoom: 1; }
+.ui-dialog .ui-dialog-buttonpane { text-align: left; border-width: 1px 0 0 0; background-image: none; margin: .5em 0 0 0; padding: .3em 1em .5em .4em; }
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { float: right; }
+.ui-dialog .ui-dialog-buttonpane button { margin: .5em .4em .5em 0; cursor: pointer; }
+.ui-dialog .ui-resizable-se { width: 14px; height: 14px; right: 3px; bottom: 3px; }
+.ui-draggable .ui-dialog-titlebar { cursor: move; }
+/*
+ * jQuery UI Slider 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider#theming
+ */
+.ui-slider { position: relative; text-align: left; }
+.ui-slider .ui-slider-handle { position: absolute; z-index: 2; width: 1.2em; height: 1.2em; cursor: default; }
+.ui-slider .ui-slider-range { position: absolute; z-index: 1; font-size: .7em; display: block; border: 0; background-position: 0 0; }
+
+.ui-slider-horizontal { height: .8em; }
+.ui-slider-horizontal .ui-slider-handle { top: -.3em; margin-left: -.6em; }
+.ui-slider-horizontal .ui-slider-range { top: 0; height: 100%; }
+.ui-slider-horizontal .ui-slider-range-min { left: 0; }
+.ui-slider-horizontal .ui-slider-range-max { right: 0; }
+
+.ui-slider-vertical { width: .8em; height: 100px; }
+.ui-slider-vertical .ui-slider-handle { left: -.3em; margin-left: 0; margin-bottom: -.6em; }
+.ui-slider-vertical .ui-slider-range { left: 0; width: 100%; }
+.ui-slider-vertical .ui-slider-range-min { bottom: 0; }
+.ui-slider-vertical .ui-slider-range-max { top: 0; }/*
+ * jQuery UI Tabs 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs#theming
+ */
+.ui-tabs { position: relative; padding: .2em; zoom: 1; } /* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+.ui-tabs .ui-tabs-nav { margin: 0; padding: .2em .2em 0; }
+.ui-tabs .ui-tabs-nav li { list-style: none; float: left; position: relative; top: 1px; margin: 0 .2em 1px 0; border-bottom: 0 !important; padding: 0; white-space: nowrap; }
+.ui-tabs .ui-tabs-nav li a { float: left; padding: .5em 1em; text-decoration: none; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected { margin-bottom: 0; padding-bottom: 1px; }
+.ui-tabs .ui-tabs-nav li.ui-tabs-selected a, .ui-tabs .ui-tabs-nav li.ui-state-disabled a, .ui-tabs .ui-tabs-nav li.ui-state-processing a { cursor: text; }
+.ui-tabs .ui-tabs-nav li a, .ui-tabs.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-selected a { cursor: pointer; } /* first selector in group seems obsolete, but required to overcome bug in Opera applying cursor: text overall if defined elsewhere... */
+.ui-tabs .ui-tabs-panel { display: block; border-width: 0; padding: 1em 1.4em; background: none; }
+.ui-tabs .ui-tabs-hide { display: none !important; }
+/*
+ * jQuery UI Datepicker 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker#theming
+ */
+.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; }
+.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; }
+.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; }
+.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; }
+.ui-datepicker .ui-datepicker-prev { left:2px; }
+.ui-datepicker .ui-datepicker-next { right:2px; }
+.ui-datepicker .ui-datepicker-prev-hover { left:1px; }
+.ui-datepicker .ui-datepicker-next-hover { right:1px; }
+.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px;  }
+.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; }
+.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; }
+.ui-datepicker select.ui-datepicker-month-year {width: 100%;}
+.ui-datepicker select.ui-datepicker-month, 
+.ui-datepicker select.ui-datepicker-year { width: 49%;}
+.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; }
+.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0;  }
+.ui-datepicker td { border: 0; padding: 1px; }
+.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; }
+.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; }
+.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; }
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; }
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi { width:auto; }
+.ui-datepicker-multi .ui-datepicker-group { float:left; }
+.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; }
+.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; }
+.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; }
+.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; }
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; }
+.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; }
+.ui-datepicker-row-break { clear:both; width:100%; font-size:0em; }
+
+/* RTL support */
+.ui-datepicker-rtl { direction: rtl; }
+.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; }
+.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; }
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group { float:right; }
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; }
+
+/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */
+.ui-datepicker-cover {
+    display: none; /*sorry for IE5*/
+    display/**/: block; /*sorry for IE5*/
+    position: absolute; /*must have*/
+    z-index: -1; /*must have*/
+    filter: mask(); /*must have*/
+    top: -4px; /*must have*/
+    left: -4px; /*must have*/
+    width: 200px; /*must have*/
+    height: 200px; /*must have*/
+}/*
+ * jQuery UI Progressbar 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar#theming
+ */
+.ui-progressbar { height:2em; text-align: left; }
+.ui-progressbar .ui-progressbar-value {margin: -1px; height:100%; }
\ No newline at end of file
diff --git a/core/css/multiselect.css b/core/css/multiselect.css
new file mode 100644
index 0000000000000000000000000000000000000000..22e5098d3bf1c62179f90a137af0506480d0cb44
--- /dev/null
+++ b/core/css/multiselect.css
@@ -0,0 +1,6 @@
+ul.multiselectoptions { z-index:49; position:absolute; background-color:#fff; padding-top:.5em; border:1px solid #ddd; border-top:none; -moz-border-radius-bottomleft:.5em; -webkit-border-bottom-left-radius:.5em; border-bottom-left-radius:.5em; -moz-border-radius-bottomright:.5em; -webkit-border-bottom-right-radius:.5em; border-bottom-right-radius:.5em; -moz-box-shadow:0 1px 1px #ddd; -webkit-box-shadow:0 1px 1px #ddd; box-shadow:0 1px 1px #ddd; }
+div.multiselect { padding-right:.6em; display:inline; position:relative; display:inline-block }
+div.multiselect.active { background-color:#fff; border-bottom:none; border-bottom-left-radius:0; border-bottom-right-radius:0; z-index:50; position:relative }
+div.multiselect>span:first-child { margin-right:2em; float:left; }
+div.multiselect>span:last-child { float:right; position:relative }
+ul.multiselectoptions input.new{ margin:0; padding-bottom:0.2em; padding-top:0.2em; border-top-left-radius:0; border-top-right-radius:0; }
diff --git a/core/css/styles.css b/core/css/styles.css
new file mode 100644
index 0000000000000000000000000000000000000000..a3326c0082611a2027ec376746637a73d17189e6
--- /dev/null
+++ b/core/css/styles.css
@@ -0,0 +1,114 @@
+/* Copyright (c) 2011, Jan-Christoph Borchardt
+ This file is licensed under the Affero General Public License version 3 or later.
+ See the COPYING-README file. */
+
+html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { margin:0; padding:0; border:0; outline:0; font-weight:inherit; font-size:100%; font-family:inherit; vertical-align:baseline; cursor:default; }
+article, aside, dialog, figure, footer, header, hgroup, nav, section { display:block; }
+body { line-height:1.5; }
+table { border-collapse:separate; border-spacing:0; white-space:nowrap; }
+caption, th, td { text-align:left; font-weight:normal; }
+table, td, th { vertical-align:middle; }
+a { border:0; color:#000; text-decoration:none;}
+a, a *, input, input *, select, .button span, li, label { cursor:pointer; }
+ul { list-style:none; }
+body { background:#fefefe; font:normal .8em/1.6em "Lucida Grande", Arial, Verdana, sans-serif; color:#000; }
+
+
+/* HEADERS */
+#body-user #header, #body-settings #header { position:fixed; top:0; z-index:100; width:100%; height:2.5em; padding:.5em; background:#1d2d44; -moz-box-shadow:0 0 10px #000, inset 0 -2px 10px #222; -webkit-box-shadow:0 0 10px #000, inset 0 -2px 10px #222; box-shadow:0 0 10px #000, inset 0 -2px 10px #222; }
+#body-login #header { margin:-2em auto 0; text-align:center; height:10em;
+ -moz-box-shadow:0 0 1em #000; -webkit-box-shadow:0 0 1em #000; box-shadow:0 0 1em #000;
+background: #1d2d44; /* Old browsers */
+background: -moz-linear-gradient(top, #35537a 0%, #1d2d42 100%); /* FF3.6+ */
+background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#35537a), color-stop(100%,#1d2d42)); /* Chrome,Safari4+ */
+background: -webkit-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* Chrome10+,Safari5.1+ */
+background: -o-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* Opera11.10+ */
+background: -ms-linear-gradient(top, #35537a 0%,#1d2d42 100%); /* IE10+ */
+background: linear-gradient(top, #35537a 0%,#1d2d42 100%); /* W3C */
+filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#35537a', endColorstr='#1d2d42',GradientType=0 ); /* IE6-9 */ }
+
+#owncloud { float:left; }
+
+
+/* INPUTS */
+input[type="text"], input[type="password"] { cursor:text; }
+input, select, .button, #quota, div.jp-progress, .pager li a { font-size:1em; width:10em; margin:.3em; padding:.6em .5em .4em; background:#fff; color:#333; border:1px solid #ddd; -moz-box-shadow:0 1px 1px #fff, 0 2px 0 #bbb inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; box-shadow:0 1px 1px #fff, 0 1px 0 #bbb inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; outline:none; }
+input[type="text"], input[type="password"], input[type="search"] { background:#f8f8f8; color:#555; cursor:text; }
+input[type="text"]:hover, input[type="text"]:focus, input[type="password"]:hover, input[type="password"]:focus, input[type="search"]:hover, input[type="search"]:focus { background:#fff; color:#333; }
+
+input[type="submit"], input[type="button"], .button, #quota, div.jp-progress, .pager li a { width:auto; padding:.4em; border:1px solid #ddd; font-weight:bold; cursor:pointer; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; }
+input[type="submit"]:hover, input[type="submit"]:focus, input[type="button"]:hover, input[type="button"]:focus, .button:hover { background:#fff; color:#333; }
+input[type="checkbox"] { width:auto; }
+
+#body-login input { font-size:1.5em; }
+#body-login input[type="submit"] { float:right; margin-right:.8em; }
+#remember_login { margin:.8em .2em 0 1em; }
+form.searchbox input[type="search"] { position:fixed; font-size:1.2em; top:.4em; right:3em; padding:.2em .5em .2em 1.5em; background-image:url('../img/actions/search.svg'); background-repeat:no-repeat; background-position:.5em center; border:0; -moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em; }
+input[type="submit"].enabled { background:#66f866; border:1px solid #5e5; -moz-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; -webkit-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #cfc inset; }
+input[type="submit"].highlight{ background:#ffc100; border:1px solid #db0; text-shadow:#ffeedd 0 1px 0; -moz-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #ffeedd inset; -webkit-box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #ffeedd inset; box-shadow:0 1px 1px #f8f8f8, 0 1px 1px #ffeedd inset; }
+
+
+/* CONTENT ------------------------------------------------------------------ */
+#controls { width:100%; top:3.5em; height:2.8em; margin:0; background:#f7f7f7; border-bottom:1px solid #eee; position:fixed; z-index:50; -moz-box-shadow:0 -3px 7px #000; -webkit-box-shadow:0 -3px 7px #000; box-shadow:0 -3px 7px #000; }
+#controls .button { display:inline-block; }
+#content { margin:3.5em 0 0 12.5em; }
+#leftcontent { position:absolute; top:6.4em; width:20em; background:#f8f8f8; height:100%; border-right:1px solid #ddd; }
+#leftcontent li { padding:.3em .8em; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; }
+#leftcontent li:hover, #leftcontent li:active { background:#eee; }
+#rightcontent { position:absolute; top:6.4em; left:33em; }
+
+
+/* LOG IN & INSTALLATION ------------------------------------------------------------ */
+#body-login { background:#ddd; }
+#body-login p.info { width:16em; margin:2em auto; padding:1em; background:#eee; -moz-box-shadow:0 1px 0 #bbb inset; -webkit-box-shadow:0 1px 0 #bbb inset; box-shadow:0 1px 0 #bbb inset; -moz-border-radius:1em; -webkit-border-radius:1em; border-radius:1em; }
+#body-login p.info a { font-weight:bold; }
+
+#login { min-height:30em; margin:2em auto 0; border-bottom:1px solid #f8f8f8; background:#eee; }
+#login form { width:18em; margin:2em auto 5em; padding:0; }
+#login form fieldset { background:0; border:0; margin-bottom:2em; padding:0; }
+#login form fieldset legend { font-weight:bold; }
+#login form label { position:absolute; margin:.8em .8em; font-size:1.5em; color:#666; }
+#login #dbhostlabel, #login #dbtableprefixlabel, #login #directorylabel { display:block; margin:.95em 0 .8em -7em; }
+#login form input[type="checkbox"]+label { position:relative; margin:0; font-size:1em; text-shadow:#fff 0 1px 0; }
+#login form ul.errors { background:#fed7d7; border:1px solid #f00; list-style-indent:inside; margin:0 0 4em 0; padding:1em 1em 1em 5em; }
+
+#login form #selectDbType { text-align:center; }
+#login form #selectDbType label { position:static; font-size:1em; margin:0 -.3em 1em; cursor:pointer; padding:.4em; border:1px solid #ddd; font-weight:bold; background:#f8f8f8; color:#555; text-shadow:#eee 0 1px 0; -moz-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; -webkit-box-shadow:0 1px 1px #fff, 0 1px 1px #fff inset; }
+#login form #selectDbType label span { cursor:pointer; font-size:0.9em; }
+#login form #selectDbType label.ui-state-hover span, #login form #selectDbType label.ui-state-active span { color:#000; }
+#login form #selectDbType label.ui-state-hover, #login form #selectDbType label.ui-state-active { color: #333; background-color: #ccc; }
+
+
+/* NAVIGATION ------------------------------------------------------------- */
+#navigation { position:fixed; top:3.5em; float:left; width:12.5em; padding:0; z-index:75; height:100%; background:#eee; border-right: 1px #ccc solid; -moz-box-shadow: -3px 0 7px #000; -webkit-box-shadow: -3px 0 7px #000; box-shadow: -3px 0 7px #000; overflow:hidden;}
+#navigation a { display:block; padding:.6em .5em .4em 2.5em; background:#eee 1em center no-repeat; border-bottom:1px solid #ddd; border-top:1px solid #fff; text-decoration:none; font-size:1.2em; color:#666; text-shadow:#f8f8f8 0 1px 0; -webkit-transition:background 300ms; -moz-transition:background 300ms; -o-transition:background 300ms; transition:background 300ms; }
+#navigation a.active, #navigation a:hover, #navigation a:focus { background-color:#dbdbdb; border-top:1px solid #d4d4d4; border-bottom:1px solid #ccc; color:#333; }
+#navigation a.active { background-color:#ddd; }
+#navigation #settings { position:absolute; bottom:3.5em; width:100%; }
+#expand { margin:0 0 .2em 1.2em; cursor:pointer; }
+#expand+span { position:relative; bottom:.4em; left:.2em; font-size:1.2em; color:#666; text-shadow:#f8f8f8 0 1px 0; }
+#logout { position:absolute; right:0; top:0; padding:1.2em 2em .55em 1.2em; }
+
+
+/* VARIOUS REUSABLE SELECTORS */
+.hidden { display:none; }
+
+#notification { z-index:101; cursor:pointer; background-color:#fc4; border:0; padding:0 .7em .3em; display:none; position:fixed; left:50%; top:0; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; -moz-border-radius-bottomright:1em; -webkit-border-bottom-right-radius:1em; border-bottom-right-radius:1em; }
+
+.action, .selectedActions a, #logout { opacity:.3; -webkit-transition:opacity 500ms; -moz-transition:opacity 500ms; -o-transition:opacity 500ms; transition:opacity 500ms; }
+.action:hover, .selectedActions a:hover, #logout:hover { opacity:1; }
+
+table tr { -webkit-transition:background-color 500ms; -moz-transition:background-color 500ms; -o-transition:background-color 500ms; transition:background-color 500ms; }
+tbody tr:hover, tr:active { background-color:#f8f8f8; }
+
+#body-settings .personalblock, #body-settings .helpblock { padding:.5em 1em; margin:1em; background:#f8f8f8; color:#555; text-shadow:#fff 0 1px 0; -moz-border-radius:.5em; -webkit-border-radius:.5em; border-radius:.5em; }
+#body-settings .personalblock#quota { position:relative; margin-top:4.5em; padding:0; }
+#body-settings #controls+.helpblock { position:relative; margin-top:7.3em; }
+
+#quota div, div.jp-play-bar, div.jp-seek-bar { padding:.6em 1em; background:#e6e6e6; font-weight:normal; white-space:nowrap; -moz-border-radius-bottomleft:.4em; -webkit-border-bottom-left-radius:.4em; border-bottom-left-radius:.4em; -moz-border-radius-topleft:.4em; -webkit-border-top-left-radius:.4em; border-top-left-radius:.4em; }
+div.jp-play-bar, div.jp-seek-bar { padding:0; }
+
+.pager { list-style:none; float:right; display:inline; margin:.7em 12.7em 0 0; }
+.pager li { display:inline-block; }
+
+li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ffe .8em .8em no-repeat; border:1px solid #ccc; -moz-border-radius:10px; -webkit-border-radius:10px; border-radius:10px; }
diff --git a/core/img/actions/delete.png b/core/img/actions/delete.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc0c782882deaa4f9ecf1676592ddba0cc9aacbc
Binary files /dev/null and b/core/img/actions/delete.png differ
diff --git a/core/img/actions/delete.svg b/core/img/actions/delete.svg
new file mode 100644
index 0000000000000000000000000000000000000000..86c8317d01da333608489b0b8ee708d524cd3747
--- /dev/null
+++ b/core/img/actions/delete.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="delete.svg">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="25.345359"
+     inkscape:cx="1.5609441"
+     inkscape:cy="8.3505321"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 3,1040.3622 1,-1 4,3 4,-3 1,1 -3,4 3,4 -1,1 -4,-3 -4,3 -1,-1 3,-4 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccccccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/download.png b/core/img/actions/download.png
new file mode 100644
index 0000000000000000000000000000000000000000..14e88e14c0f1dd68a8f155cc99ccf958507a555c
Binary files /dev/null and b/core/img/actions/download.png differ
diff --git a/core/img/actions/download.svg b/core/img/actions/download.svg
new file mode 100644
index 0000000000000000000000000000000000000000..107a46f07bf454ba25915bb6179dec0a966f6797
--- /dev/null
+++ b/core/img/actions/download.svg
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="download.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.921875"
+     inkscape:cx="-5.3403178"
+     inkscape:cy="10.148736"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 6,1037.3622 4,0 1,7 4,0 -7,7 -7,-7 4,0 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccccccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/logout.png b/core/img/actions/logout.png
new file mode 100644
index 0000000000000000000000000000000000000000..74dcd33bee7b1bd0d502068f570e300fa0f4d9ce
Binary files /dev/null and b/core/img/actions/logout.png differ
diff --git a/core/img/actions/logout.svg b/core/img/actions/logout.svg
new file mode 100644
index 0000000000000000000000000000000000000000..d95ac959778b23c0b1ff8687136510a11567dc5a
--- /dev/null
+++ b/core/img/actions/logout.svg
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="18"
+   height="16"
+   id="svg2403"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="system-shutdown-panel2.svg">
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview3047"
+     showgrid="false"
+     inkscape:zoom="17.875"
+     inkscape:cx="9"
+     inkscape:cy="8"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2403" />
+  <metadata
+     id="metadata15">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <defs
+     id="defs2405">
+    <linearGradient
+       x1="11.644068"
+       y1="2.4988678"
+       x2="11.644068"
+       y2="15.00281"
+       id="linearGradient2392"
+       xlink:href="#linearGradient3678"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(1.0000001,1.1920928e-8)" />
+    <linearGradient
+       x1="8.4964771"
+       y1="-0.061573759"
+       x2="8.4964771"
+       y2="8.083209"
+       id="linearGradient2395"
+       xlink:href="#linearGradient3678"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0526316,0,0,0.9843625,0.5789474,0.06024281)" />
+    <linearGradient
+       id="linearGradient3678">
+      <stop
+         id="stop3680"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3682"
+         style="stop-color:#e6e6e6;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+  </defs>
+  <path
+     d="M 5.8427514,4.45 C 0.60889,7.192151 2.9756281,15.05 8.959192,15.05 c 5.921233,0 8.575036,-7.449605 3.11644,-10.6 M 9,8 9,1.9996319"
+     id="path3341-5"
+     style="opacity:0.5;fill:none;stroke:#000000;stroke-width:1.89999998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
+  <path
+     d="M 9,7 9,0.99963191"
+     id="path3716"
+     style="fill:none;stroke:url(#linearGradient2395);stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
+  <path
+     d="M 5.8427514,3.45 C 0.60889,6.1921513 2.9756281,14.05 8.959192,14.05 c 5.921233,0 8.575036,-7.4496051 3.11644,-10.6"
+     id="path3341"
+     style="fill:none;stroke:url(#linearGradient2392);stroke-width:1.89999998;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" />
+</svg>
diff --git a/core/img/actions/pause-big.png b/core/img/actions/pause-big.png
new file mode 100644
index 0000000000000000000000000000000000000000..9bcfd0406d8165e2adc4d3cc0909d038e94faa93
Binary files /dev/null and b/core/img/actions/pause-big.png differ
diff --git a/core/img/actions/pause-big.svg b/core/img/actions/pause-big.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b521057a35cef9f29c055f380f5915ab861e287f
--- /dev/null
+++ b/core/img/actions/pause-big.svg
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="play-big.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-add.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.2402344"
+     inkscape:cx="110.24162"
+     inkscape:cy="54.102269"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 1,1037.3622 0,14 5,0 0,-14 z m 9,0 0,14 5,0 0,-14 z"
+       id="path3086-7"
+       sodipodi:nodetypes="cccccccccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/pause.png b/core/img/actions/pause.png
new file mode 100644
index 0000000000000000000000000000000000000000..ced8c43ab3441af94470f9a97440759f175b9764
Binary files /dev/null and b/core/img/actions/pause.png differ
diff --git a/core/img/actions/pause.svg b/core/img/actions/pause.svg
new file mode 100644
index 0000000000000000000000000000000000000000..ff3c69d6c71c704ea758e3aea58329118c224a3a
--- /dev/null
+++ b/core/img/actions/pause.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="play-next.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-big.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.921875"
+     inkscape:cx="-5.3403178"
+     inkscape:cy="10.148736"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="M 3 3 L 3 13 L 7 13 L 7 3 L 3 3 z M 9 3 L 9 13 L 13 13 L 13 3 L 9 3 z "
+       transform="translate(0,1036.3622)"
+       id="path3086" />
+  </g>
+</svg>
diff --git a/core/img/actions/play-add.png b/core/img/actions/play-add.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c330d4ac441aa9c7eec3c811ddcf558872e3f05
Binary files /dev/null and b/core/img/actions/play-add.png differ
diff --git a/core/img/actions/play-add.svg b/core/img/actions/play-add.svg
new file mode 100644
index 0000000000000000000000000000000000000000..25ff0b57eee76312ddfe1547b0af797157d518b9
--- /dev/null
+++ b/core/img/actions/play-add.svg
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="play-add.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-add.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="25.345359"
+     inkscape:cx="2.4224415"
+     inkscape:cy="8.0693339"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <g
+       id="g4806"
+       style="fill:#000000;fill-opacity:1">
+      <path
+         sodipodi:nodetypes="cccc"
+         inkscape:connector-curvature="0"
+         id="path3086"
+         d="m 2,1037.3622 11,6 -11,6 z"
+         style="fill:#000000;fill-opacity:1;stroke:none" />
+      <path
+         sodipodi:nodetypes="ccccccccccccc"
+         id="rect2984"
+         d="m 11,1045.3622 0,2 -2,0 0,2 2,0 0,2 2,0 0,-2 2,0 0,-2 -2,0 0,-2 z"
+         style="fill:#000000;fill-opacity:1;stroke:none"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/core/img/actions/play-big.png b/core/img/actions/play-big.png
new file mode 100644
index 0000000000000000000000000000000000000000..3ccd36129ecb340989191d6dfa4410c821b3230a
Binary files /dev/null and b/core/img/actions/play-big.png differ
diff --git a/core/img/actions/play-big.svg b/core/img/actions/play-big.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2ef67415323bd115ce2d60993e160bfc4ce059bf
--- /dev/null
+++ b/core/img/actions/play-big.svg
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="play.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-add.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.921875"
+     inkscape:cx="-5.3403178"
+     inkscape:cy="10.148736"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 0,1036.3622 16,8 -16,8 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/play-next.png b/core/img/actions/play-next.png
new file mode 100644
index 0000000000000000000000000000000000000000..0c0ccc87cdc433492e972be8bd14b6b6bcd6ee32
Binary files /dev/null and b/core/img/actions/play-next.png differ
diff --git a/core/img/actions/play-next.svg b/core/img/actions/play-next.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9a41e4bd9d088db119c0e0930646f167361b37b1
--- /dev/null
+++ b/core/img/actions/play-next.svg
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="play-big.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-big.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.921875"
+     inkscape:cx="-5.3403178"
+     inkscape:cy="10.148736"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 7,1038.3622 7,6 -7,6 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 2,1038.3622 7,6 -7,6 z"
+       id="path3086-5"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/play-previous.png b/core/img/actions/play-previous.png
new file mode 100644
index 0000000000000000000000000000000000000000..d98cedaa1e8904d7ec6c393810746ba24c263e40
Binary files /dev/null and b/core/img/actions/play-previous.png differ
diff --git a/core/img/actions/play-previous.svg b/core/img/actions/play-previous.svg
new file mode 100644
index 0000000000000000000000000000000000000000..31d45dedb4df5cd0792f78416366810d2f2e1938
--- /dev/null
+++ b/core/img/actions/play-previous.svg
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="next.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-next.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.921875"
+     inkscape:cx="-5.3403178"
+     inkscape:cy="10.148736"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 9,1038.3622 -7,6 7,6 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 14,1038.3622 -7,6 7,6 z"
+       id="path3086-5"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/play.png b/core/img/actions/play.png
new file mode 100644
index 0000000000000000000000000000000000000000..a252a751554e142eb9dda6daecf529b41ffb0bed
Binary files /dev/null and b/core/img/actions/play.png differ
diff --git a/core/img/actions/play.svg b/core/img/actions/play.svg
new file mode 100644
index 0000000000000000000000000000000000000000..7bb7b5c262a2d7f1e407250587d521fd5b1ef8ad
--- /dev/null
+++ b/core/img/actions/play.svg
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="play.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/play-add.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="17.921875"
+     inkscape:cx="-5.3403178"
+     inkscape:cy="10.148736"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 2,1038.3622 12,6 -12,6 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/public.png b/core/img/actions/public.png
new file mode 100644
index 0000000000000000000000000000000000000000..75d1366326b1bb1ef22ffba0a0982ed5112096c4
Binary files /dev/null and b/core/img/actions/public.png differ
diff --git a/core/img/actions/public.svg b/core/img/actions/public.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b47305fbd086f41304f9f75fd64f84d40ccc90e1
--- /dev/null
+++ b/core/img/actions/public.svg
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg2457"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="world.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud-sharing/core/img/actions/settings.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata23">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview21"
+     showgrid="false"
+     inkscape:zoom="17.875"
+     inkscape:cx="-12.837249"
+     inkscape:cy="5.7622378"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2457" />
+  <defs
+     id="defs2459">
+    <linearGradient
+       id="linearGradient5128">
+      <stop
+         id="stop5130"
+         style="stop-color:#e5e5e5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop5132"
+         style="stop-color:#ababab;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="86.132919"
+       y1="105.105"
+       x2="84.63858"
+       y2="20.895"
+       id="linearGradient3260"
+       xlink:href="#linearGradient5128"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(9.6142312e-2,0,0,9.6142312e-2,1.8468935,1.9430362)" />
+    <linearGradient
+       id="linearGradient3397">
+      <stop
+         id="stop3399"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3401"
+         style="stop-color:#8c8c8c;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21"
+       y1="0"
+       x2="21"
+       y2="16.004715"
+       id="linearGradient3264"
+       xlink:href="#linearGradient3397"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,0)" />
+    <linearGradient
+       x1="63.9995"
+       y1="3.1001"
+       x2="63.9995"
+       y2="122.8994"
+       id="linearGradient3309"
+       gradientUnits="userSpaceOnUse">
+      <stop
+         id="stop3311"
+         style="stop-color:#f6f6f6;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3313"
+         style="stop-color:#cccccc;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105"
+       id="linearGradient3262"
+       xlink:href="#linearGradient3309"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,0)" />
+    <linearGradient
+       id="linearGradient3678">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3680" />
+      <stop
+         style="stop-color:#e6e6e6;stop-opacity:1;"
+         offset="1"
+         id="stop3682" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3309-5"
+       id="linearGradient3066-2"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,0)"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105" />
+    <linearGradient
+       x1="63.9995"
+       y1="3.1001"
+       x2="63.9995"
+       y2="122.8994"
+       id="linearGradient3309-5"
+       gradientUnits="userSpaceOnUse">
+      <stop
+         id="stop3311-3"
+         style="stop-color:#f6f6f6;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3313-3"
+         style="stop-color:#cccccc;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3678"
+       id="linearGradient3920"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,-0.444)"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3678-0"
+       id="linearGradient3920-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,-0.444)"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105" />
+    <linearGradient
+       id="linearGradient3678-0">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3680-5" />
+      <stop
+         style="stop-color:#e6e6e6;stop-opacity:1;"
+         offset="1"
+         id="stop3682-7" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3784"
+       x1="0.5"
+       y1="7.5560002"
+       x2="15.5"
+       y2="7.5560002"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3810"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)"
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3813"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,10.462268,-5.974418)"
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3816"
+       gradientUnits="userSpaceOnUse"
+       x1="7.4930072"
+       y1="0.0035526801"
+       x2="7.4930072"
+       y2="14.998127" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3798"
+       x1="8.8461542"
+       y1="0.89504272"
+       x2="8.8461542"
+       y2="15.048951"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7-2"
+       id="linearGradient3798-6"
+       x1="8.8461542"
+       y1="0.89504272"
+       x2="8.8461542"
+       y2="15.048951"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7-2">
+      <stop
+         id="stop3589-9-2-2-6-2-9"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="translate(-1,-1)"
+       y2="15.048951"
+       x2="8.8461542"
+       y1="0.89504272"
+       x1="8.8461542"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3815"
+       xlink:href="#linearGradient3587-6-5-3-5-7-2"
+       inkscape:collect="always" />
+  </defs>
+  <path
+     inkscape:connector-curvature="0"
+     style="opacity:1;fill:#000000;fill-opacity:1;stroke:none"
+     d="M 8,1 C 4.1340071,1 1,4.134007 1,7.9999999 1,11.865994 4.1340071,15 8,15 11.865994,15 15,11.865994 15,7.9999999 15,4.134007 11.865994,1 8,1 z m 0.8020833,0.8932295 c 1.2010717,0.026708 2.2624547,0.7482116 3.3359377,1.2213541 L 13.869792,5.511719 13.596354,6.5416669 14.125,6.8697919 14.1159,8.0911458 c -0.0121,0.349374 0.005,0.6992101 -0.0091,1.048177 -0.166346,0.6623539 -0.550627,1.2665712 -0.875,1.8684892 -0.219888,0.108406 0.02005,-0.7185 -0.11849,-0.97526 C 13.14504,9.4386408 12.642422,9.4660089 12.302112,9.7955727 11.881413,10.041226 10.955977,10.114914 10.92581,9.4492183 10.687264,8.6490761 10.891165,7.7966268 11.217384,7.0520835 L 10.679623,6.3958335 10.87103,4.7096354 10.014259,3.8437502 10.21478,2.8958336 9.2121752,2.3307295 C 9.0145444,2.1755339 8.6384357,2.114115 8.5559252,1.902344 8.6372992,1.897654 8.7219474,1.891447 8.8020189,1.893224 z m -2.4609375,0.00912 c 0.031442,0.00459 0.069992,0.026431 0.1276042,0.072917 C 6.8067806,2.1608975 6.3863479,2.3716106 6.2864583,2.5677086 5.7466682,2.9328038 6.4524911,3.2318365 6.6875,3.5247398 7.0642392,3.4164892 7.4410308,2.8779535 7.9908854,3.0416669 8.6942527,2.8222093 8.5821719,3.630807 8.984375,3.9895836 9.036567,4.1585309 9.8643709,4.7080895 9.3671875,4.5273437 8.9577408,4.2098855 8.5022772,4.2337911 8.2096354,4.6914062 7.4187262,5.1199798 7.8867869,3.8662153 7.5078125,3.5611981 6.9348738,2.9219861 7.1750002,4.0387484 7.1067708,4.3723957 6.7342944,4.364267 6.0387231,4.0858224 5.6575521,4.5364583 L 6.03125,5.1471356 6.4778646,4.4635416 C 6.5864179,4.2161129 6.7226128,4.6558348 6.8424479,4.736979 6.9855355,5.0129459 7.6653536,5.4804485 7.1523438,5.6119794 6.3917179,6.0339397 5.7934201,6.6737624 5.1471354,7.2434895 4.9290953,7.7034971 4.4841468,7.6508764 4.2083333,7.2708332 3.5410706,6.8603335 3.5906422,7.9274218 3.625,8.3281249 l 0.5833333,-0.3645833 0,0.6015625 C 4.19179,8.6789089 4.2058787,8.7972867 4.1992147,8.9114582 3.790491,9.3384813 3.3785344,8.3120287 3.0234334,8.0820311 L 2.9960896,6.5781252 C 3.0089957,6.1556005 2.9197821,5.7229754 3.0052082,5.3111981 3.8089547,4.4486619 4.6253679,3.5550749 5.1015624,2.4583336 l 0.7838542,0 C 6.4331575,2.7236662 6.1210544,1.8701843 6.3411457,1.902344 z M 5.1835938,9.722656 c 0.095099,-0.010145 0.2032823,0.011573 0.3190103,0.072921 0.7379441,0.1056226 1.289687,0.640901 1.8776042,1.048178 0.4687224,0.464525 1.4828124,0.315782 1.5950521,1.102865 -0.1706086,0.853749 -1.0104785,1.312191 -1.75,1.61328 C 7.0406658,13.662851 6.8423351,13.744732 6.6328125,13.77865 5.9471995,13.950405 5.6507787,13.2474 5.5117188,12.721359 5.2012551,12.071255 4.4254987,11.578795 4.5364583,10.779953 4.5547311,10.382752 4.7714976,9.7666104 5.1835938,9.7226607 z"
+     id="path3002" />
+</svg>
diff --git a/core/img/actions/rename.png b/core/img/actions/rename.png
new file mode 100644
index 0000000000000000000000000000000000000000..9993a092df101d4b2796379ac6f1cbe62f131a3c
Binary files /dev/null and b/core/img/actions/rename.png differ
diff --git a/core/img/actions/rename.svg b/core/img/actions/rename.svg
new file mode 100644
index 0000000000000000000000000000000000000000..44b464c850f8559376e90f8b26395926a202fafa
--- /dev/null
+++ b/core/img/actions/rename.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="rename.svg"
+   inkscape:export-filename="/home/jancborchardt/rename.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="8.9609376"
+     inkscape:cx="13.152158"
+     inkscape:cy="4.0337477"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="M 12.59375 1.34375 C 12.0625 1.3125 11.5 1.5 11 2 L 14 5 C 15.5 3.5 14.1875 1.4375 12.59375 1.34375 z M 10 3 L 3 10 L 1 15 L 6 13 L 13 6 L 10 3 z M 3.5 10.5 L 5.5 12.5 L 3 14 L 2 13 L 3.5 10.5 z "
+       transform="translate(0,1036.3622)"
+       id="path3086" />
+  </g>
+</svg>
diff --git a/core/img/actions/search.png b/core/img/actions/search.png
new file mode 100644
index 0000000000000000000000000000000000000000..bfedb80bb578838260c22fc96b2f80ab3d318dbf
Binary files /dev/null and b/core/img/actions/search.png differ
diff --git a/core/img/actions/search.svg b/core/img/actions/search.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c8d9d848c4673e68b21d315eba37d5cc23f2243a
--- /dev/null
+++ b/core/img/actions/search.svg
@@ -0,0 +1,1632 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="icons-single.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/search.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="2.8284273"
+     inkscape:cx="-44.315999"
+     inkscape:cy="38.30965"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg11300">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g3252">
+    <g
+       id="layer1-4">
+      <path
+         inkscape:connector-curvature="0"
+         style="opacity:0.6;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="path3220-5"
+         d="m 6,1.9992355 c -2.7614237,0 -5,2.2385763 -5,5 0,2.7614237 2.2385763,4.9999995 5,4.9999995 0.9847834,0 1.8822735,-0.289667 2.65625,-0.78125 l 4.46875,4.625 c 0.09558,0.105267 0.226186,0.164519 0.375,0.15625 0.148816,-0.0083 0.303095,-0.07119 0.40625,-0.1875 l 0.9375,-1.0625 c 0.191938,-0.220889 0.195486,-0.535924 0,-0.71875 L 10.25,9.6242355 c 0.477599,-0.7663478 0.75,-1.6555164 0.75,-2.625 0,-2.7614237 -2.2385763,-5 -5,-5 z m 0,2 c 1.6568542,0 3,1.3431458 3,3 0,1.6568542 -1.3431458,3 -3,3 -1.6568542,0 -3,-1.3431458 -3,-3 0,-1.6568542 1.3431458,-3 3,-3 z" />
+      <path
+         inkscape:connector-curvature="0"
+         style="opacity:0.7;color:#000000;fill:url(#linearGradient3795-2);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="path3220"
+         d="M 6,1 C 3.2385763,1 1,3.2385763 1,6 c 0,2.7614237 2.2385763,5 5,5 0.9847834,0 1.8822735,-0.289667 2.65625,-0.78125 l 4.46875,4.625 c 0.09558,0.105267 0.226186,0.164519 0.375,0.15625 0.148816,-0.0083 0.303095,-0.07119 0.40625,-0.1875 l 0.9375,-1.0625 c 0.191938,-0.220889 0.195486,-0.535924 0,-0.71875 L 10.25,8.625 C 10.727599,7.8586522 11,6.9694836 11,6 11,3.2385763 8.7614237,1 6,1 z M 6,3 C 7.6568542,3 9,4.3431458 9,6 9,7.6568542 7.6568542,9 6,9 4.3431458,9 3,7.6568542 3,6 3,4.3431458 4.3431458,3 6,3 z" />
+    </g>
+  </g>
+</svg>
diff --git a/core/img/actions/settings.png b/core/img/actions/settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..5b1607e59fcdcc465ad42b8cd5ef4b15d66570a6
Binary files /dev/null and b/core/img/actions/settings.png differ
diff --git a/core/img/actions/settings.svg b/core/img/actions/settings.svg
new file mode 100644
index 0000000000000000000000000000000000000000..da685e8be0b6818909b4d896cf1848010cff65a5
--- /dev/null
+++ b/core/img/actions/settings.svg
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg2457"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="settings.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud-sharing/core/img/actions/settings.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata23">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview21"
+     showgrid="false"
+     inkscape:zoom="17.875"
+     inkscape:cx="-0.41568268"
+     inkscape:cy="1.2867133"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg2457" />
+  <defs
+     id="defs2459">
+    <linearGradient
+       id="linearGradient5128">
+      <stop
+         id="stop5130"
+         style="stop-color:#e5e5e5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop5132"
+         style="stop-color:#ababab;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="86.132919"
+       y1="105.105"
+       x2="84.63858"
+       y2="20.895"
+       id="linearGradient3260"
+       xlink:href="#linearGradient5128"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(9.6142312e-2,0,0,9.6142312e-2,1.8468935,1.9430362)" />
+    <linearGradient
+       id="linearGradient3397">
+      <stop
+         id="stop3399"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3401"
+         style="stop-color:#8c8c8c;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21"
+       y1="0"
+       x2="21"
+       y2="16.004715"
+       id="linearGradient3264"
+       xlink:href="#linearGradient3397"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,0)" />
+    <linearGradient
+       x1="63.9995"
+       y1="3.1001"
+       x2="63.9995"
+       y2="122.8994"
+       id="linearGradient3309"
+       gradientUnits="userSpaceOnUse">
+      <stop
+         id="stop3311"
+         style="stop-color:#f6f6f6;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3313"
+         style="stop-color:#cccccc;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105"
+       id="linearGradient3262"
+       xlink:href="#linearGradient3309"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,0)" />
+    <linearGradient
+       id="linearGradient3678">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3680" />
+      <stop
+         style="stop-color:#e6e6e6;stop-opacity:1;"
+         offset="1"
+         id="stop3682" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3309-5"
+       id="linearGradient3066-2"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,0)"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105" />
+    <linearGradient
+       x1="63.9995"
+       y1="3.1001"
+       x2="63.9995"
+       y2="122.8994"
+       id="linearGradient3309-5"
+       gradientUnits="userSpaceOnUse">
+      <stop
+         id="stop3311-3"
+         style="stop-color:#f6f6f6;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3313-3"
+         style="stop-color:#cccccc;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3678"
+       id="linearGradient3920"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,-0.444)"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3678-0"
+       id="linearGradient3920-6"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-17.058189,-0.444)"
+       x1="25"
+       y1="0"
+       x2="25"
+       y2="16.000105" />
+    <linearGradient
+       id="linearGradient3678-0">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0"
+         id="stop3680-5" />
+      <stop
+         style="stop-color:#e6e6e6;stop-opacity:1;"
+         offset="1"
+         id="stop3682-7" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3784"
+       x1="0.5"
+       y1="7.5560002"
+       x2="15.5"
+       y2="7.5560002"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3810"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)"
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3813"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,10.462268,-5.974418)"
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       id="linearGradient3816"
+       gradientUnits="userSpaceOnUse"
+       x1="7.4930072"
+       y1="0.0035526801"
+       x2="7.4930072"
+       y2="14.998127" />
+  </defs>
+  <g
+     transform="translate(0.027972,0.944)"
+     id="g2479-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1">
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994000000003;marker:none;visibility:visible;display:block;overflow:visible;enable-background:accumulate"
+       id="path2426-2"
+       d="M 6.9375,0.056 C 6.6890831,0.056 6.5,0.245082 6.5,0.4935 l 0,1.25 C 5.9460971,1.885701 5.448836,2.115378 4.96875,2.39975 L 4.0625,1.4935 c -0.1756569,-0.175658 -0.449342,-0.175658 -0.625,0 l -1.5,1.5 c -0.175657,0.175658 -0.175657,0.449342 0,0.625 L 2.84375,4.52475 C 2.5593789,5.004836 2.329701,5.502097 2.1875,6.056 l -1.25,0 C 0.68908301,6.056 0.5,6.245082 0.5,6.4935 l 0,2.125 c 1e-8,0.248418 0.189083,0.4375 0.4375,0.4375 l 1.25,0 c 0.1422009,0.553903 0.371879,1.051164 0.65625,1.53125 L 1.9375,11.4935 c -0.175657,0.175658 -0.175657,0.449342 0,0.625 l 1.5,1.5 c 0.1756579,0.175658 0.449342,0.175658 0.625,0 L 4.96875,12.71225 C 5.4488361,12.996622 5.946097,13.2263 6.5,13.3685 l 0,1.25 c 1e-7,0.248418 0.189083,0.4375 0.4375,0.4375 l 2.125,0 C 9.3109176,15.056 9.5,14.866918 9.5,14.6185 l 0,-1.25 c 0.553903,-0.1422 1.051164,-0.371878 1.53125,-0.65625 l 0.90625,0.90625 c 0.175658,0.175658 0.449342,0.175658 0.625,0 l 1.5,-1.5 c 0.175658,-0.175658 0.175658,-0.449342 0,-0.625 L 13.15625,10.58725 C 13.440622,10.107164 13.6703,9.609903 13.8125,9.056 l 1.25,0 C 15.310918,9.056 15.5,8.866917 15.5,8.6185 l 0,-2.125 C 15.5,6.245082 15.310917,6.056 15.0625,6.056 l -1.25,0 C 13.6703,5.502097 13.440622,5.004836 13.15625,4.52475 L 14.0625,3.6185 c 0.175658,-0.175658 0.175658,-0.449342 0,-0.625 l -1.5,-1.5 c -0.175658,-0.175658 -0.449342,-0.175658 -0.625,0 L 11.03125,2.39975 C 10.551164,2.115378 10.053903,1.885701 9.5,1.7435 l 0,-1.25 C 9.4999996,0.245082 9.310918,0.056 9.0625,0.056 z M 8,4.2133427 c 1.8451469,0 3.342657,1.4975104 3.342657,3.3426573 0,1.8451469 -1.4975101,3.342657 -3.342657,3.342657 C 6.1548531,10.898657 4.6573427,9.4011469 4.6573427,7.556 4.6573427,5.7108531 6.1548531,4.2133427 8,4.2133427 z"
+       sodipodi:nodetypes="ssccssssccssssccssssccssssccssssccssssccssssccssssssss" />
+  </g>
+  <g
+     id="g2479"
+     transform="translate(0,-0.056)"
+     style="fill-opacity:1;fill:url(#linearGradient3784);opacity:0.7">
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:url(#linearGradient3816);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99999994000000003;marker:none;visibility:visible;display:block;overflow:visible;enable-background:accumulate"
+       id="path2426"
+       d="M 6.9375,0.056 C 6.6890831,0.056 6.5,0.245082 6.5,0.4935 l 0,1.25 C 5.9460971,1.885701 5.448836,2.115378 4.96875,2.39975 L 4.0625,1.4935 c -0.1756569,-0.175658 -0.449342,-0.175658 -0.625,0 l -1.5,1.5 c -0.175657,0.175658 -0.175657,0.449342 0,0.625 L 2.84375,4.52475 C 2.5593789,5.004836 2.329701,5.502097 2.1875,6.056 l -1.25,0 C 0.68908301,6.056 0.5,6.245082 0.5,6.4935 l 0,2.125 c 1e-8,0.248418 0.189083,0.4375 0.4375,0.4375 l 1.25,0 c 0.1422009,0.553903 0.371879,1.051164 0.65625,1.53125 L 1.9375,11.4935 c -0.175657,0.175658 -0.175657,0.449342 0,0.625 l 1.5,1.5 c 0.1756579,0.175658 0.449342,0.175658 0.625,0 L 4.96875,12.71225 C 5.4488361,12.996622 5.946097,13.2263 6.5,13.3685 l 0,1.25 c 1e-7,0.248418 0.189083,0.4375 0.4375,0.4375 l 2.125,0 C 9.3109176,15.056 9.5,14.866918 9.5,14.6185 l 0,-1.25 c 0.553903,-0.1422 1.051164,-0.371878 1.53125,-0.65625 l 0.90625,0.90625 c 0.175658,0.175658 0.449342,0.175658 0.625,0 l 1.5,-1.5 c 0.175658,-0.175658 0.175658,-0.449342 0,-0.625 L 13.15625,10.58725 C 13.440622,10.107164 13.6703,9.609903 13.8125,9.056 l 1.25,0 C 15.310918,9.056 15.5,8.866917 15.5,8.6185 l 0,-2.125 C 15.5,6.245082 15.310917,6.056 15.0625,6.056 l -1.25,0 C 13.6703,5.502097 13.440622,5.004836 13.15625,4.52475 L 14.0625,3.6185 c 0.175658,-0.175658 0.175658,-0.449342 0,-0.625 l -1.5,-1.5 c -0.175658,-0.175658 -0.449342,-0.175658 -0.625,0 L 11.03125,2.39975 C 10.551164,2.115378 10.053903,1.885701 9.5,1.7435 l 0,-1.25 C 9.4999996,0.245082 9.310918,0.056 9.0625,0.056 z M 8,4.2133427 c 1.8451469,0 3.342657,1.4975104 3.342657,3.3426573 0,1.8451469 -1.4975101,3.342657 -3.342657,3.342657 C 6.1548531,10.898657 4.6573427,9.4011469 4.6573427,7.556 4.6573427,5.7108531 6.1548531,4.2133427 8,4.2133427 z"
+       sodipodi:nodetypes="ssccssssccssssccssssccssssccssssccssssccssssccssssssss" />
+  </g>
+</svg>
diff --git a/core/img/actions/share.png b/core/img/actions/share.png
new file mode 100644
index 0000000000000000000000000000000000000000..62c4627f317dc007a1186852c9df294bf1ed34a7
Binary files /dev/null and b/core/img/actions/share.png differ
diff --git a/core/img/actions/share.svg b/core/img/actions/share.svg
new file mode 100644
index 0000000000000000000000000000000000000000..a5f2f8cb4d2a4e745ac14e8b706764538ec3c34e
--- /dev/null
+++ b/core/img/actions/share.svg
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="share.svg">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="7.919596"
+     inkscape:cx="-4.1004833"
+     inkscape:cy="12.085717"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid2985"
+       empspacing="8"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       dotted="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;overflow:visible"
+       d="m 12.227825,1037.3622 c -1.356528,0 -2.45919,1.0977 -2.45919,2.4542 0,0.075 0.0084,0.1504 0.0149,0.2236 l -4.734564,2.4145 c -0.429101,-0.3667 -0.986112,-0.5863 -1.594748,-0.5863 -1.356527,0 -2.454223,1.0977 -2.454223,2.4543 0,1.3565 1.097696,2.4542 2.454223,2.4542 0.546067,0 1.052763,-0.1755 1.46061,-0.477 l 4.863734,2.4741 c -0.0024,0.044 -0.0099,0.089 -0.0099,0.1342 0,1.3565 1.102663,2.4542 2.45919,2.4542 1.356527,0 2.454223,-1.0977 2.454223,-2.4542 0,-1.3565 -1.097696,-2.4592 -2.454223,-2.4592 -0.636532,0 -1.218019,0.2437 -1.654365,0.6409 L 5.878678,1044.7 c 0.01892,-0.1228 0.03478,-0.2494 0.03478,-0.3775 0,-0.072 -0.0089,-0.1437 -0.0149,-0.2137 l 4.73953,-2.4145 c 0.428025,0.3627 0.984876,0.5813 1.58978,0.5813 1.356527,0 2.454223,-1.1027 2.454223,-2.4592 0,-1.3565 -1.097696,-2.4542 -2.454223,-2.4542 z"
+       id="circle54"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/core/img/actions/shared.png b/core/img/actions/shared.png
new file mode 100644
index 0000000000000000000000000000000000000000..073ff741685cad87e4505cc0a7aef199d99d0140
Binary files /dev/null and b/core/img/actions/shared.png differ
diff --git a/core/img/actions/shared.svg b/core/img/actions/shared.svg
new file mode 100644
index 0000000000000000000000000000000000000000..2302cc98919cbf01035c654ca0716fcfeab03b87
--- /dev/null
+++ b/core/img/actions/shared.svg
@@ -0,0 +1,1738 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="users.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/personal.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="5.8712803"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3417"
+       xlink:href="#linearGradient3587-6-5-10"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="20.074369"
+       x2="14.152531"
+       y1="-1.4095211"
+       x1="14.501121"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3437"
+       xlink:href="#linearGradient3587-6-5-19"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4127"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <path
+       inkscape:connector-curvature="0"
+       id="path2880-5-3-9-2"
+       style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:1;color:#000000;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00012147000000007;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+       d="m 4.5688738,2.4830511 c -0.9648083,0 -1.7832921,0.7055933 -1.7832921,1.6161756 0.00685,0.2878093 0.032588,0.6427159 0.2043359,1.3932536 l 0,0.018581 0.018574,0.018573 c 0.055135,0.1579316 0.1353681,0.2482737 0.2414872,0.3715352 0.1061191,0.1232607 0.2326347,0.2683371 0.3529435,0.3901106 0.014154,0.014326 0.023227,0.023201 0.037149,0.037163 0.023859,0.10383 0.052763,0.2155718 0.074304,0.3158043 0.057317,0.2666774 0.051439,0.4555265 0.037155,0.5201477 C 3.3369413,7.3099685 2.8211499,7.4833329 2.3583332,7.6845423 2.098497,7.797507 1.8633735,7.8983808 1.6710221,8.0189236 1.4786714,8.1394673 1.2873756,8.2305391 1.2251996,8.501919 c -7.972e-4,0.012367 -7.972e-4,0.024787 0,0.037163 -0.060756,0.5578815 -0.1526614,1.3782525 -0.2229116,1.931979 -0.0151658,0.116557 0.046264,0.239427 0.1486075,0.297228 0.8403342,0.453932 2.131186,0.636628 3.4179767,0.631608 1.2867901,-0.005 2.5673618,-0.198451 3.3808242,-0.631608 0.1023419,-0.0578 0.1637753,-0.180671 0.1486076,-0.297228 C 8.0758756,10.297976 8.0483234,9.9076775 8.0240004,9.5236465 7.9996757,9.1396165 7.9785611,8.761852 7.9496986,8.5390808 7.9396279,8.4838569 7.9135213,8.4316576 7.8753932,8.3904667 7.6169852,8.0818878 7.2309122,7.8932484 6.7794115,7.7031288 6.3672181,7.5295608 5.8839866,7.3493151 5.4047914,7.1458272 c -0.02682,-0.059748 -0.053461,-0.233582 0,-0.5015715 0.014356,-0.071959 0.036836,-0.1490327 0.055729,-0.2229212 0.045032,-0.05044 0.080132,-0.091658 0.1300317,-0.1486132 C 5.6969774,6.1512485 5.8113313,6.023821 5.9063431,5.9011869 6.0013555,5.7785519 6.0790889,5.6733465 6.1292547,5.5296516 l 0.018574,-0.018581 C 6.3419943,4.7274034 6.3420955,4.4003931 6.352165,4.1178159 l 0,-0.018573 c 0,-0.9105811 -0.818482,-1.6161744 -1.7832922,-1.6161744 z M 9.6699343,0.99999453 c -1.4066511,0 -2.5999668,1.02868367 -2.5999668,2.35622057 0.00998,0.4195963 0.047512,0.9370148 0.2979133,2.0312236 l 0,0.027083 0.027081,0.027083 c 0.080384,0.230248 0.1973613,0.3619578 0.3520786,0.54166 0.1547174,0.1797023 0.3391718,0.3912086 0.514577,0.5687425 0.020637,0.020887 0.033864,0.033826 0.054161,0.054175 0.034785,0.1513735 0.076926,0.3142816 0.1083314,0.4604109 0.083566,0.3887889 0.074995,0.6641113 0.054171,0.758323 C 7.8738277,8.0371487 7.121825,8.2898971 6.4470573,8.58324 6.0682268,8.7479308 5.7254263,8.8949953 5.4449859,9.0707345 c -0.2804394,0.17574 -0.5593407,0.308514 -0.6499909,0.704158 -0.00116,0.01804 -0.00116,0.03613 0,0.05418 -0.08858,0.8133375 -0.2225741,2.0093535 -0.324996,2.8166315 -0.022111,0.169929 0.067452,0.349061 0.2166637,0.433328 1.2251728,0.661787 3.1071818,0.92814 4.9832698,0.920822 1.8760865,-0.0073 3.7431075,-0.289321 4.9291025,-0.920822 0.14921,-0.08427 0.238778,-0.263399 0.216664,-0.433328 -0.0327,-0.252341 -0.07287,-0.821355 -0.108332,-1.381233 -0.03546,-0.559878 -0.06625,-1.11062 -0.108329,-1.4353995 -0.01468,-0.0805 -0.05274,-0.156611 -0.108333,-0.216664 C 14.113957,9.1625255 13.551078,8.887509 12.892809,8.6103329 12.291848,8.3572881 11.587316,8.094509 10.888669,7.7978433 c -0.0391,-0.087106 -0.07795,-0.3405381 0,-0.7312406 0.02093,-0.1049108 0.05371,-0.2172748 0.08125,-0.3249962 0.06566,-0.073537 0.116829,-0.1336294 0.189581,-0.2166638 0.155164,-0.1770945 0.321887,-0.3628712 0.46041,-0.5416592 0.138524,-0.1787888 0.251856,-0.3321687 0.324996,-0.5416609 l 0.02708,-0.027083 c 0.283087,-1.1425075 0.283235,-1.6192551 0.297916,-2.0312247 l 0,-0.027083 c 0,-1.3275361 -1.193313,-2.3562198 -2.5999669,-2.3562198 z" />
+  </g>
+</svg>
diff --git a/core/img/actions/sound-off.png b/core/img/actions/sound-off.png
new file mode 100644
index 0000000000000000000000000000000000000000..7900e500c90f1650c2299aad837114e88c9ee71d
Binary files /dev/null and b/core/img/actions/sound-off.png differ
diff --git a/core/img/actions/sound-off.svg b/core/img/actions/sound-off.svg
new file mode 100644
index 0000000000000000000000000000000000000000..053291311faa708c2ba53dc8885d4e15ef9b7238
--- /dev/null
+++ b/core/img/actions/sound-off.svg
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="sound.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/sound.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="3.1681698"
+     inkscape:cx="17.017228"
+     inkscape:cy="33.367762"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 1,1042.3622 0,4 3,0 3,3 1,0 0,-10 -1,0 -3,3 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+  </g>
+</svg>
diff --git a/core/img/actions/sound.png b/core/img/actions/sound.png
new file mode 100644
index 0000000000000000000000000000000000000000..838c9cee1719b158efdc40d2688f33ac0f28da19
Binary files /dev/null and b/core/img/actions/sound.png differ
diff --git a/core/img/actions/sound.svg b/core/img/actions/sound.svg
new file mode 100644
index 0000000000000000000000000000000000000000..6feea076a4448d68ef91feef5152168091fc8d28
--- /dev/null
+++ b/core/img/actions/sound.svg
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="16"
+   height="16"
+   id="svg3875"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="sound-off.svg"
+   inkscape:export-filename="/home/jancborchardt/owncloud/core/img/actions/pause.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs3877" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="3.1681698"
+     inkscape:cx="17.017228"
+     inkscape:cy="33.367762"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3883"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata3880">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1036.3622)">
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 1,1042.3622 0,4 3,0 3,3 1,0 0,-10 -1,0 -3,3 z"
+       id="path3086"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:#000000;fill-opacity:1;stroke:none"
+       d="m 13.25,1039.3622 -0.78125,0.625 c 0.965189,1.1998 1.53125,2.7153 1.53125,4.375 0,1.6597 -0.566061,3.1752 -1.53125,4.375 l 0.78125,0.625 c 1.098257,-1.3697 1.75,-3.1078 1.75,-5 0,-1.8922 -0.651743,-3.6303 -1.75,-5 z m -1.5625,1.25 -0.8125,0.6563 c 0.687912,0.8565 1.125,1.9096 1.125,3.0937 0,1.1841 -0.437088,2.2372 -1.125,3.0938 l 0.8125,0.6562 c 0.823121,-1.0271 1.3125,-2.3314 1.3125,-3.75 0,-1.4186 -0.489379,-2.7229 -1.3125,-3.75 z m -1.5625,1.25 -0.78125,0.625 c 0.411273,0.5135 0.65625,1.1659 0.65625,1.875 0,0.7091 -0.244977,1.3615 -0.65625,1.875 l 0.78125,0.625 c 0.545316,-0.6836 0.875,-1.5576 0.875,-2.5 0,-0.9424 -0.329684,-1.8164 -0.875,-2.5 z"
+       id="path3105"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/core/img/breadcrumb-start.png b/core/img/breadcrumb-start.png
new file mode 100644
index 0000000000000000000000000000000000000000..a79d675454e19925309f65c583f13fe78ea54c57
Binary files /dev/null and b/core/img/breadcrumb-start.png differ
diff --git a/core/img/breadcrumb-start.svg b/core/img/breadcrumb-start.svg
new file mode 100644
index 0000000000000000000000000000000000000000..4197763dc6ca678f1cb2d032e51427925fb81212
--- /dev/null
+++ b/core/img/breadcrumb-start.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="11"
+   height="36"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="New document 1">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="18.657977"
+     inkscape:cx="2.6788772"
+     inkscape:cy="16.807262"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3760"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1016.3622)">
+    <path
+       transform="translate(0,1016.3622)"
+       style="fill:#dddddd;fill-opacity:1;stroke:none"
+       d="M 0,0 11,18 0,36 z"
+       id="rect3757"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="cccc" />
+  </g>
+</svg>
diff --git a/core/img/breadcrumb.png b/core/img/breadcrumb.png
new file mode 100644
index 0000000000000000000000000000000000000000..b124f349f56e7da0e9975c0e888cf8e39295783f
Binary files /dev/null and b/core/img/breadcrumb.png differ
diff --git a/core/img/breadcrumb.svg b/core/img/breadcrumb.svg
new file mode 100644
index 0000000000000000000000000000000000000000..9d522b42b73ac7918b39f7c2df3c585573097cc9
--- /dev/null
+++ b/core/img/breadcrumb.svg
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="11"
+   height="36"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="breadcrumb.svg"
+   inkscape:export-filename="/home/jancborchardt/breadcrumb.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="26.386364"
+     inkscape:cx="1.7139473"
+     inkscape:cy="25.655289"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid3760"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-1016.3622)">
+    <path
+       transform="translate(0,1016.3622)"
+       style="fill:#dddddd;fill-opacity:1;stroke:#dddddd;stroke-width:0.89999998000000003;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:31.20000076000000178;stroke-opacity:1;stroke-dasharray:none;marker-start:none"
+       d="m 0.5,0 10,18 -10,18 10,-18 z"
+       id="rect3757"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="ccccc"
+       inkscape:export-filename="/home/jancborchardt/breadcrumb-start.png"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90" />
+  </g>
+</svg>
diff --git a/core/img/favicon-touch.png b/core/img/favicon-touch.png
new file mode 100644
index 0000000000000000000000000000000000000000..20af826523cfdb9da47ba1208c81df38ecb3973c
Binary files /dev/null and b/core/img/favicon-touch.png differ
diff --git a/core/img/favicon.png b/core/img/favicon.png
new file mode 100644
index 0000000000000000000000000000000000000000..a7ee766dfa8931c9a707520176aa02aee76bb7b0
Binary files /dev/null and b/core/img/favicon.png differ
diff --git a/core/img/filetypes/audio.png b/core/img/filetypes/audio.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c844425d644f2c6e83e2962ec6c4dd201e3100e
Binary files /dev/null and b/core/img/filetypes/audio.png differ
diff --git a/core/img/filetypes/audio.svg b/core/img/filetypes/audio.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1f397660970e1e6e310c888f0c4b7973a6f4fd5a
--- /dev/null
+++ b/core/img/filetypes/audio.svg
@@ -0,0 +1,1670 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="search.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/search.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="-2.2078397"
+     inkscape:cy="6.5568429"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg11300">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       y2="15"
+       x2="9"
+       y1="0"
+       x1="9"
+       spreadMethod="pad"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3374"
+       xlink:href="#linearGradient4136-9"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient3457"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <path
+       style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:none"
+       id="path4031"
+       d="m 13.36218,1.0010808 c -2.357103,0.379835 -5.5116295,0.770846 -7.8621805,1.1718941 -0.622438,0.303692 -0.482505,1.085702 -0.510043,1.653123 0,2.531942 0,5.063885 0,7.5958261 -1.311726,-0.246608 -2.911355,0.837273 -2.980733,2.553424 -0.113252,1.068438 0.834207,1.998453 1.864482,2.023461 1.844664,0.04478 3.106285,-1.179076 3.126294,-2.726857 -0.02014,-2.599407 0.0057,-5.1998371 0,-7.7996441 0.285577,-0.02115 4.6110295,-0.846503 4.9999995,-0.901457 0,1.947147 0,3.894295 0,5.8414421 -1.146387,-0.274636 -2.6118965,0.478721 -2.9752685,2.017253 -0.15337,0.862385 0.136496,1.944831 1.0115455,2.309984 1.708237,0.784936 4.053268,-0.805037 3.957931,-2.671065 -0.02646,-3.4661721 0.02169,-6.9337631 0,-10.4003062 C 13.949347,1.3424008 13.7286,0.97756684 13.36218,1.0010808 z"
+       inkscape:connector-curvature="0" />
+    <path
+       style="opacity:0.7;fill:url(#linearGradient3457);fill-opacity:1;fill-rule:nonzero;stroke:none"
+       id="path4031-1"
+       d="m 13.36218,0.00108226 c -2.357103,0.379835 -5.5116293,0.770846 -7.8621803,1.17189404 -0.6224381,0.303692 -0.4825051,1.085702 -0.5100431,1.653123 0,2.531942 0,5.063885 0,7.5958257 -1.3117261,-0.246608 -2.9113551,0.837272 -2.9807331,2.553423 -0.113252,1.068438 0.834207,1.998453 1.864482,2.023461 1.8446642,0.04478 3.1062852,-1.179076 3.1262942,-2.726857 -0.02014,-2.5994053 0.0057,-5.1998357 0,-7.7996427 0.285577,-0.02115 4.6110293,-0.846503 4.9999993,-0.901457 0,1.947147 0,3.894295 0,5.8414424 -1.146387,-0.274636 -2.6118963,0.478721 -2.9752683,2.0172513 -0.15337,0.862385 0.136496,1.944831 1.0115453,2.309984 1.708237,0.784936 4.053268,-0.805037 3.957931,-2.671065 -0.02646,-3.4661707 0.02169,-6.9337617 0,-10.40030474 -0.04486,-0.325758 -0.265607,-0.69059202 -0.632027,-0.667078 z"
+       inkscape:connector-curvature="0" />
+  </g>
+</svg>
diff --git a/core/img/filetypes/file.png b/core/img/filetypes/file.png
new file mode 100644
index 0000000000000000000000000000000000000000..4979044889708bf736aefaee7ade0b1f4462600e
Binary files /dev/null and b/core/img/filetypes/file.png differ
diff --git a/core/img/filetypes/file.svg b/core/img/filetypes/file.svg
new file mode 100644
index 0000000000000000000000000000000000000000..478714b75d105735a987b4ea92d6447014c8c673
--- /dev/null
+++ b/core/img/filetypes/file.svg
@@ -0,0 +1,1841 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="folder.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/folder.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-4"
+       id="linearGradient4357-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       id="linearGradient3587-6-5-4">
+      <stop
+         id="stop3589-9-2-1"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-04"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-26"
+       id="linearGradient4566-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       id="linearGradient3587-6-5-26">
+      <stop
+         id="stop3589-9-2-45"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-20"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       id="linearGradient4580-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-1"
+       id="linearGradient4578-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-1">
+      <stop
+         id="stop3589-9-2-8-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="1013.451"
+       x2="209.34245"
+       y1="998.45801"
+       x1="209.34245"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3528"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-4"
+       id="linearGradient3335-2"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-4">
+      <stop
+         id="stop3589-9-2-8-7-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-6"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3567"
+       xlink:href="#linearGradient3587-6-5-2-4-4"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       transform="translate(-3.1068569e-8,20)"
+       id="g3546">
+      <path
+         sodipodi:nodetypes="ccccccccccccscccccssss"
+         inkscape:connector-curvature="0"
+         id="path4160-9-8"
+         d="m 3.3500571,-17.998043 c -0.1974696,0.03825 -0.3535533,0.233327 -0.3499974,0.437439 l 0,13.123165 c 4.7e-6,0.229041 0.205219,0.437433 0.430766,0.437439 l 10.1387373,0 c 0.225547,-6e-6 0.43076,-0.208398 0.430766,-0.437439 l 0,-10.143113 c -0.0033,-0.06685 -0.02179,-0.132893 -0.05384,-0.191379 -0.965556,-1.389642 -2.035099,-2.419138 -3.311513,-3.198772 -0.04304,-0.01632 -0.08869,-0.02559 -0.134615,-0.02734 l -7.0695295,0 c -0.026843,-0.0026 -0.053928,-0.0026 -0.080774,0 z M 9.0000021,-15.5 c 0,-0.235702 0.2642971,-0.5 0.4999991,-0.5 0.235702,0 0.2493941,0 0.4999988,0 0,0.24431 0,2 0,2 0,0 1.684071,0 2,0 0,0.287357 0,0.264298 0,0.5 0,0.235702 -0.264298,0.5 -0.5,0.5 -0.401508,0 -1.438744,0 -1.9999998,0 -0.2357023,0 -0.5,-0.264298 -0.5,-0.5 0,-0.464108 1.9e-6,-1.491737 1.9e-6,-2 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.6;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99992162;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+      <path
+         sodipodi:nodetypes="ccccccccccccscccccssss"
+         inkscape:connector-curvature="0"
+         id="path4160-9"
+         d="m 3.350057,-18.998043 c -0.1974696,0.03825 -0.3535533,0.233327 -0.3499974,0.437439 l 0,13.123165 c 4.7e-6,0.229041 0.205219,0.437433 0.430766,0.437439 l 10.1387374,0 c 0.225547,-6e-6 0.43076,-0.208398 0.430766,-0.437439 l 0,-10.143113 c -0.0033,-0.06685 -0.02179,-0.132893 -0.05384,-0.191379 -0.965556,-1.389642 -2.035099,-2.419138 -3.311513,-3.198772 -0.04304,-0.01632 -0.08869,-0.02559 -0.134615,-0.02734 l -7.0695296,0 c -0.026843,-0.0026 -0.053928,-0.0026 -0.080774,0 z M 9.000002,-16.5 c 0,-0.235702 0.2642971,-0.5 0.4999991,-0.5 0.235702,0 0.2493941,0 0.4999989,0 0,0.24431 0,2 0,2 0,0 1.684071,0 2,0 0,0.287357 0,0.264298 0,0.5 0,0.235702 -0.264298,0.5 -0.5,0.5 -0.401508,0 -1.438744,0 -1.9999999,0 -0.2357023,0 -0.5,-0.264298 -0.5,-0.5 0,-0.464108 1.9e-6,-1.491737 1.9e-6,-2 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient3567);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99992162;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+    </g>
+  </g>
+</svg>
diff --git a/core/img/filetypes/image.png b/core/img/filetypes/image.png
new file mode 100644
index 0000000000000000000000000000000000000000..980a7c6981311787a5c33102fb23d0157103b65f
Binary files /dev/null and b/core/img/filetypes/image.png differ
diff --git a/core/img/filetypes/image.svg b/core/img/filetypes/image.svg
new file mode 100644
index 0000000000000000000000000000000000000000..26c3d6312c2c15c1f9229b34659ed94291d25740
--- /dev/null
+++ b/core/img/filetypes/image.svg
@@ -0,0 +1,1697 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="audio.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/audio.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient3457"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="47.208389"
+       x2="-39.421574"
+       y1="-5.2547116"
+       x1="-39.421574"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3397"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       transform="translate(-39.999799,1)"
+       id="g3376">
+      <path
+         sodipodi:nodetypes="cccccccccccccc"
+         inkscape:connector-curvature="0"
+         id="path4160-9-9-0"
+         d="m 43.35011,1.0020512 c -0.197474,0.03825 -0.35356,0.233327 -0.350004,0.437439 l -3.07e-4,13.1230708 c 4e-6,0.229041 0.205223,0.437433 0.430774,0.437439 l 10.14024,0 c 0.225551,-6e-6 0.430768,-0.208398 0.430774,-0.437439 l 3.07e-4,-12.9606108 c -3.29e-4,-0.336436 -0.265499,-0.601856 -0.516871,-0.599899 0,0 -7.760335,0 -10.134913,0 z M 44,11 l 9,0 4.13e-4,3 L 44,14 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.6;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99992162;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+      <path
+         sodipodi:nodetypes="cccccccccccccc"
+         inkscape:connector-curvature="0"
+         id="path4160-9-9-8"
+         d="m 43.35011,0.0020508 c -0.197474,0.03825 -0.35356,0.233327 -0.350004,0.437439 l -3.07e-4,13.1230712 c 4e-6,0.229041 0.205223,0.437433 0.430774,0.437439 l 10.14024,0 c 0.225551,-6e-6 0.430768,-0.208398 0.430774,-0.437439 L 54.001894,0.6019496 C 54.001565,0.265514 53.736395,9.38e-5 53.485023,0.0020508 c 0,0 -7.760335,0 -10.134913,0 z M 44,10 l 9,0 4.13e-4,3 L 44,13 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient3397);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.99992162;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+    </g>
+  </g>
+</svg>
diff --git a/core/img/jquery-tipsy.gif b/core/img/jquery-tipsy.gif
new file mode 100644
index 0000000000000000000000000000000000000000..eb7718dfc168c3c63382c36c55e0fc3ab53974c1
Binary files /dev/null and b/core/img/jquery-tipsy.gif differ
diff --git a/core/img/loading.gif b/core/img/loading.gif
new file mode 100644
index 0000000000000000000000000000000000000000..5b33f7e54f4e55b6b8774d86d96895db9af044b4
Binary files /dev/null and b/core/img/loading.gif differ
diff --git a/core/img/logo-wide.png b/core/img/logo-wide.png
new file mode 100644
index 0000000000000000000000000000000000000000..b2c16a0f60a6991eb6a98109d017b48e3b3a3a3b
Binary files /dev/null and b/core/img/logo-wide.png differ
diff --git a/core/img/logo-wide.svg b/core/img/logo-wide.svg
new file mode 100644
index 0000000000000000000000000000000000000000..73b37cc7aaaeadf755a5424e48ea48d26508c7a4
--- /dev/null
+++ b/core/img/logo-wide.svg
@@ -0,0 +1,875 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948)  -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   id="Layer_1"
+   x="0px"
+   y="0px"
+   width="140"
+   height="32"
+   viewBox="0 0 139.99999 32"
+   enable-background="new 0 0 595.275 311.111"
+   xml:space="preserve"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="logo-wide.svg"><metadata
+   id="metadata327"><rdf:RDF><cc:Work
+       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+   id="defs325"><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_1_"
+   id="linearGradient3353"
+   gradientUnits="userSpaceOnUse"
+   x1="288.49411"
+   y1="55.888199"
+   x2="288.49411"
+   y2="339.22189" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_2_"
+   id="linearGradient3355"
+   gradientUnits="userSpaceOnUse"
+   x1="251.2114"
+   y1="55.888199"
+   x2="251.2114"
+   y2="339.22159" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_3_"
+   id="linearGradient3357"
+   gradientUnits="userSpaceOnUse"
+   x1="293.22461"
+   y1="55.888199"
+   x2="293.22461"
+   y2="339.22171" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_4_"
+   id="linearGradient3359"
+   gradientUnits="userSpaceOnUse"
+   x1="375.33401"
+   y1="55.888199"
+   x2="375.33401"
+   y2="339.22159" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_5_"
+   id="linearGradient3361"
+   gradientUnits="userSpaceOnUse"
+   x1="334.49411"
+   y1="55.888199"
+   x2="334.49411"
+   y2="339.22159" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_6_"
+   id="linearGradient3363"
+   gradientUnits="userSpaceOnUse"
+   x1="458.42679"
+   y1="55.8867"
+   x2="458.42679"
+   y2="339.2236" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_7_"
+   id="linearGradient3365"
+   gradientUnits="userSpaceOnUse"
+   x1="413.16309"
+   y1="55.888199"
+   x2="413.16309"
+   y2="339.22131" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_8_"
+   id="linearGradient3367"
+   gradientUnits="userSpaceOnUse"
+   x1="290.76169"
+   y1="55.8867"
+   x2="290.76169"
+   y2="339.2236" /><linearGradient
+   inkscape:collect="always"
+   xlink:href="#SVGID_9_"
+   id="linearGradient3369"
+   gradientUnits="userSpaceOnUse"
+   x1="346.77341"
+   y1="55.888199"
+   x2="346.77341"
+   y2="339.22119" />
+	<linearGradient
+   y2="339.22189"
+   x2="288.49411"
+   y1="55.888199"
+   x1="288.49411"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_1_">
+		<stop
+   id="stop261"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop263"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.22159"
+   x2="251.2114"
+   y1="55.888199"
+   x1="251.2114"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_2_">
+		<stop
+   id="stop268"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop270"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.22171"
+   x2="293.22461"
+   y1="55.888199"
+   x1="293.22461"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_3_">
+		<stop
+   id="stop275"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop277"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.22159"
+   x2="375.33401"
+   y1="55.888199"
+   x1="375.33401"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_4_">
+		<stop
+   id="stop282"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop284"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.22159"
+   x2="334.49411"
+   y1="55.888199"
+   x1="334.49411"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_5_">
+		<stop
+   id="stop289"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop291"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.2236"
+   x2="458.42679"
+   y1="55.8867"
+   x1="458.42679"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_6_">
+		<stop
+   id="stop296"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop298"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.22131"
+   x2="413.16309"
+   y1="55.888199"
+   x1="413.16309"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_7_">
+		<stop
+   id="stop303"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop305"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.2236"
+   x2="290.76169"
+   y1="55.8867"
+   x1="290.76169"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_8_">
+		<stop
+   id="stop310"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop312"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+	<linearGradient
+   y2="339.22119"
+   x2="346.77341"
+   y1="55.888199"
+   x1="346.77341"
+   gradientUnits="userSpaceOnUse"
+   id="SVGID_9_">
+		<stop
+   id="stop317"
+   style="stop-color:#BED5E1"
+   offset="0" />
+		<stop
+   id="stop319"
+   style="stop-color:#567B8F"
+   offset="1" />
+	</linearGradient>
+	
+</defs><sodipodi:namedview
+   pagecolor="#ffffff"
+   bordercolor="#666666"
+   borderopacity="1"
+   objecttolerance="10"
+   gridtolerance="10"
+   guidetolerance="10"
+   inkscape:pageopacity="0"
+   inkscape:pageshadow="2"
+   inkscape:window-width="1280"
+   inkscape:window-height="776"
+   id="namedview323"
+   showgrid="false"
+   inkscape:zoom="3.4068286"
+   inkscape:cx="79.998916"
+   inkscape:cy="32.107419"
+   inkscape:window-x="0"
+   inkscape:window-y="24"
+   inkscape:window-maximized="1"
+   inkscape:current-layer="Layer_1"
+   showguides="true"
+   inkscape:guide-bbox="true" />
+<pattern
+   y="565.223"
+   width="69"
+   height="69"
+   patternUnits="userSpaceOnUse"
+   id="Polka_Dot_Pattern"
+   viewBox="2.125 -70.896 69 69"
+   overflow="visible">
+	<g
+   id="g4">
+		<polygon
+   fill="none"
+   points="71.125,-1.896 2.125,-1.896 2.125,-70.896 71.125,-70.896   "
+   id="polygon6" />
+		<polygon
+   fill="#F6BB60"
+   points="71.125,-1.896 2.125,-1.896 2.125,-70.896 71.125,-70.896   "
+   id="polygon8" />
+		<g
+   id="g10">
+			<path
+   fill="#FFFFFF"
+   d="M61.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path12" />
+			<path
+   fill="#FFFFFF"
+   d="M54.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path14" />
+			<path
+   fill="#FFFFFF"
+   d="M46.439-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path16" />
+			<path
+   fill="#FFFFFF"
+   d="M38.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path18" />
+			<path
+   fill="#FFFFFF"
+   d="M31.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path20" />
+			<path
+   fill="#FFFFFF"
+   d="M23.439-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path22" />
+			<path
+   fill="#FFFFFF"
+   d="M15.772-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path24" />
+			<path
+   fill="#FFFFFF"
+   d="M8.105-71.653c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path26" />
+			<path
+   fill="#FFFFFF"
+   d="M0.439-71.653c0.018,0.072,0.008,0.127-0.026,0.19C0.361-71.362,0.3-71.4,0.248-71.335     c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path28" />
+		</g>
+		<g
+   id="g30">
+			<path
+   fill="#FFFFFF"
+   d="M69.439-71.653c0.018,0.072,0.008,0.127-0.026,0.19c-0.052,0.101-0.113,0.062-0.165,0.128     c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path32" />
+		</g>
+		<path
+   fill="#FFFFFF"
+   d="M0.495-71.653c0.018,0.072,0.008,0.127-0.026,0.19c-0.052,0.101-0.113,0.062-0.165,0.128    c-0.051,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161    c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631    c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45    c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221    c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337    c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207    c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224C0.5-71.68,0.503-71.744,0.51-71.626    c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path34" />
+		<g
+   id="g36">
+			<g
+   id="g38">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path40" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path42" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path44" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path46" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path48" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path50" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path52" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path54" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path56" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-64.001c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143C2-61.45,2.217-61.397,2.391-61.46c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path58" />
+			</g>
+			<g
+   id="g60">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path62" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path64" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path66" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path68" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path70" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path72" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path74" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path76" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path78" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-56.348c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-56.374,0.503-56.438,0.51-56.32      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path80" />
+			</g>
+			<g
+   id="g82">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path84" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path86" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path88" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path90" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path92" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path94" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path96" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path98" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path100" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-48.695c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path102" />
+			</g>
+			<g
+   id="g104">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path106" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path108" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path110" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path112" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path114" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path116" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path118" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path120" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      C8.15-41.004,8.149-41.02,8.14-41.04"
+   id="path122" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-41.042c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path124" />
+			</g>
+			<g
+   id="g126">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path128" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path130" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path132" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path134" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path136" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path138" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path140" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path142" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path144" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-33.39c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-33.416,0.503-33.48,0.51-33.362      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path146" />
+			</g>
+			<g
+   id="g148">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path150" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path152" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path154" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path156" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path158" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path160" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path162" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path164" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path166" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-25.736c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path168" />
+			</g>
+			<g
+   id="g170">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path172" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path174" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path176" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path178" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path180" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path182" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path184" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path186" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path188" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-18.084c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224C0.5-18.11,0.503-18.175,0.51-18.057      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path190" />
+			</g>
+			<g
+   id="g192">
+				<path
+   fill="#FFFFFF"
+   d="M69.439-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362C69-9.692,69.159-9.523,69.154-9.4c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path194" />
+				<path
+   fill="#FFFFFF"
+   d="M61.778-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path196" />
+				<path
+   fill="#FFFFFF"
+   d="M54.118-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path198" />
+				<path
+   fill="#FFFFFF"
+   d="M46.458-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path200" />
+				<path
+   fill="#FFFFFF"
+   d="M38.797-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path202" />
+				<path
+   fill="#FFFFFF"
+   d="M31.137-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path204" />
+				<path
+   fill="#FFFFFF"
+   d="M23.477-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path206" />
+				<path
+   fill="#FFFFFF"
+   d="M15.816-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.009,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      c0.177,0.042,0.384-0.104,0.543-0.143c0.18-0.043,0.397,0.01,0.571-0.053C17.933-7.969,17.839-8.227,18-8.34      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path208" />
+				<path
+   fill="#FFFFFF"
+   d="M8.156-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      C7.915-10.05,7.866-9.836,7.886-9.75C7.717-9.692,7.876-9.523,7.871-9.4C7.868-9.351,7.83-9.295,7.826-9.239      c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631      C9.114-7.652,9.321-7.799,9.48-7.837c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path210" />
+				<path
+   fill="#FFFFFF"
+   d="M0.495-10.431c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128      C0.254-10.05,0.205-9.836,0.225-9.75C0.056-9.692,0.215-9.523,0.21-9.4c-0.002,0.05-0.041,0.105-0.045,0.161      c-0.01,0.119,0.017,0.266,0.068,0.37C0.33-8.671,0.501-8.456,0.668-8.325c0.19,0.148,0.365,0.572,0.608,0.631      C1.454-7.652,1.66-7.799,1.819-7.837C2-7.88,2.217-7.827,2.391-7.89c0.222-0.079,0.127-0.337,0.288-0.45      c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46C3.477-8.933,3.471-8.995,3.5-9.071      c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337      c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207      c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169      c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path212" />
+			</g>
+		</g>
+		<g
+   id="g214">
+			<path
+   fill="#FFFFFF"
+   d="M69.439-2.778c0.018,0.072,0.008,0.127-0.026,0.19C69.361-2.487,69.3-2.525,69.248-2.46     c-0.051,0.062-0.099,0.276-0.079,0.362C69-2.04,69.159-1.871,69.154-1.748c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C70.397,0,70.604-0.146,70.763-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.215,0.124-0.215,0.224c0.002,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path216" />
+			<path
+   fill="#FFFFFF"
+   d="M61.778-2.778c0.018,0.072,0.007,0.127-0.026,0.19C61.7-2.487,61.64-2.525,61.587-2.46     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C62.737,0,62.943-0.146,63.103-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224C61.915-3.117,61.78-3.02,61.781-2.92c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path218" />
+			<path
+   fill="#FFFFFF"
+   d="M54.118-2.778c0.018,0.072,0.007,0.127-0.026,0.19C54.04-2.487,53.98-2.525,53.927-2.46     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C55.077,0,55.283-0.146,55.442-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224C54.255-3.117,54.12-3.02,54.121-2.92c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path220" />
+			<path
+   fill="#FFFFFF"
+   d="M46.458-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C47.416,0,47.623-0.146,47.782-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224C46.594-3.117,46.459-3.02,46.46-2.92c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path222" />
+			<path
+   fill="#FFFFFF"
+   d="M38.797-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C39.756,0,39.962-0.146,40.122-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224C38.934-3.117,38.799-3.02,38.8-2.92c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path224" />
+			<path
+   fill="#FFFFFF"
+   d="M31.137-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C32.095,0,32.302-0.146,32.461-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224C31.273-3.117,31.139-3.02,31.14-2.92c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path226" />
+			<path
+   fill="#FFFFFF"
+   d="M23.477-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C24.435,0,24.642-0.146,24.801-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     c-0.021,0.011-0.021-0.005-0.03-0.025"
+   id="path228" />
+			<path
+   fill="#FFFFFF"
+   d="M15.816-2.778c0.018,0.072,0.007,0.127-0.026,0.19c-0.053,0.101-0.112,0.062-0.165,0.128     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C16.774,0,16.981-0.146,17.14-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789c-0.18,0.034-0.287,0.126-0.442,0.207     c-0.17,0.088-0.139,0.166-0.318,0.224c-0.081,0.026-0.216,0.124-0.215,0.224c0.001,0.115,0.005,0.051,0.012,0.169     C15.81-2.74,15.809-2.756,15.8-2.776"
+   id="path230" />
+			<path
+   fill="#FFFFFF"
+   d="M8.156-2.778c0.018,0.072,0.007,0.127-0.026,0.19C8.077-2.487,8.018-2.525,7.965-2.46     c-0.05,0.062-0.099,0.276-0.079,0.362c-0.169,0.058-0.01,0.227-0.015,0.35C7.868-1.698,7.83-1.643,7.826-1.587     c-0.01,0.119,0.017,0.266,0.068,0.37c0.097,0.198,0.268,0.413,0.435,0.544c0.19,0.148,0.365,0.572,0.608,0.631     C9.114,0,9.321-0.146,9.48-0.185c0.18-0.043,0.397,0.01,0.571-0.053c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.069,0.339-0.263,0.376-0.46c0.016-0.082,0.01-0.145,0.039-0.221     c0.039-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.052-0.12-0.064-0.187c-0.022-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789C8.954-3.54,8.847-3.448,8.692-3.367     c-0.17,0.088-0.139,0.166-0.318,0.224C8.292-3.117,8.158-3.02,8.159-2.92C8.16-2.805,8.164-2.869,8.17-2.751     C8.15-2.74,8.149-2.756,8.14-2.776"
+   id="path232" />
+			<path
+   fill="#FFFFFF"
+   d="M0.495-2.778c0.018,0.072,0.008,0.127-0.026,0.19C0.417-2.487,0.356-2.525,0.304-2.46     C0.253-2.397,0.205-2.184,0.225-2.098C0.056-2.04,0.215-1.871,0.21-1.748c-0.002,0.05-0.041,0.105-0.045,0.161     c-0.01,0.119,0.017,0.266,0.068,0.37C0.33-1.019,0.501-0.804,0.668-0.673c0.19,0.148,0.365,0.572,0.608,0.631     C1.454,0,1.66-0.146,1.819-0.185C2-0.228,2.217-0.175,2.391-0.237c0.222-0.079,0.127-0.337,0.288-0.45     c0.104-0.074,0.287-0.01,0.406-0.051c0.2-0.07,0.339-0.263,0.376-0.46C3.477-1.28,3.471-1.343,3.5-1.419     c0.038-0.103,0.111-0.16,0.09-0.293c-0.01-0.062-0.051-0.12-0.064-0.187c-0.021-0.114,0.002-0.224,0-0.337     c-0.003-0.2,0.017-0.379-0.078-0.55c-0.38-0.688-1.236-0.929-1.975-0.789C1.293-3.54,1.187-3.448,1.031-3.367     c-0.17,0.088-0.139,0.166-0.318,0.224C0.632-3.117,0.498-3.02,0.498-2.92C0.5-2.805,0.503-2.869,0.51-2.751     C0.489-2.74,0.488-2.756,0.479-2.776"
+   id="path234" />
+		</g>
+	</g>
+</pattern>
+
+
+
+
+
+
+
+
+
+
+
+
+<g
+   style="display:inline"
+   id="g3154"
+   transform="matrix(0.21384268,0,0,0.21384268,31.655324,-22.278409)"><circle
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="circle238"
+     r="21.999001"
+     cy="225.17101"
+     cx="97.311996"
+     sodipodi:cx="97.311996"
+     sodipodi:cy="225.17101"
+     sodipodi:rx="21.999001"
+     sodipodi:ry="21.999001"
+     d="m 119.311,225.17101 c 0,12.14971 -9.84929,21.999 -21.999004,21.999 -12.149712,0 -21.999,-9.84929 -21.999,-21.999 0,-12.14972 9.849288,-21.99901 21.999,-21.99901 12.149714,0 21.999004,9.84929 21.999004,21.99901 z" /><circle
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="circle240"
+     r="22.000999"
+     cy="225.17101"
+     cx="364.82501"
+     sodipodi:cx="364.82501"
+     sodipodi:cy="225.17101"
+     sodipodi:rx="22.000999"
+     sodipodi:ry="22.000999"
+     d="m 386.82601,225.17101 c 0,12.15081 -9.85018,22.00099 -22.001,22.00099 -12.15081,0 -22.001,-9.85018 -22.001,-22.00099 0,-12.15082 9.85019,-22.001 22.001,-22.001 12.15082,0 22.001,9.85018 22.001,22.001 z" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path242"
+     d="m 135.313,202.966 v 30.074 c 0,7.801 6.324,14.123 14.123,14.123 7.803,0 14.125,-6.322 14.125,-14.123 0,-0.004 0,-30.074 0,-30.074"
+     inkscape:connector-curvature="0" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path244"
+     d="m 163.561,202.966 v 30.074 c 0,7.801 6.324,14.123 14.125,14.123 7.801,0 14.125,-6.322 14.125,-14.123 0,-0.004 0,-30.074 0,-30.074"
+     inkscape:connector-curvature="0" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path246"
+     d="m 319.578,162.962 v 70.078 c 0,7.801 6.322,14.123 14.123,14.123"
+     inkscape:connector-curvature="0" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path248"
+     d="m 208.317,247.169 v -22.201 c 0,-12.15 9.85,-22 22,-22 12.152,0 22.002,9.85 22.002,22 0,0.006 0,22.201 0,22.201"
+     inkscape:connector-curvature="0" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path250"
+     d="m 401.322,202.966 v 22.205 c 0,12.148 9.85,21.998 22,21.998 12.15,0 22.002,-9.85 22.002,-21.998 0,-0.008 0,-22.205 0,-22.205"
+     inkscape:connector-curvature="0" /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path252"
+     d="m 303.146,176.839 c -19.421,0 -35.167,15.744 -35.167,35.166 0,19.422 15.746,35.164 35.167,35.164"
+     inkscape:connector-curvature="0" /><polyline
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="polyline254"
+     points="504.596,162.962 504.596,203.134 504.596,202.155 " /><path
+     style="fill:none;stroke:#ffffff;stroke-width:4;stroke-opacity:1"
+     id="path256"
+     d="m 490.236,247.157 c -4.275,0 -7.844,0 -7.848,0 -12.15,0 -22,-9.85 -22,-21.998 0,-12.15 9.85,-22 22,-22 l 22.207,-0.025 c 0,0 0.137,22.342 0,29.895 -0.138,7.552 -7.968,14.128 -14.359,14.128 z"
+     inkscape:connector-curvature="0" /></g><circle
+   sodipodi:ry="20.332001"
+   sodipodi:rx="20.332001"
+   sodipodi:cy="78.911003"
+   sodipodi:cx="288.49399"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-opacity:1;display:inline"
+   id="circle265"
+   r="20.332001"
+   cy="78.911003"
+   cx="288.49399"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><circle
+   sodipodi:ry="37.242001"
+   sodipodi:rx="37.242001"
+   sodipodi:cy="149.577"
+   sodipodi:cx="251.211"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-opacity:1;display:inline"
+   id="circle272"
+   r="37.242001"
+   cy="149.577"
+   cx="251.211"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><circle
+   sodipodi:ry="40.261002"
+   sodipodi:rx="40.261002"
+   sodipodi:cy="124.796"
+   sodipodi:cx="293.22501"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-opacity:1;display:inline"
+   id="circle279"
+   r="40.261002"
+   cy="124.796"
+   cx="293.22501"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><circle
+   sodipodi:ry="37.242001"
+   sodipodi:rx="37.242001"
+   sodipodi:cy="108.472"
+   sodipodi:cx="375.33401"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-opacity:1;display:inline"
+   id="circle286"
+   r="37.242001"
+   cy="108.472"
+   cx="375.33401"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><circle
+   sodipodi:ry="37.241001"
+   sodipodi:rx="37.241001"
+   sodipodi:cy="79.503998"
+   sodipodi:cx="334.49301"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-opacity:1;display:inline"
+   id="circle293"
+   r="37.241001"
+   cy="79.503998"
+   cx="334.49301"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><circle
+   sodipodi:ry="40.261002"
+   sodipodi:rx="40.261002"
+   sodipodi:cy="147.563"
+   sodipodi:cx="413.16299"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline"
+   id="circle307"
+   r="40.261002"
+   cy="147.563"
+   cx="413.16299"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><circle
+   sodipodi:ry="21.299"
+   sodipodi:rx="21.299"
+   sodipodi:cy="166.011"
+   sodipodi:cx="290.76199"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:4;stroke-opacity:1;display:inline"
+   id="circle314"
+   r="21.299"
+   cy="166.011"
+   cx="290.76199"
+   transform="matrix(0.21384268,0,0,0.21384268,-45.328041,-8.5924791)" /><path
+   inkscape:connector-curvature="0"
+   style="fill:#ffffff;fill-opacity:1;stroke:#1d2d44;stroke-width:0.8553707;stroke-opacity:1;display:inline"
+   id="path321"
+   d="m 39.804227,20.419344 c 0,6.062012 -4.91496,10.976117 -10.9774,10.976117 -6.06244,0 -10.977185,-4.914105 -10.977185,-10.976117 0,-6.062654 4.914959,-10.9771861 10.977185,-10.9771861 6.062226,0 10.9774,4.9145321 10.9774,10.9771861 z" /></svg>
\ No newline at end of file
diff --git a/core/img/owncloud-logo-medium-white.png b/core/img/owncloud-logo-medium-white.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4d06fdd62d6a1cfe82170b3009fd8a9f36e0a1e
Binary files /dev/null and b/core/img/owncloud-logo-medium-white.png differ
diff --git a/core/img/places/folder.png b/core/img/places/folder.png
new file mode 100644
index 0000000000000000000000000000000000000000..3edbe257a34cf50b5015f427138a67ea4debb5cf
Binary files /dev/null and b/core/img/places/folder.png differ
diff --git a/core/img/places/folder.svg b/core/img/places/folder.svg
new file mode 100644
index 0000000000000000000000000000000000000000..c04b00fedceb60f32caf2be906b47fb8111d1b95
--- /dev/null
+++ b/core/img/places/folder.svg
@@ -0,0 +1,1830 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="home.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/home.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-4"
+       id="linearGradient4357-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       id="linearGradient3587-6-5-4">
+      <stop
+         id="stop3589-9-2-1"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-04"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-26"
+       id="linearGradient4566-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       id="linearGradient3587-6-5-26">
+      <stop
+         id="stop3589-9-2-45"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-20"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="18.967093"
+       x2="-2.4040222"
+       y1="4.4573336"
+       x1="-2.4040222"
+       gradientTransform="translate(13.927091,16.573387)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3475"
+       xlink:href="#linearGradient3587-6-5-26"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       id="linearGradient4580-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-1"
+       id="linearGradient4578-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-1">
+      <stop
+         id="stop3589-9-2-8-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="1013.451"
+       x2="209.34245"
+       y1="998.45801"
+       x1="209.34245"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3528"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       transform="translate(-7.5117601e-7,40.000351)"
+       id="g3499">
+      <g
+         style="opacity:0.6;fill:#ffffff;fill-opacity:1;fill-rule:evenodd"
+         id="g14154"
+         transform="matrix(0.8666684,0,0,0.8666684,-172.0426,-903.42597)">
+        <path
+           style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
+           sodipodi:nodetypes="ccccccccccsccccccccccc"
+           id="rect3845"
+           d="m 200.19539,998.56643 c -0.28913,0 -0.53125,0.24212 -0.53125,0.53125 l 0,13.93752 c 0,0.2985 0.23264,0.5312 0.53125,0.5312 l 15.09127,0 c 0.2986,0 0.53125,-0.2326 0.53125,-0.5312 l 4e-4,-8.166 c 0,-0.2891 -0.24212,-0.5338 -0.53125,-0.5338 l -12.16119,0 -4e-4,6.349 c 0,0.277 -0.30237,0.5637 -0.57937,0.5637 -0.277,0 -0.57447,-0.2867 -0.57447,-0.5637 l 4e-4,-7.0056 c 0,-0.277 0.23357,-0.4974 0.51057,-0.4974 l 2.65069,0 8.37738,3e-4 -4e-4,-1.7029 c 0,-0.3272 -0.24549,-0.6047 -0.57258,-0.6047 l -7.50433,0 0,-1.77642 c 0,-0.28915 -0.23415,-0.53125 -0.52328,-0.53125 z"
+           inkscape:connector-curvature="0" />
+      </g>
+      <g
+         style="opacity:0.7;fill:url(#linearGradient3528);fill-opacity:1;fill-rule:evenodd"
+         id="g14154-0"
+         transform="matrix(0.8666684,0,0,0.8666684,-172.0426,-904.42597)">
+        <path
+           style="fill:url(#linearGradient4578-7);fill-opacity:1;fill-rule:evenodd;stroke:none;display:inline"
+           sodipodi:nodetypes="ccccccccccsccccccccccc"
+           id="rect3845-5"
+           d="m 200.19539,998.56643 c -0.28913,0 -0.53125,0.24212 -0.53125,0.53125 l 0,13.93752 c 0,0.2985 0.23264,0.5312 0.53125,0.5312 l 15.09127,0 c 0.2986,0 0.53125,-0.2326 0.53125,-0.5312 l 4e-4,-8.166 c 0,-0.2891 -0.24212,-0.5338 -0.53125,-0.5338 l -12.16119,0 -4e-4,6.349 c 0,0.277 -0.30237,0.5637 -0.57937,0.5637 -0.277,0 -0.57447,-0.2867 -0.57447,-0.5637 l 4e-4,-7.0056 c 0,-0.277 0.23357,-0.4974 0.51057,-0.4974 l 2.65069,0 8.37738,3e-4 -4e-4,-1.7029 c 0,-0.3272 -0.24549,-0.6047 -0.57258,-0.6047 l -7.50433,0 0,-1.77642 c 0,-0.28915 -0.23415,-0.53125 -0.52328,-0.53125 z"
+           inkscape:connector-curvature="0" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/core/img/places/home.png b/core/img/places/home.png
new file mode 100644
index 0000000000000000000000000000000000000000..b3fb9bbaf6fba02fa618187ba4ba03f0eeb21fc5
Binary files /dev/null and b/core/img/places/home.png differ
diff --git a/core/img/places/home.svg b/core/img/places/home.svg
new file mode 100644
index 0000000000000000000000000000000000000000..4b45ef12bcbb4be2059a85ebe5102d6c855fc2da
--- /dev/null
+++ b/core/img/places/home.svg
@@ -0,0 +1,1779 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="help.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/help.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-4"
+       id="linearGradient4357-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       id="linearGradient3587-6-5-4">
+      <stop
+         id="stop3589-9-2-1"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-04"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="28.776533"
+       x2="0.44923753"
+       y1="13.895414"
+       x1="0.86849999"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3456"
+       xlink:href="#linearGradient3587-6-5-4"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-26"
+       id="linearGradient4566-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       id="linearGradient3587-6-5-26">
+      <stop
+         id="stop3589-9-2-45"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-20"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="18.967093"
+       x2="-2.4040222"
+       y1="4.4573336"
+       x1="-2.4040222"
+       gradientTransform="translate(13.927091,16.573387)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3475"
+       xlink:href="#linearGradient3587-6-5-26"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       id="g4550"
+       transform="translate(0,-20)">
+      <path
+         inkscape:connector-curvature="0"
+         d="M 8,22.03072 0,30 l 3,0 0,6 10,0 0,-6 3,0 L 13,26.969459 13,23 l -3.0000001,0 0,1.081152 L 8,22.03072 z"
+         id="path3887"
+         style="opacity:0.6;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <path
+         inkscape:connector-curvature="0"
+         d="M 8,21.03072 0,29 l 3,0 0,6 10,0 0,-6 3,0 L 13,25.969459 13,22 l -3.0000001,0 0,1.081152 L 8,21.03072 z"
+         id="path3883"
+         style="opacity:0.7;fill:url(#linearGradient3475);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+  </g>
+</svg>
diff --git a/core/img/weather-clear.png b/core/img/weather-clear.png
new file mode 100644
index 0000000000000000000000000000000000000000..0acf7a9b2afd4ba9b8492f51d5204721f9e4559a
Binary files /dev/null and b/core/img/weather-clear.png differ
diff --git a/core/js/jquery-1.6.2.min.js b/core/js/jquery-1.6.2.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..48590ecb96a74f5987d125e7fbc5a26c1392543a
--- /dev/null
+++ b/core/js/jquery-1.6.2.min.js
@@ -0,0 +1,18 @@
+/*!
+ * jQuery JavaScript Library v1.6.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Thu Jun 30 14:16:56 2011 -0400
+ */
+(function(a,b){function cv(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cs(a){if(!cg[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ch||(ch=c.createElement("iframe"),ch.frameBorder=ch.width=ch.height=0),b.appendChild(ch);if(!ci||!ch.createElement)ci=(ch.contentWindow||ch.contentDocument).document,ci.write((c.compatMode==="CSS1Compat"?"<!doctype html>":"")+"<html><body>"),ci.close();d=ci.createElement(a),ci.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ch)}cg[a]=e}return cg[a]}function cr(a,b){var c={};f.each(cm.concat.apply([],cm.slice(0,b)),function(){c[this]=a});return c}function cq(){cn=b}function cp(){setTimeout(cq,0);return cn=f.now()}function cf(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ce(){try{return new a.XMLHttpRequest}catch(b){}}function b$(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g<i;g++){if(g===1)for(h in a.converters)typeof h=="string"&&(e[h.toLowerCase()]=a.converters[h]);l=k,k=d[g];if(k==="*")k=l;else if(l!=="*"&&l!==k){m=l+" "+k,n=e[m]||e["* "+k];if(!n){p=b;for(o in e){j=o.split(" ");if(j[0]===l||j[0]==="*"){p=e[j[1]+" "+k];if(p){o=e[o],o===!0?n=p:p===!0&&(n=o);break}}}}!n&&!p&&f.error("No conversion from "+m.replace(" "," to ")),n!==!0&&(c=n?n(c):p(o(c)))}}return c}function bZ(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=a.mimeType||c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}k||(k=i)}j=j||k}if(j){j!==f[0]&&f.unshift(j);return d[j]}}function bY(a,b,c,d){if(f.isArray(b))f.each(b,function(b,e){c||bC.test(a)?d(a,e):bY(a+"["+(typeof e=="object"||f.isArray(e)?b:"")+"]",e,c,d)});else if(!c&&b!=null&&typeof b=="object")for(var e in b)bY(a+"["+e+"]",b[e],c,d);else d(a,b)}function bX(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bR,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l=="string"&&(!k||g[l]?l=b:(c.dataTypes.unshift(l),l=bX(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bX(a,c,d,e,"*",g));return l}function bW(a){return function(b,c){typeof b!="string"&&(c=b,b="*");if(f.isFunction(c)){var d=b.toLowerCase().split(bN),e=0,g=d.length,h,i,j;for(;e<g;e++)h=d[e],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}function bA(a,b,c){var d=b==="width"?a.offsetWidth:a.offsetHeight,e=b==="width"?bv:bw;if(d>0){c!=="border"&&f.each(e,function(){c||(d-=parseFloat(f.css(a,"padding"+this))||0),c==="margin"?d+=parseFloat(f.css(a,c+this))||0:d-=parseFloat(f.css(a,"border"+this+"Width"))||0});return d+"px"}d=bx(a,b,b);if(d<0||d==null)d=a.style[b]||0;d=parseFloat(d)||0,c&&f.each(e,function(){d+=parseFloat(f.css(a,"padding"+this))||0,c!=="padding"&&(d+=parseFloat(f.css(a,"border"+this+"Width"))||0),c==="margin"&&(d+=parseFloat(f.css(a,c+this))||0)});return d+"px"}function bm(a,b){b.src?f.ajax({url:b.src,async:!1,dataType:"script"}):f.globalEval((b.text||b.textContent||b.innerHTML||"").replace(be,"/*$0*/")),b.parentNode&&b.parentNode.removeChild(b)}function bl(a){f.nodeName(a,"input")?bk(a):"getElementsByTagName"in a&&f.grep(a.getElementsByTagName("input"),bk)}function bk(a){if(a.type==="checkbox"||a.type==="radio")a.defaultChecked=a.checked}function bj(a){return"getElementsByTagName"in a?a.getElementsByTagName("*"):"querySelectorAll"in a?a.querySelectorAll("*"):[]}function bi(a,b){var c;if(b.nodeType===1){b.clearAttributes&&b.clearAttributes(),b.mergeAttributes&&b.mergeAttributes(a),c=b.nodeName.toLowerCase();if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(f.expando)}}function bh(a,b){if(b.nodeType===1&&!!f.hasData(a)){var c=f.expando,d=f.data(a),e=f.data(b,d);if(d=d[c]){var g=d.events;e=e[c]=f.extend({},d);if(g){delete e.handle,e.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)f.event.add(b,h+(g[h][i].namespace?".":"")+g[h][i].namespace,g[h][i],g[h][i].data)}}}}function bg(a,b){return f.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function W(a,b,c){b=b||0;if(f.isFunction(b))return f.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return f.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=f.grep(a,function(a){return a.nodeType===1});if(R.test(b))return f.filter(b,d,!c);b=f.filter(b,d)}return f.grep(a,function(a,d){return f.inArray(a,b)>=0===c})}function V(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function N(a,b){return(a&&a!=="*"?a+".":"")+b.replace(z,"`").replace(A,"&")}function M(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;i<s.length;i++)g=s[i],g.origType.replace(x,"")===a.type?q.push(g.selector):s.splice(i--,1);e=f(a.target).closest(q,a.currentTarget);for(j=0,k=e.length;j<k;j++){m=e[j];for(i=0;i<s.length;i++){g=s[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))&&!m.elem.disabled){h=m.elem,d=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,d=f(a.relatedTarget).closest(g.selector)[0],d&&f.contains(h,d)&&(d=h);(!d||d!==h)&&p.push({elem:h,handleObj:g,level:m.level})}}}for(j=0,k=p.length;j<k;j++){e=p[j];if(c&&e.level>c)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function K(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function E(){return!0}function D(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function J(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(J,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=/-([a-z])/ig,x=function(a,b){return b.toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){d=i[c],f=a[c];if(i===f)continue;l&&f&&(e.isPlainObject(f)||(g=e.isArray(f)))?(g?(g=!1,h=d&&e.isArray(d)?d:[]):h=d&&e.isPlainObject(d)?d:{},i[c]=e.extend(l,h,f)):f!==b&&(i[c]=f)}return i},e.extend({noConflict:function(b){a.$===e&&(a.$=g),b&&a.jQuery===e&&(a.jQuery=f);return e},isReady:!1,readyWait:1,holdReady:function(a){a?e.readyWait++:e.ready(!0)},ready:function(a){if(a===!0&&!--e.readyWait||a!==!0&&!e.isReady){if(!c.body)return setTimeout(e.ready,1);e.isReady=!0;if(a!==!0&&--e.readyWait>0)return;A.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!A){A=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||D.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g<h;)if(c.apply(a[g++],d)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(;g<h;)if(c.call(a[g],g,a[g++])===!1)break;return a},trim:G?function(a){return a==null?"":G.call(a)}:function(a){return a==null?"":(a+"").replace(k,"").replace(l,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var d=e.type(a);a.length==null||d==="string"||d==="function"||d==="regexp"||e.isWindow(a)?E.call(c,a):e.merge(c,a)}return c},inArray:function(a,b){if(H)return H.call(b,a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length=="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,c,d){var f,g,h=[],i=0,j=a.length,k=a instanceof e||j!==b&&typeof j=="number"&&(j>0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i<j;i++)f=c(a[i],i,d),f!=null&&(h[h.length]=f);else for(g in a)f=c(a[g],g,d),f!=null&&(h[h.length]=f);return h.concat.apply([],h)},guid:1,proxy:function(a,c){if(typeof c=="string"){var d=a[c];c=a,a=d}if(!e.isFunction(a))return b;var f=F.call(arguments,2),g=function(){return a.apply(c,f.concat(F.call(arguments)))};g.guid=a.guid=a.guid||g.guid||e.guid++;return g},access:function(a,c,d,f,g,h){var i=a.length;if(typeof c=="object"){for(var j in c)e.access(a,j,c[j],f,g,d);return a}if(d!==b){f=!h&&f&&e.isFunction(d);for(var k=0;k<i;k++)g(a[k],c,f?d.call(a[k],k,g(a[k],c)):d,h);return a}return i?g(a[0],c):b},now:function(){return(new Date).getTime()},uaMatch:function(a){a=a.toLowerCase();var b=s.exec(a)||t.exec(a)||u.exec(a)||a.indexOf("compatible")<0&&v.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}e.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function(d,f){f&&f instanceof e&&!(f instanceof a)&&(f=a(f));return e.fn.init.call(this,d,f,b)},a.fn.init.prototype=a.fn;var b=a(c);return a},browser:{}}),e.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){I["[object "+b+"]"]=b.toLowerCase()}),z=e.uaMatch(y),z.browser&&(e.browser[z.browser]=!0,e.browser.version=z.version),e.browser.webkit&&(e.browser.safari=!0),j.test(" ")&&(k=/^[\s\xA0]+/,l=/[\s\xA0]+$/),h=e(c),c.addEventListener?B=function(){c.removeEventListener("DOMContentLoaded",B,!1),e.ready()}:c.attachEvent&&(B=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",B),e.ready())});return e}(),g="done fail isResolved isRejected promise then always pipe".split(" "),h=[].slice;f.extend({_Deferred:function(){var a=[],b,c,d,e={done:function(){if(!d){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=f.type(i),j==="array"?e.done.apply(e,i):j==="function"&&a.push(i);k&&e.resolveWith(k[0],k[1])}return this},resolveWith:function(e,f){if(!d&&!b&&!c){f=f||[],c=1;try{while(a[0])a.shift().apply(e,f)}finally{b=[e,f],c=0}}return this},resolve:function(){e.resolveWith(this,arguments);return this},isResolved:function(){return!!c||!!b},cancel:function(){d=1,a=[];return this}};return e},Deferred:function(a){var b=f._Deferred(),c=f._Deferred(),d;f.extend(b,{then:function(a,c){b.done(a).fail(c);return this},always:function(){return b.done.apply(b,arguments).fail.apply(this,arguments)},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,pipe:function(a,c){return f.Deferred(function(d){f.each({done:[a,"resolve"],fail:[c,"reject"]},function(a,c){var e=c[0],g=c[1],h;f.isFunction(e)?b[a](function(){h=e.apply(this,arguments),h&&f.isFunction(h.promise)?h.promise().then(d.resolve,d.reject):d[g](h)}):b[a](d[g])})}).promise()},promise:function(a){if(a==null){if(d)return d;d=a={}}var c=g.length;while(c--)a[g[c]]=b[g[c]];return a}}),b.done(c.cancel).fail(b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){function i(a){return function(c){b[a]=arguments.length>1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c<d;c++)b[c]&&f.isFunction(b[c].promise)?b[c].promise().then(i(c),g.reject):--e;e||g.resolveWith(g,b)}else g!==a&&g.resolveWith(g,d?[a]:[]);return g.promise()}}),f.support=function(){var a=c.createElement("div"),b=c.documentElement,d,e,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u;a.setAttribute("className","t"),a.innerHTML="   <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=a.getElementsByTagName("input")[0],k={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},i.checked=!0,k.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,k.optDisabled=!h.disabled;try{delete a.test}catch(v){k.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function(){k.noCloneEvent=!1}),a.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),k.radioValue=i.value==="t",i.setAttribute("checked","checked"),a.appendChild(i),l=c.createDocumentFragment(),l.appendChild(a.firstChild),k.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",m=c.getElementsByTagName("body")[0],o=c.createElement(m?"div":"body"),p={visibility:"hidden",width:0,height:0,border:0,margin:0},m&&f.extend(p,{position:"absolute",left:-1e3,top:-1e3});for(t in p)o.style[t]=p[t];o.appendChild(a),n=m||b,n.insertBefore(o,n.firstChild),k.appendChecked=i.checked,k.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,k.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",k.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>",q=a.getElementsByTagName("td"),u=q[0].offsetHeight===0,q[0].style.display="",q[1].style.display="none",k.reliableHiddenOffsets=u&&q[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(j=c.createElement("div"),j.style.width="0",j.style.marginRight="0",a.appendChild(j),k.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(j,null)||{marginRight:0}).marginRight,10)||0)===0),o.innerHTML="",n.removeChild(o);if(a.attachEvent)for(t in{submit:1,change:1,focusin:1})s="on"+t,u=s in a,u||(a.setAttribute(s,"return;"),u=typeof a[s]=="function"),k[t+"Bubbles"]=u;o=l=g=h=m=j=a=i=null;return k}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]||i[c]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h<i;h++)g=e[h].name,g.indexOf("data-")===0&&(g=f.camelCase(g.substring(5)),k(this[0],g,d[g]))}}return d}if(typeof a=="object")return this.each(function(){f.data(this,a)});var j=a.split(".");j[1]=j[1]?"."+j[1]:"";if(c===b){d=this.triggerHandler("getData"+j[1]+"!",[j[0]]),d===b&&this.length&&(d=f.data(this[0],a),d=k(this[0],a,d));return d===b&&j[1]?this.data(j[0]):d}return this.each(function(){var b=f(this),d=[j[0],c];b.triggerHandler("setData"+j[1]+"!",d),f.data(this,a,c),b.triggerHandler("changeData"+j[1]+"!",d)})},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,c){a&&(c=(c||"fx")+"mark",f.data(a,c,(f.data(a,c,b,!0)||0)+1,!0))},_unmark:function(a,c,d){a!==!0&&(d=c,c=a,a=!1);if(c){d=d||"fx";var e=d+"mark",g=a?0:(f.data(c,e,b,!0)||1)-1;g?f.data(c,e,g,!0):(f.removeData(c,e,!0),m(c,d,"mark"))}},queue:function(a,c,d){if(a){c=(c||"fx")+"queue";var e=f.data(a,c,b,!0);d&&(!e||f.isArray(d)?e=f.data(a,c,f.makeArray(d),!0):e.push(d));return e||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e;d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),d.call(a,function(){f.dequeue(a,b)})),c.length||(f.removeData(a,b+"queue",!0),m(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){typeof a!="string"&&(c=a,a="fx");if(c===b)return f.queue(this[0],a);return this.each(function(){var b=f.queue(this,a,c);a==="fx"&&b[0]!=="inprogress"&&f.dequeue(this,a)})},dequeue:function(a){return this.each(function(){f.dequeue(this,a)})},delay:function(a,b){a=f.fx?f.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){f.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,c){function m(){--h||d.resolveWith(e,[e])}typeof a!="string"&&(c=a,a=b),a=a||"fx";var d=f.Deferred(),e=this,g=e.length,h=1,i=a+"defer",j=a+"queue",k=a+"mark",l;while(g--)if(l=f.data(e[g],i,b,!0)||(f.data(e[g],j,b,!0)||f.data(e[g],k,b,!0))&&f.data(e[g],i,f._Deferred(),!0))h++,l.done(m);m();return d.promise()}});var n=/[\n\t\r]/g,o=/\s+/,p=/\r/g,q=/^(?:button|input)$/i,r=/^(?:button|input|object|select|textarea)$/i,s=/^a(?:rea)?$/i,t=/^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,u=/\:|^on/,v,w;f.fn.extend({attr:function(a,b){return f.access(this,a,b,!0,f.attr)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,a,b,!0,f.prop)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(o);for(c=0,d=this.length;c<d;c++){e=this[c];if(e.nodeType===1)if(!e.className&&b.length===1)e.className=a;else{g=" "+e.className+" ";for(h=0,i=b.length;h<i;h++)~g.indexOf(" "+b[h]+" ")||(g+=b[h]+" ");e.className=f.trim(g)}}}return this},removeClass:function(a){var c,d,e,g,h,i,j;if(f.isFunction(a))return this.each(function(b){f(this).removeClass(a.call(this,b,this.className))});if(a&&typeof a=="string"||a===b){c=(a||"").split(o);for(d=0,e=this.length;d<e;d++){g=this[d];if(g.nodeType===1&&g.className)if(a){h=(" "+g.className+" ").replace(n," ");for(i=0,j=c.length;i<j;i++)h=h.replace(" "+c[i]+" "," ");g.className=f.trim(h)}else g.className=""}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";if(f.isFunction(a))return this.each(function(c){f(this).toggleClass(a.call(this,c,this.className,b),b)});return this.each(function(){if(c==="string"){var e,g=0,h=f(this),i=b,j=a.split(o);while(e=j[g++])i=d?i:!h.hasClass(e),h[i?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&f._data(this,"__className__",this.className),this.className=this.className||a===!1?"":f._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(n," ").indexOf(b)>-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;d=e.value;return typeof d=="string"?d.replace(p,""):d==null?"":d}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h<i;h++){var j=e[h];if(j.selected&&(f.support.optDisabled?!j.disabled:j.getAttribute("disabled")===null)&&(!j.parentNode.disabled||!f.nodeName(j.parentNode,"optgroup"))){b=f(j).val();if(g)return b;d.push(b)}}if(g&&!d.length&&e.length)return f(e[c]).val();return d},set:function(a,b){var c=f.makeArray(b);f(a).find("option").each(function(){this.selected=f.inArray(f(this).val(),c)>=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);j&&(c=f.attrFix[c]||c,i=f.attrHooks[c],i||(t.test(c)?i=w:v&&c!=="className"&&(f.nodeName(a,"form")||u.test(c))&&(i=v)));if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j&&(h=i.get(a,c))!==null)return h;h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}},value:{get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return b in a?a.value:null},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);i&&(c=f.propFix[c]||c,h=f.propHooks[c]);return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return f.prop(a,c)?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=!0),a.setAttribute(c,c.toLowerCase()));return c}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.attrHooks.title=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=/\.(.*)$/,y=/^(?:textarea|input|select)$/i,z=/\./g,A=/ /g,B=/[^\w\s.|`]/g,C=function(a){return a.replace(B,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=D;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=D);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),C).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))f.event.remove(a,r,q.handler,j),p.splice(j--,1)}continue}o=f.event.special[h]||{};for(j=e||0;j<p.length;j++){q=p[j];if(d.guid===q.guid){if(l||n.test(q.namespace))e==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(e!=null)break}}if(p.length===0||e!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&f.removeEvent(a,h,s.handle),g=null,delete t[h]}if(f.isEmptyObject(t)){var u=s.handle;u&&(u.elem=null),delete s.events,delete s.handle,f.isEmptyObject(s)&&f.removeData(a,b,!0)}}},customEvent:{getData:!0,setData:!0,changeData:!0},trigger:function(c,d,e,g){var h=c.type||c,i=[],j;h.indexOf("!")>=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.
+shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d!=null?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h<i;h++){var j=d[h];if(e||c.namespace_re.test(j.namespace)){c.handler=j.handler,c.data=j.data,c.handleObj=j;var k=j.handler.apply(this,g);k!==b&&(c.result=k,k===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[f.expando])return a;var d=a;a=f.Event(d);for(var e=this.props.length,g;e;)g=this.props[--e],a[g]=d[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=a.target.ownerDocument||c,i=h.documentElement,j=h.body;a.pageX=a.clientX+(i&&i.scrollLeft||j&&j.scrollLeft||0)-(i&&i.clientLeft||j&&j.clientLeft||0),a.pageY=a.clientY+(i&&i.scrollTop||j&&j.scrollTop||0)-(i&&i.clientTop||j&&j.clientTop||0)}a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:f.proxy,special:{ready:{setup:f.bindReady,teardown:f.noop},live:{add:function(a){f.event.add(this,N(a.origType,a.selector),f.extend({},a,{handler:M,guid:a.handler.guid}))},remove:function(a){f.event.remove(this,N(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){f.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},f.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},f.Event=function(a,b){if(!this.preventDefault)return new f.Event(a,b);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?E:D):this.type=a,b&&f.extend(this,b),this.timeStamp=f.now(),this[f.expando]=!0},f.Event.prototype={preventDefault:function(){this.isDefaultPrevented=E;var a=this.originalEvent;!a||(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=E;var a=this.originalEvent;!a||(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=E,this.stopPropagation()},isDefaultPrevented:D,isPropagationStopped:D,isImmediatePropagationStopped:D};var F=function(a){var b=a.relatedTarget,c=!1,d=a.type;a.type=a.data,b!==this&&(b&&(c=f.contains(this,b)),c||(f.event.handle.apply(this,arguments),a.type=d))},G=function(a){a.type=a.data,f.event.handle.apply(this,arguments)};f.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){f.event.special[a]={setup:function(c){f.event.add(this,b,c&&c.selector?G:F,a)},teardown:function(a){f.event.remove(this,b,a&&a.selector?G:F)}}}),f.support.submitBubbles||(f.event.special.submit={setup:function(a,b){if(!f.nodeName(this,"form"))f.event.add(this,"click.specialSubmit",function(a){var b=a.target,c=b.type;(c==="submit"||c==="image")&&f(b).closest("form").length&&K("submit",this,arguments)}),f.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,c=b.type;(c==="text"||c==="password")&&f(b).closest("form").length&&a.keyCode===13&&K("submit",this,arguments)});else return!1},teardown:function(a){f.event.remove(this,".specialSubmit")}});if(!f.support.changeBubbles){var H,I=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},J=function(c){var d=c.target,e,g;if(!!y.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=I(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:J,beforedeactivate:J,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&J.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&J.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",I(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in H)f.event.add(this,c+".specialChange",H[c]);return y.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return y.test(this.nodeName)}},H=f.event.special.change.filters,H.focus=H.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i<j;i++)f.event.add(this[i],a,g,d);return this}}),f.fn.extend({unbind:function(a,b){if(typeof a=="object"&&!a.preventDefault)for(var c in a)this.unbind(c,a[c]);else for(var d=0,e=this.length;d<e;d++)f.event.remove(this[d],a,b);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){f.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0])return f.event.trigger(a,b,this[0],!0)},toggle:function(a){var b=arguments,c=a.guid||f.guid++,d=0,e=function(c){var e=(f.data(this,"lastToggle"+a.guid)||0)%d;f.data(this,"lastToggle"+a.guid,e+1),c.preventDefault();return b[e].apply(this,arguments)||!1};e.guid=c;while(d<b.length)b[d++].guid=c;return this.click(e)},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var L={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};f.each(["live","die"],function(a,c){f.fn[c]=function(a,d,e,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:f(this.context);if(typeof a=="object"&&!a.preventDefault){for(var o in a)n[c](o,d,a[o],m);return this}if(c==="die"&&!a&&g&&g.charAt(0)==="."){n.unbind(g);return this}if(d===!1||f.isFunction(d))e=d||D,d=b;a=(a||"").split(" ");while((h=a[i++])!=null){j=x.exec(h),k="",j&&(k=j[0],h=h.replace(x,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}l=h,L[h]?(a.push(L[h]+k),h=h+k):h=(L[h]||h)+k;if(c==="live")for(var p=0,q=n.length;p<q;p++)f.event.add(n[p],"live."+N(h,m),{data:d,selector:m,handler:e,origType:h,origHandler:e,preType:l});else n.unbind("live."+N(h,m),e)}return this}}),f.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){f.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}if(i.nodeType===1){f||(i.sizcache=c,i.sizset=g);if(typeof b!="string"){if(i===b){j=!0;break}}else if(k.filter(b,[i]).length>0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}i=i[a]}d[g]=j}}}var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}return a},k.matches=function(a,b){return k(a,null,null,b)},k.matchesSelector=function(a,b){return k(b,null,null,[a]).length>0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e<f;e++){var g,h=l.order[e];if(g=l.leftMatch[h].exec(a)){var j=g[1];g.splice(1,1);if(j.substr(j.length-1)!=="\\"){g[1]=(g[1]||"").replace(i,""),d=l.find[h](g,b,c);if(d!=null){a=a.replace(l.match[h],"");break}}}}d||(d=typeof b.getElementsByTagName!="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},k.filter=function(a,c,d,e){var f,g,h=a,i=[],j=c,m=c&&c[0]&&k.isXML(c[0]);while(a&&c.length){for(var n in l.filter)if((f=l.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=l.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;j===i&&(i=[]);if(l.preFilter[n]){f=l.preFilter[n](f,j,d,i,e,m);if(!f)g=o=!0;else if(f===!0)continue}if(f)for(var s=0;(p=j[s])!=null;s++)if(p){o=q(p,f,s,j);var t=e^!!o;d&&o!=null?t?g=!0:j[s]=!1:t&&(i.push(p),g=!0)}if(o!==b){d||(j=i),a=a.replace(l.match[n],"");if(!g)return[];break}}if(a===h)if(g==null)k.error(a);else break;h=a}return j},k.error=function(a){throw"Syntax error, unrecognized expression: "+a};var l=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")},type:function(a){return a.getAttribute("type")}},relative:{"+":function(a,b){var c=typeof b=="string",d=c&&!j.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1);a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}e&&k.filter(b,a,!0)},">":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&k.filter(b,a,!0)}},"":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("parentNode",b,f,a,e,c)},"~":function(a,b,c){var e,f=d++,g=u;typeof b=="string"&&!j.test(b)&&(b=b.toLowerCase(),e=b,g=t),g("previousSibling",b,f,a,e,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(i,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h<i;h++)if(g[h]===a)return!1;return!0}k.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c<f;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}var r,s;c.documentElement.compareDocumentPosition?r=function(a,b){if(a===b){g=!0;return 0}if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(r=function(a,b){if(a===b){g=!0;return 0}if(a.sourceIndex&&b.sourceIndex)return a.sourceIndex-b.sourceIndex;var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(h===i)return s(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return s(e[k],f[k]);return k===c?s(a,f[k],-1):s(e[k],b,1)},s=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}),k.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=k.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)k(a,f[g],d);return k.filter(e,d)};f.find=k,f.expr=k.selectors,f.expr[":"]=f.expr.filters,f.unique=k.uniqueSort,f.text=k.getText,f.isXMLDoc=k.isXML,f.contains=k.contains}();var O=/Until$/,P=/^(?:parents|prevUntil|prevAll)/,Q=/,/,R=/^.[^:#\[\.,]*$/,S=Array.prototype.slice,T=f.expr.match.POS,U={children:!0,contents:!0,next:!0,prev:!0};f.fn.extend({find:function(a){var b=this,c,d;if(typeof a!="string")return f(a).filter(function(){for(c=0,d=b.length;c<d;c++)if(f.contains(b[c],this))return!0});var e=this.pushStack("","find",a),g,h,i;for(c=0,d=this.length;c<d;c++){g=e.length,f.find(a,this[c],e);if(c>0)for(h=g;h<e.length;h++)for(i=0;i<g;i++)if(e[i]===e[h]){e.splice(h--,1);break}}return e},has:function(a){var b=f(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(f.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(W(this,a,!1),"not",a)},filter:function(a){return this.pushStack(W(this,a,!0),"filter",a)},is:function(a){return!!a&&(typeof a=="string"?f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d<e;d++)i=a[d],j[i]||(j[i]=T.test(i)?f(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=T.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d<e;d++){g=this[d];while(g){if(l?l.index(g)>-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(V(c[0])||V(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=S.call(arguments);O.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!U[a]?f.unique(e):e,(this.length>1||Q.test(d))&&P.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var X=/ jQuery\d+="(?:\d+|null)"/g,Y=/^\s+/,Z=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,$=/<([\w:]+)/,_=/<tbody/i,ba=/<|&#?\w+;/,bb=/<(?:script|object|embed|option|style)/i,bc=/checked\s*(?:[^=]|=\s*.checked.)/i,bd=/\/(java|ecma)script/i,be=/^\s*<!(?:\[CDATA\[|\-\-)/,bf={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};bf.optgroup=bf.option,bf.tbody=bf.tfoot=bf.colgroup=bf.caption=bf.thead,bf.th=bf.td,f.support.htmlSerialize||(bf._default=[1,"div<div>","</div>"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(X,""):null;if(typeof a=="string"&&!bb.test(a)&&(f.support.leadingWhitespace||!Y.test(a))&&!bf[($.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Z,"<$1></$2>");try{for(var c=0,d=this.length;c<d;c++)this[c].nodeType===1&&(f.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(e){this.empty().append(a)}}else f.isFunction(a)?this.each(function(b){var c=f(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(f.isFunction(a))return this.each(function(b){var c=f(this),d=c.html();c.replaceWith(a.call(this,b,d))});typeof a!="string"&&(a=f(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;f(this).remove(),b?f(b).before(a):f(c).append(a)})}return this.length?this.pushStack(f(f.isFunction(a)?a():a),"replaceWith",a):this},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,d){var e,g,h,i,j=a[0],k=[];if(!f.support.checkClone&&arguments.length===3&&typeof j=="string"&&bc.test(j))return this.each(function(){f(this).domManip(a,c,d,!0)});if(f.isFunction(j))return this.each(function(e){var g=f(this);a[0]=j.call(this,e,c?g.html():b),g.domManip(a,c,d)});if(this[0]){i=j&&j.parentNode,f.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?e={fragment:i}:e=f.buildFragment(a,this,k),h=e.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&f.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)d.call(c?bg(this[l],g):this[l],e.cacheable||m>1&&l<n?f.clone(h,!0,!0):h)}k.length&&f.each(k,bm)}return this}}),f.buildFragment=function(a,b,d){var e,g,h,i;b&&b[0]&&(i=b[0].ownerDocument||b[0]),i.createDocumentFragment||(i=c),a.length===1&&typeof a[0]=="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!bb.test(a[0])&&(f.support.checkClone||!bc.test(a[0]))&&(g=!0,h=f.fragments[a[0]],h&&h!==1&&(e=h)),e||(e=i.createDocumentFragment(),f.clean(a,i,e,d)),g&&(f.fragments[a[0]]=h?e:1);return{fragment:e,cacheable:g}},f.fragments={},f.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){f.fn[a]=function(c){var d=[],e=f(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&e.length===1){e[b](this[0]);return this}for(var h=0,i=e.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j
+)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bi(a,d),e=bj(a),g=bj(d);for(h=0;e[h];++h)bi(e[h],g[h])}if(b){bh(a,d);if(c){e=bj(a),g=bj(d);for(h=0;e[h];++h)bh(e[h],g[h])}}e=g=null;return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!ba.test(k))k=b.createTextNode(k);else{k=k.replace(Z,"<$1></$2>");var l=($.exec(k)||["",""])[1].toLowerCase(),m=bf[l]||bf._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=_.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]==="<table>"&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Y.test(k)&&o.insertBefore(b.createTextNode(Y.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i<r;i++)bl(k[i]);else bl(k);k.nodeType?h.push(k):h=f.merge(h,k)}if(d){g=function(a){return!a.type||bd.test(a.type)};for(j=0;h[j];j++)if(e&&f.nodeName(h[j],"script")&&(!h[j].type||h[j].type.toLowerCase()==="text/javascript"))e.push(h[j].parentNode?h[j].parentNode.removeChild(h[j]):h[j]);else{if(h[j].nodeType===1){var s=f.grep(h[j].getElementsByTagName("script"),g);h.splice.apply(h,[j+1,0].concat(s))}d.appendChild(h[j])}}return h},cleanData:function(a){var b,c,d=f.cache,e=f.expando,g=f.event.special,h=f.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&f.noData[j.nodeName.toLowerCase()])continue;c=j[f.expando];if(c){b=d[c]&&d[c][e];if(b&&b.events){for(var k in b.events)g[k]?f.event.remove(j,k):f.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[f.expando]:j.removeAttribute&&j.removeAttribute(f.expando),delete d[c]}}}});var bn=/alpha\([^)]*\)/i,bo=/opacity=([^)]*)/,bp=/([A-Z]|^ms)/g,bq=/^-?\d+(?:px)?$/i,br=/^-?\d/,bs=/^[+\-]=/,bt=/[^+\-\.\de]+/g,bu={position:"absolute",visibility:"hidden",display:"block"},bv=["Left","Right"],bw=["Top","Bottom"],bx,by,bz;f.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return f.access(this,a,c,!0,function(a,c,d){return d!==b?f.style(a,c,d):f.css(a,c)})},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bx(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d;if(h==="number"&&isNaN(d)||d==null)return;h==="string"&&bs.test(d)&&(d=+d.replace(bt,"")+parseFloat(f.css(a,c)),h="number"),h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(bx)return bx(a,c)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]}}),f.curCSS=f.css,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){var e;if(c){if(a.offsetWidth!==0)return bA(a,b,d);f.swap(a,bu,function(){e=bA(a,b,d)});return e}},set:function(a,b){if(!bq.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bo.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bn.test(g)?g.replace(bn,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bx(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(by=function(a,c){var d,e,g;c=c.replace(bp,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bz=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bq.test(d)&&br.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bx=by||bz,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bB=/%20/g,bC=/\[\]$/,bD=/\r?\n/g,bE=/#.*$/,bF=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bG=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bH=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bI=/^(?:GET|HEAD)$/,bJ=/^\/\//,bK=/\?/,bL=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,bM=/^(?:select|textarea)/i,bN=/\s+/,bO=/([?&])_=[^&]*/,bP=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bQ=f.fn.load,bR={},bS={},bT,bU;try{bT=e.href}catch(bV){bT=c.createElement("a"),bT.href="",bT=bT.href}bU=bP.exec(bT.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bQ)return bQ.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("<div>").append(c.replace(bL,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bM.test(this.nodeName)||bG.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bD,"\r\n")}}):{name:b.name,value:c.replace(bD,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bT,isLocal:bH.test(bU[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bW(bR),ajaxTransport:bW(bS),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?bZ(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=b$(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bF.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bE,"").replace(bJ,bU[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bN),d.crossDomain==null&&(r=bP.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bU[1]&&r[2]==bU[2]&&(r[3]||(r[1]==="http:"?80:443))==(bU[3]||(bU[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bX(bR,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bI.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bK.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bO,"$1_="+x);d.url=y+(y===d.url?(bK.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bX(bS,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)bY(g,a[g],c,e);return d.join("&").replace(bB,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var b_=f.now(),ca=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+b_++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(ca.test(b.url)||e&&ca.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(ca,l),b.url===j&&(e&&(k=k.replace(ca,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var cb=a.ActiveXObject?function(){for(var a in cd)cd[a](0,1)}:!1,cc=0,cd;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ce()||cf()}:ce,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,cb&&delete cd[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cc,cb&&(cd||(cd={},f(a).unload(cb)),cd[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cg={},ch,ci,cj=/^(?:toggle|show|hide)$/,ck=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cl,cm=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cn,co=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cr("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)d=this[g],d.style&&(e=d.style.display,!f._data(d,"olddisplay")&&e==="none"&&(e=d.style.display=""),e===""&&f.css(d,"display")==="none"&&f._data(d,"olddisplay",cs(d.nodeName)));for(g=0;g<h;g++){d=this[g];if(d.style){e=d.style.display;if(e===""||e==="none")d.style.display=f._data(d,"olddisplay")||""}}return this},hide:function(a,b,c){if(a||a===0)return this.animate(cr("hide",3),a,b,c);for(var d=0,e=this.length;d<e;d++)if(this[d].style){var g=f.css(this[d],"display");g!=="none"&&!f._data(this[d],"olddisplay")&&f._data(this[d],"olddisplay",g)}for(d=0;d<e;d++)this[d].style&&(this[d].style.display="none");return this},_toggle:f.fn.toggle,toggle:function(a,b,c){var d=typeof a=="boolean";f.isFunction(a)&&f.isFunction(b)?this._toggle.apply(this,arguments):a==null||d?this.each(function(){var b=d?a:f(this).is(":hidden");f(this)[b?"show":"hide"]()}):this.animate(cr("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=f.speed(b,c,d);if(f.isEmptyObject(a))return this.each(e.complete,[!1]);a=f.extend({},a);return this[e.queue===!1?"each":"queue"](function(){e.queue===!1&&f._mark(this);var b=f.extend({},e),c=this.nodeType===1,d=c&&f(this).is(":hidden"),g,h,i,j,k,l,m,n,o;b.animatedProperties={};for(i in a){g=f.camelCase(i),i!==g&&(a[g]=a[i],delete a[i]),h=a[g],f.isArray(h)?(b.animatedProperties[g]=h[1],h=a[g]=h[0]):b.animatedProperties[g]=b.specialEasing&&b.specialEasing[g]||b.easing||"swing";if(h==="hide"&&d||h==="show"&&!d)return b.complete.call(this);c&&(g==="height"||g==="width")&&(b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY],f.css(this,"display")==="inline"&&f.css(this,"float")==="none"&&(f.support.inlineBlockNeedsLayout?(j=cs(this.nodeName),j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)):this.style.display="inline-block"))}b.overflow!=null&&(this.style.overflow="hidden");for(i in a)k=new f.fx(this,b,i),h=a[i],cj.test(h)?k[h==="toggle"?d?"show":"hide":h]():(l=ck.exec(h),m=k.cur(),l?(n=parseFloat(l[2]),o=l[3]||(f.cssNumber[i]?"":"px"),o!=="px"&&(f.style(this,i,(n||1)+o),m=(n||1)/k.cur()*m,f.style(this,i,m+o)),l[1]&&(n=(l[1]==="-="?-1:1)*n+m),k.custom(m,n,o)):k.custom(m,h,""));return!0})},stop:function(a,b){a&&this.queue([]),this.each(function(){var a=f.timers,c=a.length;b||f._unmark(!0,this);while(c--)a[c].elem===this&&(b&&a[c](!0),a.splice(c,1))}),b||this.dequeue();return this}}),f.each({slideDown:cr("show",1),slideUp:cr("hide",1),slideToggle:cr("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){f.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),f.extend({speed:function(a,b,c){var d=a&&typeof a=="object"?f.extend({},a):{complete:c||!c&&b||f.isFunction(a)&&a,duration:a,easing:c&&b||b&&!f.isFunction(b)&&b};d.duration=f.fx.off?0:typeof d.duration=="number"?d.duration:d.duration in f.fx.speeds?f.fx.speeds[d.duration]:f.fx.speeds._default,d.old=d.complete,d.complete=function(a){f.isFunction(d.old)&&d.old.call(this),d.queue!==!1?f.dequeue(this):a!==!1&&f._unmark(this)};return d},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig=b.orig||{}}}),f.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(f.fx.step[this.prop]||f.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=f.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function h(a){return d.step(a)}var d=this,e=f.fx,g;this.startTime=cn||cp(),this.start=a,this.end=b,this.unit=c||this.unit||(f.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,h.elem=this.elem,h()&&f.timers.push(h)&&!cl&&(co?(cl=!0,g=function(){cl&&(co(g),e.tick())},co(g)):cl=setInterval(e.tick,e.interval))},show:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),f(this.elem).show()},hide:function(){this.options.orig[this.prop]=f.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=cn||cp(),c=!0,d=this.elem,e=this.options,g,h;if(a||b>=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b<a.length;++b)a[b]()||a.splice(b--,1);a.length||f.fx.stop()},interval:13,stop:function(){clearInterval(cl),cl=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){f.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),f.expr&&f.expr.filters&&(f.expr.filters.animated=function(a){return f.grep(f.timers,function(b){return a===b.elem}).length});var ct=/^t(?:able|d|h)$/i,cu=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?f.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(d){}var e=b.ownerDocument,g=e.documentElement;if(!c||!f.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=e.body,i=cv(e),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||f.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||f.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:f.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){f.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return f.offset.bodyOffset(b);f.offset.initialize();var c,d=b.offsetParent,e=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(f.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===d&&(l+=b.offsetTop,m+=b.offsetLeft,f.offset.doesNotAddBorder&&(!f.offset.doesAddBorderForTableAndCells||!ct.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),e=d,d=b.offsetParent),f.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;f.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},f.offset={initialize:function(){var a=c.body,b=c.createElement("div"),d,e,g,h,i=parseFloat(f.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cu.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cu.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cv(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cv(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){var a=this[0];return a&&a.style?parseFloat(f.css(a,d,"padding")):null},f.fn["outer"+c]=function(a){var b=this[0];return b&&b.style?parseFloat(f.css(b,d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window);
\ No newline at end of file
diff --git a/core/js/jquery-showpassword.js b/core/js/jquery-showpassword.js
new file mode 100644
index 0000000000000000000000000000000000000000..0f4678327a3bc64a2d4dbdb1ee2213993e6ab9e2
--- /dev/null
+++ b/core/js/jquery-showpassword.js
@@ -0,0 +1,113 @@
+/*
+*	@name							Show Password
+*	@descripton						
+*	@version						1.3
+*	@requires						Jquery 1.5
+*
+*	@author							Jan Jarfalk
+*	@author-email					jan.jarfalk@unwrongest.com
+*	@author-website					http://www.unwrongest.com
+*
+*	@special-thanks					Michel Gratton
+*
+*	@licens							MIT License - http://www.opensource.org/licenses/mit-license.php
+*/
+(function($){
+     $.fn.extend({
+         showPassword: function(c) {	
+            
+            // Setup callback object
+			var callback 	= {'fn':null,'args':{}}
+				callback.fn = c;
+			
+			// Clones passwords and turn the clones into text inputs
+			var cloneElement = function( element ) {
+				
+				var $element = $(element);
+					
+				$clone = $("<input />");
+					
+				// Name added for JQuery Validation compatibility
+				// Element name is required to avoid script warning.
+				$clone.attr({
+					'type'		:	'text',
+					'class'		:	$element.attr('class'),
+					'style'		:	$element.attr('style'),
+					'size'		:	$element.attr('size'),
+					'name'		:	$element.attr('name')+'-clone',
+					'tabindex' 	:	$element.attr('tabindex')
+				});
+					
+				return $clone;
+			
+			};
+			
+			// Transfers values between two elements
+			var update = function(a,b){
+				b.val(a.val());
+			};
+			
+			// Shows a or b depending on checkbox
+			var setState = function( checkbox, a, b ){
+			
+				if(checkbox.is(':checked')){
+					update(a,b);
+					b.show();
+					a.hide();
+				} else {
+					update(b,a);
+					b.hide();
+					a.show();
+				}
+				
+			};
+            
+            return this.each(function() {
+            	
+            	var $input					= $(this),
+            		$checkbox 				= $($input.data('typetoggle'));
+            	
+            	// Create clone
+				var $clone = cloneElement($input);
+					$clone.insertAfter($input);
+				
+				// Set callback arguments
+            	if(callback.fn){	
+            		callback.args.input		= $input;
+            		callback.args.checkbox	= $checkbox;
+					callback.args.clone 	= $clone;
+            	}
+				
+
+				
+				$checkbox.bind('click', function() {
+					setState( $checkbox, $input, $clone );
+				});
+				
+				$input.bind('keyup', function() {
+					update( $input, $clone )
+				});
+				
+				$clone.bind('keyup', function(){ 
+					update( $clone, $input );
+					
+					// Added for JQuery Validation compatibility
+					// This will trigger validation if it's ON for keyup event
+					$input.trigger('keyup');
+					
+				});
+				
+				// Added for JQuery Validation compatibility
+				// This will trigger validation if it's ON for blur event
+				$clone.bind('blur', function() { $input.trigger('focusout'); });
+				
+				setState( $checkbox, $input, $clone );
+				
+				if( callback.fn ){
+					callback.fn( callback.args );
+				}
+
+            });
+        }
+    });
+})(jQuery);
diff --git a/core/js/jquery-tipsy.js b/core/js/jquery-tipsy.js
new file mode 100644
index 0000000000000000000000000000000000000000..9567ed3bacc7928be61db0297211b06493b072d6
--- /dev/null
+++ b/core/js/jquery-tipsy.js
@@ -0,0 +1,241 @@
+// tipsy, facebook style tooltips for jquery
+// version 1.0.0a
+// (c) 2008-2010 jason frame [jason@onehackoranother.com]
+// released under the MIT license
+
+(function($) {
+    
+    function maybeCall(thing, ctx) {
+        return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
+    };
+    
+    function Tipsy(element, options) {
+        this.$element = $(element);
+        this.options = options;
+        this.enabled = true;
+        this.fixTitle();
+    };
+    
+    Tipsy.prototype = {
+        show: function() {
+            var title = this.getTitle();
+            if (title && this.enabled) {
+                var $tip = this.tip();
+                
+                $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
+                $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
+                $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
+                
+                var pos = $.extend({}, this.$element.offset(), {
+                    width: this.$element[0].offsetWidth,
+                    height: this.$element[0].offsetHeight
+                });
+                
+                var actualWidth = $tip[0].offsetWidth,
+                    actualHeight = $tip[0].offsetHeight,
+                    gravity = maybeCall(this.options.gravity, this.$element[0]);
+                
+                var tp;
+                switch (gravity.charAt(0)) {
+                    case 'n':
+                        tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
+                        break;
+                    case 's':
+                        tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
+                        break;
+                    case 'e':
+                        tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
+                        break;
+                    case 'w':
+                        tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
+                        break;
+                }
+                
+                if (gravity.length == 2) {
+                    if (gravity.charAt(1) == 'w') {
+                        tp.left = pos.left + pos.width / 2 - 15;
+                    } else {
+                        tp.left = pos.left + pos.width / 2 - actualWidth + 15;
+                    }
+                }
+                
+                $tip.css(tp).addClass('tipsy-' + gravity);
+                $tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
+                if (this.options.className) {
+                    $tip.addClass(maybeCall(this.options.className, this.$element[0]));
+                }
+                
+                if (this.options.fade) {
+                    $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
+                } else {
+                    $tip.css({visibility: 'visible', opacity: this.options.opacity});
+                }
+            }
+        },
+        
+        hide: function() {
+            if (this.options.fade) {
+                this.tip().stop().fadeOut(function() { $(this).remove(); });
+            } else {
+                this.tip().remove();
+            }
+        },
+        
+        fixTitle: function() {
+            var $e = this.$element;
+            if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
+                $e.attr('original-title', $e.attr('title') || '').removeAttr('title');
+            }
+        },
+        
+        getTitle: function() {
+            var title, $e = this.$element, o = this.options;
+            this.fixTitle();
+            var title, o = this.options;
+            if (typeof o.title == 'string') {
+                title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
+            } else if (typeof o.title == 'function') {
+                title = o.title.call($e[0]);
+            }
+            title = ('' + title).replace(/(^\s*|\s*$)/, "");
+            return title || o.fallback;
+        },
+        
+        tip: function() {
+            if (!this.$tip) {
+                this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
+            }
+            return this.$tip;
+        },
+        
+        validate: function() {
+            if (!this.$element[0].parentNode) {
+                this.hide();
+                this.$element = null;
+                this.options = null;
+            }
+        },
+        
+        enable: function() { this.enabled = true; },
+        disable: function() { this.enabled = false; },
+        toggleEnabled: function() { this.enabled = !this.enabled; }
+    };
+    
+    $.fn.tipsy = function(options) {
+        
+        if (options === true) {
+            return this.data('tipsy');
+        } else if (typeof options == 'string') {
+            var tipsy = this.data('tipsy');
+            if (tipsy) tipsy[options]();
+            return this;
+        }
+        
+        options = $.extend({}, $.fn.tipsy.defaults, options);
+        
+        function get(ele) {
+            var tipsy = $.data(ele, 'tipsy');
+            if (!tipsy) {
+                tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
+                $.data(ele, 'tipsy', tipsy);
+            }
+            return tipsy;
+        }
+        
+        function enter() {
+            var tipsy = get(this);
+            tipsy.hoverState = 'in';
+            if (options.delayIn == 0) {
+                tipsy.show();
+            } else {
+                tipsy.fixTitle();
+                setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
+            }
+        };
+        
+        function leave() {
+            var tipsy = get(this);
+            tipsy.hoverState = 'out';
+            if (options.delayOut == 0) {
+                tipsy.hide();
+            } else {
+                setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
+            }
+        };
+        
+        if (!options.live) this.each(function() { get(this); });
+        
+        if (options.trigger != 'manual') {
+            var binder   = options.live ? 'live' : 'bind',
+                eventIn  = options.trigger == 'hover' ? 'mouseenter' : 'focus',
+                eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
+            this[binder](eventIn, enter)[binder](eventOut, leave);
+        }
+        
+        return this;
+        
+    };
+    
+    $.fn.tipsy.defaults = {
+        className: null,
+        delayIn: 0,
+        delayOut: 0,
+        fade: false,
+        fallback: '',
+        gravity: 'n',
+        html: false,
+        live: false,
+        offset: 0,
+        opacity: 0.8,
+        title: 'title',
+        trigger: 'hover'
+    };
+    
+    // Overwrite this method to provide options on a per-element basis.
+    // For example, you could store the gravity in a 'tipsy-gravity' attribute:
+    // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
+    // (remember - do not modify 'options' in place!)
+    $.fn.tipsy.elementOptions = function(ele, options) {
+        return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
+    };
+    
+    $.fn.tipsy.autoNS = function() {
+        return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
+    };
+    
+    $.fn.tipsy.autoWE = function() {
+        return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
+    };
+    
+    /**
+     * yields a closure of the supplied parameters, producing a function that takes
+     * no arguments and is suitable for use as an autogravity function like so:
+     *
+     * @param margin (int) - distance from the viewable region edge that an
+     *        element should be before setting its tooltip's gravity to be away
+     *        from that edge.
+     * @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
+     *        if there are no viewable region edges effecting the tooltip's
+     *        gravity. It will try to vary from this minimally, for example,
+     *        if 'sw' is preferred and an element is near the right viewable 
+     *        region edge, but not the top edge, it will set the gravity for
+     *        that element's tooltip to be 'se', preserving the southern
+     *        component.
+     */
+     $.fn.tipsy.autoBounds = function(margin, prefer) {
+		return function() {
+			var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
+			    boundTop = $(document).scrollTop() + margin,
+			    boundLeft = $(document).scrollLeft() + margin,
+			    $this = $(this);
+
+			if ($this.offset().top < boundTop) dir.ns = 'n';
+			if ($this.offset().left < boundLeft) dir.ew = 'w';
+			if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
+			if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
+
+			return dir.ns + (dir.ew ? dir.ew : '');
+		}
+	};
+    
+})(jQuery);
diff --git a/core/js/jquery-ui-1.8.14.custom.min.js b/core/js/jquery-ui-1.8.14.custom.min.js
new file mode 100644
index 0000000000000000000000000000000000000000..f9e4f1e840001850557fc95f918af50bc7628a7c
--- /dev/null
+++ b/core/js/jquery-ui-1.8.14.custom.min.js
@@ -0,0 +1,789 @@
+/*!
+ * jQuery UI 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI
+ */
+(function(c,j){function k(a,b){var d=a.nodeName.toLowerCase();if("area"===d){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&l(a)}return(/input|select|textarea|button|object/.test(d)?!a.disabled:"a"==d?a.href||b:b)&&l(a)}function l(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.14",
+keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();
+b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this,"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,
+"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position");if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",
+function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,m,n){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(m)g-=parseFloat(c.curCSS(f,"border"+this+"Width",true))||0;if(n)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,
+outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h,d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){return k(a,!isNaN(c.attr(a,"tabindex")))},tabbable:function(a){var b=c.attr(a,"tabindex"),d=isNaN(b);
+return(d||b>=0)&&k(a,!d)}});c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=
+0;e<b.length;e++)a.options[b[e][0]]&&b[e][1].apply(a.element,d)}},contains:function(a,b){return document.compareDocumentPosition?a.compareDocumentPosition(b)&16:a!==b&&a.contains(b)},hasScroll:function(a,b){if(c(a).css("overflow")==="hidden")return false;b=b&&b==="left"?"scrollLeft":"scrollTop";var d=false;if(a[b]>0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a<b+d},isOver:function(a,b,d,e,h,i){return c.ui.isOverAxis(a,d,h)&&c.ui.isOverAxis(b,e,i)}})}})(jQuery);
+;/*!
+ * jQuery UI Widget 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function(b,j){if(b.cleanData){var k=b.cleanData;b.cleanData=function(a){for(var c=0,d;(d=a[c])!=null;c++)b(d).triggerHandler("remove");k(a)}}else{var l=b.fn.remove;b.fn.remove=function(a,c){return this.each(function(){if(!c)if(!a||b.filter(a,[this]).length)b("*",this).add([this]).each(function(){b(this).triggerHandler("remove")});return l.call(b(this),a,c)})}}b.widget=function(a,c,d){var e=a.split(".")[0],f;a=a.split(".")[1];f=e+"-"+a;if(!d){d=c;c=b.Widget}b.expr[":"][f]=function(h){return!!b.data(h,
+a)};b[e]=b[e]||{};b[e][a]=function(h,g){arguments.length&&this._createWidget(h,g)};c=new c;c.options=b.extend(true,{},c.options);b[e][a].prototype=b.extend(true,c,{namespace:e,widgetName:a,widgetEventPrefix:b[e][a].prototype.widgetEventPrefix||a,widgetBaseClass:f},d);b.widget.bridge(a,b[e][a])};b.widget.bridge=function(a,c){b.fn[a]=function(d){var e=typeof d==="string",f=Array.prototype.slice.call(arguments,1),h=this;d=!e&&f.length?b.extend.apply(null,[true,d].concat(f)):d;if(e&&d.charAt(0)==="_")return h;
+e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g,f):g;if(i!==g&&i!==j){h=i;return false}}):this.each(function(){var g=b.data(this,a);g?g.option(d||{})._init():b.data(this,a,new c(d,this))});return h}};b.Widget=function(a,c){arguments.length&&this._createWidget(a,c)};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(a,c){b.data(c,this.widgetName,this);this.element=b(c);this.options=b.extend(true,{},this.options,
+this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},
+widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this},
+enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery);
+;/*!
+ * jQuery UI Mouse 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Mouse
+ *
+ * Depends:
+ *	jquery.ui.widget.js
+ */
+(function(b){var d=false;b(document).mousedown(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+
+this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!==
+false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&&
+!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=
+false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery);
+;/*
+ * jQuery UI Position 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Position
+ */
+(function(c){c.ui=c.ui||{};var n=/left|center|right/,o=/top|center|bottom/,t=c.fn.position,u=c.fn.offset;c.fn.position=function(b){if(!b||!b.of)return t.apply(this,arguments);b=c.extend({},b);var a=c(b.of),d=a[0],g=(b.collision||"flip").split(" "),e=b.offset?b.offset.split(" "):[0,0],h,k,j;if(d.nodeType===9){h=a.width();k=a.height();j={top:0,left:0}}else if(d.setTimeout){h=a.width();k=a.height();j={top:a.scrollTop(),left:a.scrollLeft()}}else if(d.preventDefault){b.at="left top";h=k=0;j={top:b.of.pageY,
+left:b.of.pageX}}else{h=a.outerWidth();k=a.outerHeight();j=a.offset()}c.each(["my","at"],function(){var f=(b[this]||"").split(" ");if(f.length===1)f=n.test(f[0])?f.concat(["center"]):o.test(f[0])?["center"].concat(f):["center","center"];f[0]=n.test(f[0])?f[0]:"center";f[1]=o.test(f[1])?f[1]:"center";b[this]=f});if(g.length===1)g[1]=g[0];e[0]=parseInt(e[0],10)||0;if(e.length===1)e[1]=e[0];e[1]=parseInt(e[1],10)||0;if(b.at[0]==="right")j.left+=h;else if(b.at[0]==="center")j.left+=h/2;if(b.at[1]==="bottom")j.top+=
+k;else if(b.at[1]==="center")j.top+=k/2;j.left+=e[0];j.top+=e[1];return this.each(function(){var f=c(this),l=f.outerWidth(),m=f.outerHeight(),p=parseInt(c.curCSS(this,"marginLeft",true))||0,q=parseInt(c.curCSS(this,"marginTop",true))||0,v=l+p+(parseInt(c.curCSS(this,"marginRight",true))||0),w=m+q+(parseInt(c.curCSS(this,"marginBottom",true))||0),i=c.extend({},j),r;if(b.my[0]==="right")i.left-=l;else if(b.my[0]==="center")i.left-=l/2;if(b.my[1]==="bottom")i.top-=m;else if(b.my[1]==="center")i.top-=
+m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top-q};c.each(["left","top"],function(s,x){c.ui.position[g[s]]&&c.ui.position[g[s]][x](i,{targetWidth:h,targetHeight:k,elemWidth:l,elemHeight:m,collisionPosition:r,collisionWidth:v,collisionHeight:w,offset:e,my:b.my,at:b.at})});c.fn.bgiframe&&f.bgiframe();f.offset(c.extend(i,{using:b.using}))})};c.ui.position={fit:{left:function(b,a){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();b.left=
+d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+=
+a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b),
+g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery);
+;/*
+ * jQuery UI Draggable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Draggables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper==
+"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b=
+this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('<div class="ui-draggable-iframeFix" style="background: #fff;"></div>').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper=
+this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});
+this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true},
+_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b=
+false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,
+10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle||
+!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&&
+a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=
+this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),
+10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),
+10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,
+(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!=
+"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),
+10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+
+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&
+!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.left<g[0])e=g[0]+this.offset.click.left;
+if(a.pageY-this.offset.click.top<g[1])h=g[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>g[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.top<g[1]||h-this.offset.click.top>g[3])?h:!(h-this.offset.click.top<g[1])?h-b.grid[1]:h+b.grid[1]:h;e=b.grid[0]?this.originalPageX+Math.round((e-this.originalPageX)/
+b.grid[0])*b.grid[0]:this.originalPageX;e=g?!(e-this.offset.click.left<g[0]||e-this.offset.click.left>g[2])?e:!(e-this.offset.click.left<g[0])?e-b.grid[0]:e+b.grid[0]:e}}return{top:h-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop()),left:e-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&d.browser.version<
+526&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())}},_clear:function(){this.helper.removeClass("ui-draggable-dragging");this.helper[0]!=this.element[0]&&!this.cancelHelperRemoval&&this.helper.remove();this.helper=null;this.cancelHelperRemoval=false},_trigger:function(a,b,c){c=c||this._uiHash();d.ui.plugin.call(this,a,[b,c]);if(a=="drag")this.positionAbs=this._convertPositionTo("absolute");return d.Widget.prototype._trigger.call(this,a,b,
+c)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}});d.extend(d.ui.draggable,{version:"1.8.14"});d.ui.plugin.add("draggable","connectToSortable",{start:function(a,b){var c=d(this).data("draggable"),f=c.options,e=d.extend({},b,{item:c.element});c.sortables=[];d(f.connectToSortable).each(function(){var h=d.data(this,"sortable");if(h&&!h.options.disabled){c.sortables.push({instance:h,shouldRevert:h.options.revert});
+h.refreshPositions();h._trigger("activate",a,e)}})},stop:function(a,b){var c=d(this).data("draggable"),f=d.extend({},b,{item:c.element});d.each(c.sortables,function(){if(this.instance.isOver){this.instance.isOver=0;c.cancelHelperRemoval=true;this.instance.cancelHelperRemoval=false;if(this.shouldRevert)this.instance.options.revert=true;this.instance._mouseStop(a);this.instance.options.helper=this.instance.options._helper;c.options.helper=="original"&&this.instance.currentItem.css({top:"auto",left:"auto"})}else{this.instance.cancelHelperRemoval=
+false;this.instance._trigger("deactivate",a,f)}})},drag:function(a,b){var c=d(this).data("draggable"),f=this;d.each(c.sortables,function(){this.instance.positionAbs=c.positionAbs;this.instance.helperProportions=c.helperProportions;this.instance.offset.click=c.offset.click;if(this.instance._intersectsWith(this.instance.containerCache)){if(!this.instance.isOver){this.instance.isOver=1;this.instance.currentItem=d(f).clone().removeAttr("id").appendTo(this.instance.element).data("sortable-item",true);
+this.instance.options._helper=this.instance.options.helper;this.instance.options.helper=function(){return b.helper[0]};a.target=this.instance.currentItem[0];this.instance._mouseCapture(a,true);this.instance._mouseStart(a,true,true);this.instance.offset.click.top=c.offset.click.top;this.instance.offset.click.left=c.offset.click.left;this.instance.offset.parent.left-=c.offset.parent.left-this.instance.offset.parent.left;this.instance.offset.parent.top-=c.offset.parent.top-this.instance.offset.parent.top;
+c._trigger("toSortable",a);c.dropped=this.instance.element;c.currentItem=c.element;this.instance.fromOutside=c}this.instance.currentItem&&this.instance._mouseDrag(a)}else if(this.instance.isOver){this.instance.isOver=0;this.instance.cancelHelperRemoval=true;this.instance.options.revert=false;this.instance._trigger("out",a,this.instance._uiHash(this.instance));this.instance._mouseStop(a,true);this.instance.options.helper=this.instance.options._helper;this.instance.currentItem.remove();this.instance.placeholder&&
+this.instance.placeholder.remove();c._trigger("fromSortable",a);c.dropped=false}})}});d.ui.plugin.add("draggable","cursor",{start:function(){var a=d("body"),b=d(this).data("draggable").options;if(a.css("cursor"))b._cursor=a.css("cursor");a.css("cursor",b.cursor)},stop:function(){var a=d(this).data("draggable").options;a._cursor&&d("body").css("cursor",a._cursor)}});d.ui.plugin.add("draggable","opacity",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("opacity"))b._opacity=
+a.css("opacity");a.css("opacity",b.opacity)},stop:function(a,b){a=d(this).data("draggable").options;a._opacity&&d(b.helper).css("opacity",a._opacity)}});d.ui.plugin.add("draggable","scroll",{start:function(){var a=d(this).data("draggable");if(a.scrollParent[0]!=document&&a.scrollParent[0].tagName!="HTML")a.overflowOffset=a.scrollParent.offset()},drag:function(a){var b=d(this).data("draggable"),c=b.options,f=false;if(b.scrollParent[0]!=document&&b.scrollParent[0].tagName!="HTML"){if(!c.axis||c.axis!=
+"x")if(b.overflowOffset.top+b.scrollParent[0].offsetHeight-a.pageY<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop+c.scrollSpeed;else if(a.pageY-b.overflowOffset.top<c.scrollSensitivity)b.scrollParent[0].scrollTop=f=b.scrollParent[0].scrollTop-c.scrollSpeed;if(!c.axis||c.axis!="y")if(b.overflowOffset.left+b.scrollParent[0].offsetWidth-a.pageX<c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft+c.scrollSpeed;else if(a.pageX-b.overflowOffset.left<
+c.scrollSensitivity)b.scrollParent[0].scrollLeft=f=b.scrollParent[0].scrollLeft-c.scrollSpeed}else{if(!c.axis||c.axis!="x")if(a.pageY-d(document).scrollTop()<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()-c.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<c.scrollSensitivity)f=d(document).scrollTop(d(document).scrollTop()+c.scrollSpeed);if(!c.axis||c.axis!="y")if(a.pageX-d(document).scrollLeft()<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()-
+c.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<c.scrollSensitivity)f=d(document).scrollLeft(d(document).scrollLeft()+c.scrollSpeed)}f!==false&&d.ui.ddmanager&&!c.dropBehaviour&&d.ui.ddmanager.prepareOffsets(b,a)}});d.ui.plugin.add("draggable","snap",{start:function(){var a=d(this).data("draggable"),b=a.options;a.snapElements=[];d(b.snap.constructor!=String?b.snap.items||":data(draggable)":b.snap).each(function(){var c=d(this),f=c.offset();this!=a.element[0]&&a.snapElements.push({item:this,
+width:c.outerWidth(),height:c.outerHeight(),top:f.top,left:f.left})})},drag:function(a,b){for(var c=d(this).data("draggable"),f=c.options,e=f.snapTolerance,h=b.offset.left,g=h+c.helperProportions.width,n=b.offset.top,o=n+c.helperProportions.height,i=c.snapElements.length-1;i>=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e<h&&h<l+e&&k-e<n&&n<m+e||j-e<h&&h<l+e&&k-e<o&&o<m+e||j-e<g&&g<l+e&&k-e<n&&n<m+e||j-e<g&&g<l+e&&k-e<o&&
+o<m+e){if(f.snapMode!="inner"){var p=Math.abs(k-o)<=e,q=Math.abs(m-n)<=e,r=Math.abs(j-g)<=e,s=Math.abs(l-h)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k-c.helperProportions.height,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j-c.helperProportions.width}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l}).left-c.margins.left}var t=
+p||q||r||s;if(f.snapMode!="outer"){p=Math.abs(k-n)<=e;q=Math.abs(m-o)<=e;r=Math.abs(j-h)<=e;s=Math.abs(l-g)<=e;if(p)b.position.top=c._convertPositionTo("relative",{top:k,left:0}).top-c.margins.top;if(q)b.position.top=c._convertPositionTo("relative",{top:m-c.helperProportions.height,left:0}).top-c.margins.top;if(r)b.position.left=c._convertPositionTo("relative",{top:0,left:j}).left-c.margins.left;if(s)b.position.left=c._convertPositionTo("relative",{top:0,left:l-c.helperProportions.width}).left-c.margins.left}if(!c.snapElements[i].snapping&&
+(p||q||r||s||t))c.options.snap.snap&&c.options.snap.snap.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=p||q||r||s||t}else{c.snapElements[i].snapping&&c.options.snap.release&&c.options.snap.release.call(c.element,a,d.extend(c._uiHash(),{snapItem:c.snapElements[i].item}));c.snapElements[i].snapping=false}}}});d.ui.plugin.add("draggable","stack",{start:function(){var a=d(this).data("draggable").options;a=d.makeArray(d(a.stack)).sort(function(c,f){return(parseInt(d(c).css("zIndex"),
+10)||0)-(parseInt(d(f).css("zIndex"),10)||0)});if(a.length){var b=parseInt(a[0].style.zIndex)||0;d(a).each(function(c){this.style.zIndex=b+c});this[0].style.zIndex=b+a.length}}});d.ui.plugin.add("draggable","zIndex",{start:function(a,b){a=d(b.helper);b=d(this).data("draggable").options;if(a.css("zIndex"))b._zIndex=a.css("zIndex");a.css("zIndex",b.zIndex)},stop:function(a,b){a=d(this).data("draggable").options;a._zIndex&&d(b.helper).css("zIndex",a._zIndex)}})})(jQuery);
+;/*
+ * jQuery UI Droppable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Droppables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.draggable.js
+ */
+(function(d){d.widget("ui.droppable",{widgetEventPrefix:"drop",options:{accept:"*",activeClass:false,addClasses:true,greedy:false,hoverClass:false,scope:"default",tolerance:"intersect"},_create:function(){var a=this.options,b=a.accept;this.isover=0;this.isout=1;this.accept=d.isFunction(b)?b:function(c){return c.is(b)};this.proportions={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight};d.ui.ddmanager.droppables[a.scope]=d.ui.ddmanager.droppables[a.scope]||[];d.ui.ddmanager.droppables[a.scope].push(this);
+a.addClasses&&this.element.addClass("ui-droppable")},destroy:function(){for(var a=d.ui.ddmanager.droppables[this.options.scope],b=0;b<a.length;b++)a[b]==this&&a.splice(b,1);this.element.removeClass("ui-droppable ui-droppable-disabled").removeData("droppable").unbind(".droppable");return this},_setOption:function(a,b){if(a=="accept")this.accept=d.isFunction(b)?b:function(c){return c.is(b)};d.Widget.prototype._setOption.apply(this,arguments)},_activate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&
+this.element.addClass(this.options.activeClass);b&&this._trigger("activate",a,this.ui(b))},_deactivate:function(a){var b=d.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass);b&&this._trigger("deactivate",a,this.ui(b))},_over:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.addClass(this.options.hoverClass);
+this._trigger("over",a,this.ui(b))}},_out:function(a){var b=d.ui.ddmanager.current;if(!(!b||(b.currentItem||b.element)[0]==this.element[0]))if(this.accept.call(this.element[0],b.currentItem||b.element)){this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("out",a,this.ui(b))}},_drop:function(a,b){var c=b||d.ui.ddmanager.current;if(!c||(c.currentItem||c.element)[0]==this.element[0])return false;var e=false;this.element.find(":data(droppable)").not(".ui-draggable-dragging").each(function(){var g=
+d.data(this,"droppable");if(g.options.greedy&&!g.options.disabled&&g.options.scope==c.options.scope&&g.accept.call(g.element[0],c.currentItem||c.element)&&d.ui.intersect(c,d.extend(g,{offset:g.element.offset()}),g.options.tolerance)){e=true;return false}});if(e)return false;if(this.accept.call(this.element[0],c.currentItem||c.element)){this.options.activeClass&&this.element.removeClass(this.options.activeClass);this.options.hoverClass&&this.element.removeClass(this.options.hoverClass);this._trigger("drop",
+a,this.ui(c));return this.element}return false},ui:function(a){return{draggable:a.currentItem||a.element,helper:a.helper,position:a.position,offset:a.positionAbs}}});d.extend(d.ui.droppable,{version:"1.8.14"});d.ui.intersect=function(a,b,c){if(!b.offset)return false;var e=(a.positionAbs||a.position.absolute).left,g=e+a.helperProportions.width,f=(a.positionAbs||a.position.absolute).top,h=f+a.helperProportions.height,i=b.offset.left,k=i+b.proportions.width,j=b.offset.top,l=j+b.proportions.height;
+switch(c){case "fit":return i<=e&&g<=k&&j<=f&&h<=l;case "intersect":return i<e+a.helperProportions.width/2&&g-a.helperProportions.width/2<k&&j<f+a.helperProportions.height/2&&h-a.helperProportions.height/2<l;case "pointer":return d.ui.isOver((a.positionAbs||a.position.absolute).top+(a.clickOffset||a.offset.click).top,(a.positionAbs||a.position.absolute).left+(a.clickOffset||a.offset.click).left,j,i,b.proportions.height,b.proportions.width);case "touch":return(f>=j&&f<=l||h>=j&&h<=l||f<j&&h>l)&&(e>=
+i&&e<=k||g>=i&&g<=k||e<i&&g>k);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f<c.length;f++)if(!(c[f].options.disabled||a&&!c[f].accept.call(c[f].element[0],a.currentItem||a.element))){for(var h=0;h<g.length;h++)if(g[h]==c[f].element[0]){c[f].proportions.height=0;continue a}c[f].visible=c[f].element.css("display")!=
+"none";if(c[f].visible){e=="mousedown"&&c[f]._activate.call(c[f],b);c[f].offset=c[f].element.offset();c[f].proportions={width:c[f].element[0].offsetWidth,height:c[f].element[0].offsetHeight}}}},drop:function(a,b){var c=false;d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(this.options){if(!this.options.disabled&&this.visible&&d.ui.intersect(a,this,this.options.tolerance))c=c||this._drop.call(this,b);if(!this.options.disabled&&this.visible&&this.accept.call(this.element[0],a.currentItem||
+a.element)){this.isout=1;this.isover=0;this._deactivate.call(this,b)}}});return c},dragStart:function(a,b){a.element.parentsUntil("body").bind("scroll.droppable",function(){a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)})},drag:function(a,b){a.options.refreshPositions&&d.ui.ddmanager.prepareOffsets(a,b);d.each(d.ui.ddmanager.droppables[a.options.scope]||[],function(){if(!(this.options.disabled||this.greedyChild||!this.visible)){var c=d.ui.intersect(a,this,this.options.tolerance);if(c=
+!c&&this.isover==1?"isout":c&&this.isover==0?"isover":null){var e;if(this.options.greedy){var g=this.element.parents(":data(droppable):eq(0)");if(g.length){e=d.data(g[0],"droppable");e.greedyChild=c=="isover"?1:0}}if(e&&c=="isover"){e.isover=0;e.isout=1;e._out.call(e,b)}this[c]=1;this[c=="isout"?"isover":"isout"]=0;this[c=="isover"?"_over":"_out"].call(this,b);if(e&&c=="isout"){e.isout=0;e.isover=1;e._over.call(e,b)}}}})},dragStop:function(a,b){a.element.parentsUntil("body").unbind("scroll.droppable");
+a.options.refreshPositions||d.ui.ddmanager.prepareOffsets(a,b)}}})(jQuery);
+;/*
+ * jQuery UI Resizable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Resizables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.resizable",e.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1E3},_create:function(){var b=this,a=this.options;this.element.addClass("ui-resizable");e.extend(this,{_aspectRatio:!!a.aspectRatio,aspectRatio:a.aspectRatio,originalElement:this.element,
+_proportionallyResizeElements:[],_helper:a.helper||a.ghost||a.animate?a.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){/relative/.test(this.element.css("position"))&&e.browser.opera&&this.element.css({position:"relative",top:"auto",left:"auto"});this.element.wrap(e('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),
+top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=
+this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=a.handles||(!e(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",
+nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all")this.handles="n,e,s,w,se,sw,ne,nw";var c=this.handles.split(",");this.handles={};for(var d=0;d<c.length;d++){var f=e.trim(c[d]),g=e('<div class="ui-resizable-handle '+("ui-resizable-"+f)+'"></div>');/sw|se|ne|nw/.test(f)&&g.css({zIndex:++a.zIndex});"se"==f&&g.addClass("ui-icon ui-icon-gripsmall-diagonal-se");this.handles[f]=".ui-resizable-"+f;this.element.append(g)}}this._renderAxis=function(h){h=h||this.element;for(var i in this.handles){if(this.handles[i].constructor==
+String)this.handles[i]=e(this.handles[i],this.element).show();if(this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)){var j=e(this.handles[i],this.element),l=0;l=/sw|ne|nw|se|n|s/.test(i)?j.outerHeight():j.outerWidth();j=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join("");h.css(j,l);this._proportionallyResize()}e(this.handles[i])}};this._renderAxis(this.element);this._handles=e(".ui-resizable-handle",this.element).disableSelection();
+this._handles.mouseover(function(){if(!b.resizing){if(this.className)var h=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);b.axis=h&&h[1]?h[1]:"se"}});if(a.autoHide){this._handles.hide();e(this.element).addClass("ui-resizable-autohide").hover(function(){if(!a.disabled){e(this).removeClass("ui-resizable-autohide");b._handles.show()}},function(){if(!a.disabled)if(!b.resizing){e(this).addClass("ui-resizable-autohide");b._handles.hide()}})}this._mouseInit()},destroy:function(){this._mouseDestroy();
+var b=function(c){e(c).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};if(this.elementIsWrapper){b(this.element);var a=this.element;a.after(this.originalElement.css({position:a.css("position"),width:a.outerWidth(),height:a.outerHeight(),top:a.css("top"),left:a.css("left")})).remove()}this.originalElement.css("resize",this.originalResizeStyle);b(this.originalElement);return this},_mouseCapture:function(b){var a=
+false;for(var c in this.handles)if(e(this.handles[c])[0]==b.target)a=true;return!this.options.disabled&&a},_mouseStart:function(b){var a=this.options,c=this.element.position(),d=this.element;this.resizing=true;this.documentScroll={top:e(document).scrollTop(),left:e(document).scrollLeft()};if(d.is(".ui-draggable")||/absolute/.test(d.css("position")))d.css({position:"absolute",top:c.top,left:c.left});e.browser.opera&&/relative/.test(d.css("position"))&&d.css({position:"relative",top:"auto",left:"auto"});
+this._renderProxy();c=m(this.helper.css("left"));var f=m(this.helper.css("top"));if(a.containment){c+=e(a.containment).scrollLeft()||0;f+=e(a.containment).scrollTop()||0}this.offset=this.helper.offset();this.position={left:c,top:f};this.size=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalSize=this._helper?{width:d.outerWidth(),height:d.outerHeight()}:{width:d.width(),height:d.height()};this.originalPosition={left:c,top:f};this.sizeDiff=
+{width:d.outerWidth()-d.width(),height:d.outerHeight()-d.height()};this.originalMousePosition={left:b.pageX,top:b.pageY};this.aspectRatio=typeof a.aspectRatio=="number"?a.aspectRatio:this.originalSize.width/this.originalSize.height||1;a=e(".ui-resizable-"+this.axis).css("cursor");e("body").css("cursor",a=="auto"?this.axis+"-resize":a);d.addClass("ui-resizable-resizing");this._propagate("start",b);return true},_mouseDrag:function(b){var a=this.helper,c=this.originalMousePosition,d=this._change[this.axis];
+if(!d)return false;c=d.apply(this,[b,b.pageX-c.left||0,b.pageY-c.top||0]);this._updateVirtualBoundaries(b.shiftKey);if(this._aspectRatio||b.shiftKey)c=this._updateRatio(c,b);c=this._respectSize(c,b);this._propagate("resize",b);a.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize();this._updateCache(c);this._trigger("resize",b,this.ui());return false},
+_mouseStop:function(b){this.resizing=false;var a=this.options,c=this;if(this._helper){var d=this._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName);d=f&&e.ui.hasScroll(d[0],"left")?0:c.sizeDiff.height;f=f?0:c.sizeDiff.width;f={width:c.helper.width()-f,height:c.helper.height()-d};d=parseInt(c.element.css("left"),10)+(c.position.left-c.originalPosition.left)||null;var g=parseInt(c.element.css("top"),10)+(c.position.top-c.originalPosition.top)||null;a.animate||this.element.css(e.extend(f,
+{top:g,left:d}));c.helper.height(c.size.height);c.helper.width(c.size.width);this._helper&&!a.animate&&this._proportionallyResize()}e("body").css("cursor","auto");this.element.removeClass("ui-resizable-resizing");this._propagate("stop",b);this._helper&&this.helper.remove();return false},_updateVirtualBoundaries:function(b){var a=this.options,c,d,f;a={minWidth:k(a.minWidth)?a.minWidth:0,maxWidth:k(a.maxWidth)?a.maxWidth:Infinity,minHeight:k(a.minHeight)?a.minHeight:0,maxHeight:k(a.maxHeight)?a.maxHeight:
+Infinity};if(this._aspectRatio||b){b=a.minHeight*this.aspectRatio;d=a.minWidth/this.aspectRatio;c=a.maxHeight*this.aspectRatio;f=a.maxWidth/this.aspectRatio;if(b>a.minWidth)a.minWidth=b;if(d>a.minHeight)a.minHeight=d;if(c<a.maxWidth)a.maxWidth=c;if(f<a.maxHeight)a.maxHeight=f}this._vBoundaries=a},_updateCache:function(b){this.offset=this.helper.offset();if(k(b.left))this.position.left=b.left;if(k(b.top))this.position.top=b.top;if(k(b.height))this.size.height=b.height;if(k(b.width))this.size.width=
+b.width},_updateRatio:function(b){var a=this.position,c=this.size,d=this.axis;if(k(b.height))b.width=b.height*this.aspectRatio;else if(k(b.width))b.height=b.width/this.aspectRatio;if(d=="sw"){b.left=a.left+(c.width-b.width);b.top=null}if(d=="nw"){b.top=a.top+(c.height-b.height);b.left=a.left+(c.width-b.width)}return b},_respectSize:function(b){var a=this._vBoundaries,c=this.axis,d=k(b.width)&&a.maxWidth&&a.maxWidth<b.width,f=k(b.height)&&a.maxHeight&&a.maxHeight<b.height,g=k(b.width)&&a.minWidth&&
+a.minWidth>b.width,h=k(b.height)&&a.minHeight&&a.minHeight>b.height;if(g)b.width=a.minWidth;if(h)b.height=a.minHeight;if(d)b.width=a.maxWidth;if(f)b.height=a.maxHeight;var i=this.originalPosition.left+this.originalSize.width,j=this.position.top+this.size.height,l=/sw|nw|w/.test(c);c=/nw|ne|n/.test(c);if(g&&l)b.left=i-a.minWidth;if(d&&l)b.left=i-a.maxWidth;if(h&&c)b.top=j-a.minHeight;if(f&&c)b.top=j-a.maxHeight;if((a=!b.width&&!b.height)&&!b.left&&b.top)b.top=null;else if(a&&!b.top&&b.left)b.left=
+null;return b},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var b=this.helper||this.element,a=0;a<this._proportionallyResizeElements.length;a++){var c=this._proportionallyResizeElements[a];if(!this.borderDif){var d=[c.css("borderTopWidth"),c.css("borderRightWidth"),c.css("borderBottomWidth"),c.css("borderLeftWidth")],f=[c.css("paddingTop"),c.css("paddingRight"),c.css("paddingBottom"),c.css("paddingLeft")];this.borderDif=e.map(d,function(g,h){g=parseInt(g,10)||
+0;h=parseInt(f[h],10)||0;return g+h})}e.browser.msie&&(e(b).is(":hidden")||e(b).parents(":hidden").length)||c.css({height:b.height()-this.borderDif[0]-this.borderDif[2]||0,width:b.width()-this.borderDif[1]-this.borderDif[3]||0})}},_renderProxy:function(){var b=this.options;this.elementOffset=this.element.offset();if(this._helper){this.helper=this.helper||e('<div style="overflow:hidden;"></div>');var a=e.browser.msie&&e.browser.version<7,c=a?1:0;a=a?2:-1;this.helper.addClass(this._helper).css({width:this.element.outerWidth()+
+a,height:this.element.outerHeight()+a,position:"absolute",left:this.elementOffset.left-c+"px",top:this.elementOffset.top-c+"px",zIndex:++b.zIndex});this.helper.appendTo("body").disableSelection()}else this.helper=this.element},_change:{e:function(b,a){return{width:this.originalSize.width+a}},w:function(b,a){return{left:this.originalPosition.left+a,width:this.originalSize.width-a}},n:function(b,a,c){return{top:this.originalPosition.top+c,height:this.originalSize.height-c}},s:function(b,a,c){return{height:this.originalSize.height+
+c}},se:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},sw:function(b,a,c){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[b,a,c]))},ne:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[b,a,c]))},nw:function(b,a,c){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[b,a,c]))}},_propagate:function(b,a){e.ui.plugin.call(this,b,[a,this.ui()]);
+b!="resize"&&this._trigger(b,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}});e.extend(e.ui.resizable,{version:"1.8.14"});e.ui.plugin.add("resizable","alsoResize",{start:function(){var b=e(this).data("resizable").options,a=function(c){e(c).each(function(){var d=e(this);d.data("resizable-alsoresize",{width:parseInt(d.width(),
+10),height:parseInt(d.height(),10),left:parseInt(d.css("left"),10),top:parseInt(d.css("top"),10),position:d.css("position")})})};if(typeof b.alsoResize=="object"&&!b.alsoResize.parentNode)if(b.alsoResize.length){b.alsoResize=b.alsoResize[0];a(b.alsoResize)}else e.each(b.alsoResize,function(c){a(c)});else a(b.alsoResize)},resize:function(b,a){var c=e(this).data("resizable");b=c.options;var d=c.originalSize,f=c.originalPosition,g={height:c.size.height-d.height||0,width:c.size.width-d.width||0,top:c.position.top-
+f.top||0,left:c.position.left-f.left||0},h=function(i,j){e(i).each(function(){var l=e(this),q=e(this).data("resizable-alsoresize"),p={},r=j&&j.length?j:l.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(r,function(n,o){if((n=(q[o]||0)+(g[o]||0))&&n>=0)p[o]=n||null});if(e.browser.opera&&/relative/.test(l.css("position"))){c._revertToRelativePosition=true;l.css({position:"absolute",top:"auto",left:"auto"})}l.css(p)})};typeof b.alsoResize=="object"&&!b.alsoResize.nodeType?
+e.each(b.alsoResize,function(i,j){h(i,j)}):h(b.alsoResize)},stop:function(){var b=e(this).data("resizable"),a=b.options,c=function(d){e(d).each(function(){var f=e(this);f.css({position:f.data("resizable-alsoresize").position})})};if(b._revertToRelativePosition){b._revertToRelativePosition=false;typeof a.alsoResize=="object"&&!a.alsoResize.nodeType?e.each(a.alsoResize,function(d){c(d)}):c(a.alsoResize)}e(this).removeData("resizable-alsoresize")}});e.ui.plugin.add("resizable","animate",{stop:function(b){var a=
+e(this).data("resizable"),c=a.options,d=a._proportionallyResizeElements,f=d.length&&/textarea/i.test(d[0].nodeName),g=f&&e.ui.hasScroll(d[0],"left")?0:a.sizeDiff.height;f={width:a.size.width-(f?0:a.sizeDiff.width),height:a.size.height-g};g=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null;var h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(f,h&&g?{top:h,left:g}:{}),{duration:c.animateDuration,easing:c.animateEasing,
+step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};d&&d.length&&e(d[0]).css({width:i.width,height:i.height});a._updateCache(i);a._propagate("resize",b)}})}});e.ui.plugin.add("resizable","containment",{start:function(){var b=e(this).data("resizable"),a=b.element,c=b.options.containment;if(a=c instanceof e?c.get(0):/parent/.test(c)?a.parent().get(0):c){b.containerElement=
+e(a);if(/document/.test(c)||c==document){b.containerOffset={left:0,top:0};b.containerPosition={left:0,top:0};b.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}}else{var d=e(a),f=[];e(["Top","Right","Left","Bottom"]).each(function(i,j){f[i]=m(d.css("padding"+j))});b.containerOffset=d.offset();b.containerPosition=d.position();b.containerSize={height:d.innerHeight()-f[3],width:d.innerWidth()-f[1]};c=b.containerOffset;
+var g=b.containerSize.height,h=b.containerSize.width;h=e.ui.hasScroll(a,"left")?a.scrollWidth:h;g=e.ui.hasScroll(a)?a.scrollHeight:g;b.parentData={element:a,left:c.left,top:c.top,width:h,height:g}}}},resize:function(b){var a=e(this).data("resizable"),c=a.options,d=a.containerOffset,f=a.position;b=a._aspectRatio||b.shiftKey;var g={top:0,left:0},h=a.containerElement;if(h[0]!=document&&/static/.test(h.css("position")))g=d;if(f.left<(a._helper?d.left:0)){a.size.width+=a._helper?a.position.left-d.left:
+a.position.left-g.left;if(b)a.size.height=a.size.width/c.aspectRatio;a.position.left=c.helper?d.left:0}if(f.top<(a._helper?d.top:0)){a.size.height+=a._helper?a.position.top-d.top:a.position.top;if(b)a.size.width=a.size.height*c.aspectRatio;a.position.top=a._helper?d.top:0}a.offset.left=a.parentData.left+a.position.left;a.offset.top=a.parentData.top+a.position.top;c=Math.abs((a._helper?a.offset.left-g.left:a.offset.left-g.left)+a.sizeDiff.width);d=Math.abs((a._helper?a.offset.top-g.top:a.offset.top-
+d.top)+a.sizeDiff.height);f=a.containerElement.get(0)==a.element.parent().get(0);g=/relative|absolute/.test(a.containerElement.css("position"));if(f&&g)c-=a.parentData.left;if(c+a.size.width>=a.parentData.width){a.size.width=a.parentData.width-c;if(b)a.size.height=a.size.width/a.aspectRatio}if(d+a.size.height>=a.parentData.height){a.size.height=a.parentData.height-d;if(b)a.size.width=a.size.height*a.aspectRatio}},stop:function(){var b=e(this).data("resizable"),a=b.options,c=b.containerOffset,d=b.containerPosition,
+f=b.containerElement,g=e(b.helper),h=g.offset(),i=g.outerWidth()-b.sizeDiff.width;g=g.outerHeight()-b.sizeDiff.height;b._helper&&!a.animate&&/relative/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g});b._helper&&!a.animate&&/static/.test(f.css("position"))&&e(this).css({left:h.left-d.left-c.left,width:i,height:g})}});e.ui.plugin.add("resizable","ghost",{start:function(){var b=e(this).data("resizable"),a=b.options,c=b.size;b.ghost=b.originalElement.clone();b.ghost.css({opacity:0.25,
+display:"block",position:"relative",height:c.height,width:c.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass(typeof a.ghost=="string"?a.ghost:"");b.ghost.appendTo(b.helper)},resize:function(){var b=e(this).data("resizable");b.ghost&&b.ghost.css({position:"relative",height:b.size.height,width:b.size.width})},stop:function(){var b=e(this).data("resizable");b.ghost&&b.helper&&b.helper.get(0).removeChild(b.ghost.get(0))}});e.ui.plugin.add("resizable","grid",{resize:function(){var b=
+e(this).data("resizable"),a=b.options,c=b.size,d=b.originalSize,f=b.originalPosition,g=b.axis;a.grid=typeof a.grid=="number"?[a.grid,a.grid]:a.grid;var h=Math.round((c.width-d.width)/(a.grid[0]||1))*(a.grid[0]||1);a=Math.round((c.height-d.height)/(a.grid[1]||1))*(a.grid[1]||1);if(/^(se|s|e)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a}else if(/^(ne)$/.test(g)){b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}else{if(/^(sw)$/.test(g)){b.size.width=d.width+h;b.size.height=
+d.height+a}else{b.size.width=d.width+h;b.size.height=d.height+a;b.position.top=f.top-a}b.position.left=f.left-h}}});var m=function(b){return parseInt(b,10)||0},k=function(b){return!isNaN(parseInt(b,10))}})(jQuery);
+;/*
+ * jQuery UI Selectable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Selectables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(e){e.widget("ui.selectable",e.ui.mouse,{options:{appendTo:"body",autoRefresh:true,distance:0,filter:"*",tolerance:"touch"},_create:function(){var c=this;this.element.addClass("ui-selectable");this.dragged=false;var f;this.refresh=function(){f=e(c.options.filter,c.element[0]);f.each(function(){var d=e(this),b=d.offset();e.data(this,"selectable-item",{element:this,$element:d,left:b.left,top:b.top,right:b.left+d.outerWidth(),bottom:b.top+d.outerHeight(),startselected:false,selected:d.hasClass("ui-selected"),
+selecting:d.hasClass("ui-selecting"),unselecting:d.hasClass("ui-unselecting")})})};this.refresh();this.selectees=f.addClass("ui-selectee");this._mouseInit();this.helper=e("<div class='ui-selectable-helper'></div>")},destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item");this.element.removeClass("ui-selectable ui-selectable-disabled").removeData("selectable").unbind(".selectable");this._mouseDestroy();return this},_mouseStart:function(c){var f=this;this.opos=[c.pageX,
+c.pageY];if(!this.options.disabled){var d=this.options;this.selectees=e(d.filter,this.element[0]);this._trigger("start",c);e(d.appendTo).append(this.helper);this.helper.css({left:c.clientX,top:c.clientY,width:0,height:0});d.autoRefresh&&this.refresh();this.selectees.filter(".ui-selected").each(function(){var b=e.data(this,"selectable-item");b.startselected=true;if(!c.metaKey){b.$element.removeClass("ui-selected");b.selected=false;b.$element.addClass("ui-unselecting");b.unselecting=true;f._trigger("unselecting",
+c,{unselecting:b.element})}});e(c.target).parents().andSelf().each(function(){var b=e.data(this,"selectable-item");if(b){var g=!c.metaKey||!b.$element.hasClass("ui-selected");b.$element.removeClass(g?"ui-unselecting":"ui-selected").addClass(g?"ui-selecting":"ui-unselecting");b.unselecting=!g;b.selecting=g;(b.selected=g)?f._trigger("selecting",c,{selecting:b.element}):f._trigger("unselecting",c,{unselecting:b.element});return false}})}},_mouseDrag:function(c){var f=this;this.dragged=true;if(!this.options.disabled){var d=
+this.options,b=this.opos[0],g=this.opos[1],h=c.pageX,i=c.pageY;if(b>h){var j=h;h=b;b=j}if(g>i){j=i;i=g;g=j}this.helper.css({left:b,top:g,width:h-b,height:i-g});this.selectees.each(function(){var a=e.data(this,"selectable-item");if(!(!a||a.element==f.element[0])){var k=false;if(d.tolerance=="touch")k=!(a.left>h||a.right<b||a.top>i||a.bottom<g);else if(d.tolerance=="fit")k=a.left>b&&a.right<h&&a.top>g&&a.bottom<i;if(k){if(a.selected){a.$element.removeClass("ui-selected");a.selected=false}if(a.unselecting){a.$element.removeClass("ui-unselecting");
+a.unselecting=false}if(!a.selecting){a.$element.addClass("ui-selecting");a.selecting=true;f._trigger("selecting",c,{selecting:a.element})}}else{if(a.selecting)if(c.metaKey&&a.startselected){a.$element.removeClass("ui-selecting");a.selecting=false;a.$element.addClass("ui-selected");a.selected=true}else{a.$element.removeClass("ui-selecting");a.selecting=false;if(a.startselected){a.$element.addClass("ui-unselecting");a.unselecting=true}f._trigger("unselecting",c,{unselecting:a.element})}if(a.selected)if(!c.metaKey&&
+!a.startselected){a.$element.removeClass("ui-selected");a.selected=false;a.$element.addClass("ui-unselecting");a.unselecting=true;f._trigger("unselecting",c,{unselecting:a.element})}}}});return false}},_mouseStop:function(c){var f=this;this.dragged=false;e(".ui-unselecting",this.element[0]).each(function(){var d=e.data(this,"selectable-item");d.$element.removeClass("ui-unselecting");d.unselecting=false;d.startselected=false;f._trigger("unselected",c,{unselected:d.element})});e(".ui-selecting",this.element[0]).each(function(){var d=
+e.data(this,"selectable-item");d.$element.removeClass("ui-selecting").addClass("ui-selected");d.selecting=false;d.selected=true;d.startselected=true;f._trigger("selected",c,{selected:d.element})});this._trigger("stop",c);this.helper.remove();return false}});e.extend(e.ui.selectable,{version:"1.8.14"})})(jQuery);
+;/*
+ * jQuery UI Sortable 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Sortables
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.sortable",d.ui.mouse,{widgetEventPrefix:"sort",options:{appendTo:"parent",axis:false,connectWith:false,containment:false,cursor:"auto",cursorAt:false,dropOnEmpty:true,forcePlaceholderSize:false,forceHelperSize:false,grid:false,handle:false,helper:"original",items:"> *",opacity:false,placeholder:false,revert:false,scroll:true,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1E3},_create:function(){var a=this.options;this.containerCache={};this.element.addClass("ui-sortable");
+this.refresh();this.floating=this.items.length?a.axis==="x"||/left|right/.test(this.items[0].item.css("float"))||/inline|table-cell/.test(this.items[0].item.css("display")):false;this.offset=this.element.offset();this._mouseInit()},destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").removeData("sortable").unbind(".sortable");this._mouseDestroy();for(var a=this.items.length-1;a>=0;a--)this.items[a].item.removeData("sortable-item");return this},_setOption:function(a,b){if(a===
+"disabled"){this.options[a]=b;this.widget()[b?"addClass":"removeClass"]("ui-sortable-disabled")}else d.Widget.prototype._setOption.apply(this,arguments)},_mouseCapture:function(a,b){if(this.reverting)return false;if(this.options.disabled||this.options.type=="static")return false;this._refreshItems(a);var c=null,e=this;d(a.target).parents().each(function(){if(d.data(this,"sortable-item")==e){c=d(this);return false}});if(d.data(a.target,"sortable-item")==e)c=d(a.target);if(!c)return false;if(this.options.handle&&
+!b){var f=false;d(this.options.handle,c).find("*").andSelf().each(function(){if(this==a.target)f=true});if(!f)return false}this.currentItem=c;this._removeCurrentsFromItems();return true},_mouseStart:function(a,b,c){b=this.options;var e=this;this.currentContainer=this;this.refreshPositions();this.helper=this._createHelper(a);this._cacheHelperProportions();this._cacheMargins();this.scrollParent=this.helper.scrollParent();this.offset=this.currentItem.offset();this.offset={top:this.offset.top-this.margins.top,
+left:this.offset.left-this.margins.left};this.helper.css("position","absolute");this.cssPosition=this.helper.css("position");d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()});this.originalPosition=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]};
+this.helper[0]!=this.currentItem[0]&&this.currentItem.hide();this._createPlaceholder();b.containment&&this._setContainment();if(b.cursor){if(d("body").css("cursor"))this._storedCursor=d("body").css("cursor");d("body").css("cursor",b.cursor)}if(b.opacity){if(this.helper.css("opacity"))this._storedOpacity=this.helper.css("opacity");this.helper.css("opacity",b.opacity)}if(b.zIndex){if(this.helper.css("zIndex"))this._storedZIndex=this.helper.css("zIndex");this.helper.css("zIndex",b.zIndex)}if(this.scrollParent[0]!=
+document&&this.scrollParent[0].tagName!="HTML")this.overflowOffset=this.scrollParent.offset();this._trigger("start",a,this._uiHash());this._preserveHelperProportions||this._cacheHelperProportions();if(!c)for(c=this.containers.length-1;c>=0;c--)this.containers[c]._trigger("activate",a,e._uiHash(this));if(d.ui.ddmanager)d.ui.ddmanager.current=this;d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.dragging=true;this.helper.addClass("ui-sortable-helper");this._mouseDrag(a);
+return true},_mouseDrag:function(a){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!this.lastPositionAbs)this.lastPositionAbs=this.positionAbs;if(this.options.scroll){var b=this.options,c=false;if(this.scrollParent[0]!=document&&this.scrollParent[0].tagName!="HTML"){if(this.overflowOffset.top+this.scrollParent[0].offsetHeight-a.pageY<b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop+b.scrollSpeed;else if(a.pageY-this.overflowOffset.top<
+b.scrollSensitivity)this.scrollParent[0].scrollTop=c=this.scrollParent[0].scrollTop-b.scrollSpeed;if(this.overflowOffset.left+this.scrollParent[0].offsetWidth-a.pageX<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft+b.scrollSpeed;else if(a.pageX-this.overflowOffset.left<b.scrollSensitivity)this.scrollParent[0].scrollLeft=c=this.scrollParent[0].scrollLeft-b.scrollSpeed}else{if(a.pageY-d(document).scrollTop()<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()-
+b.scrollSpeed);else if(d(window).height()-(a.pageY-d(document).scrollTop())<b.scrollSensitivity)c=d(document).scrollTop(d(document).scrollTop()+b.scrollSpeed);if(a.pageX-d(document).scrollLeft()<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()-b.scrollSpeed);else if(d(window).width()-(a.pageX-d(document).scrollLeft())<b.scrollSensitivity)c=d(document).scrollLeft(d(document).scrollLeft()+b.scrollSpeed)}c!==false&&d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,
+a)}this.positionAbs=this._convertPositionTo("absolute");if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";for(b=this.items.length-1;b>=0;b--){c=this.items[b];var e=c.item[0],f=this._intersectsWithPointer(c);if(f)if(e!=this.currentItem[0]&&this.placeholder[f==1?"next":"prev"]()[0]!=e&&!d.ui.contains(this.placeholder[0],e)&&(this.options.type=="semi-dynamic"?!d.ui.contains(this.element[0],
+e):true)){this.direction=f==1?"down":"up";if(this.options.tolerance=="pointer"||this._intersectsWithSides(c))this._rearrange(a,c);else break;this._trigger("change",a,this._uiHash());break}}this._contactContainers(a);d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);this._trigger("sort",a,this._uiHash());this.lastPositionAbs=this.positionAbs;return false},_mouseStop:function(a,b){if(a){d.ui.ddmanager&&!this.options.dropBehaviour&&d.ui.ddmanager.drop(this,a);if(this.options.revert){var c=this;b=c.placeholder.offset();
+c.reverting=true;d(this.helper).animate({left:b.left-this.offset.parent.left-c.margins.left+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollLeft),top:b.top-this.offset.parent.top-c.margins.top+(this.offsetParent[0]==document.body?0:this.offsetParent[0].scrollTop)},parseInt(this.options.revert,10)||500,function(){c._clear(a)})}else this._clear(a,b);return false}},cancel:function(){var a=this;if(this.dragging){this._mouseUp({target:null});this.options.helper=="original"?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):
+this.currentItem.show();for(var b=this.containers.length-1;b>=0;b--){this.containers[b]._trigger("deactivate",null,a._uiHash(this));if(this.containers[b].containerCache.over){this.containers[b]._trigger("out",null,a._uiHash(this));this.containers[b].containerCache.over=0}}}if(this.placeholder){this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.options.helper!="original"&&this.helper&&this.helper[0].parentNode&&this.helper.remove();d.extend(this,{helper:null,
+dragging:false,reverting:false,_noFinalSort:null});this.domPosition.prev?d(this.domPosition.prev).after(this.currentItem):d(this.domPosition.parent).prepend(this.currentItem)}return this},serialize:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};d(b).each(function(){var e=(d(a.item||this).attr(a.attribute||"id")||"").match(a.expression||/(.+)[-=_](.+)/);if(e)c.push((a.key||e[1]+"[]")+"="+(a.key&&a.expression?e[1]:e[2]))});!c.length&&a.key&&c.push(a.key+"=");return c.join("&")},
+toArray:function(a){var b=this._getItemsAsjQuery(a&&a.connected),c=[];a=a||{};b.each(function(){c.push(d(a.item||this).attr(a.attribute||"id")||"")});return c},_intersectsWith:function(a){var b=this.positionAbs.left,c=b+this.helperProportions.width,e=this.positionAbs.top,f=e+this.helperProportions.height,g=a.left,h=g+a.width,i=a.top,k=i+a.height,j=this.offset.click.top,l=this.offset.click.left;j=e+j>i&&e+j<k&&b+l>g&&b+l<h;return this.options.tolerance=="pointer"||this.options.forcePointerForContainers||
+this.options.tolerance!="pointer"&&this.helperProportions[this.floating?"width":"height"]>a[this.floating?"width":"height"]?j:g<b+this.helperProportions.width/2&&c-this.helperProportions.width/2<h&&i<e+this.helperProportions.height/2&&f-this.helperProportions.height/2<k},_intersectsWithPointer:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left,a.width);b=b&&a;a=this._getDragVerticalDirection();
+var c=this._getDragHorizontalDirection();if(!b)return false;return this.floating?c&&c=="right"||a=="down"?2:1:a&&(a=="down"?2:1)},_intersectsWithSides:function(a){var b=d.ui.isOverAxis(this.positionAbs.top+this.offset.click.top,a.top+a.height/2,a.height);a=d.ui.isOverAxis(this.positionAbs.left+this.offset.click.left,a.left+a.width/2,a.width);var c=this._getDragVerticalDirection(),e=this._getDragHorizontalDirection();return this.floating&&e?e=="right"&&a||e=="left"&&!a:c&&(c=="down"&&b||c=="up"&&!b)},
+_getDragVerticalDirection:function(){var a=this.positionAbs.top-this.lastPositionAbs.top;return a!=0&&(a>0?"down":"up")},_getDragHorizontalDirection:function(){var a=this.positionAbs.left-this.lastPositionAbs.left;return a!=0&&(a>0?"right":"left")},refresh:function(a){this._refreshItems(a);this.refreshPositions();return this},_connectWith:function(){var a=this.options;return a.connectWith.constructor==String?[a.connectWith]:a.connectWith},_getItemsAsjQuery:function(a){var b=[],c=[],e=this._connectWith();
+if(e&&a)for(a=e.length-1;a>=0;a--)for(var f=d(e[a]),g=f.length-1;g>=0;g--){var h=d.data(f[g],"sortable");if(h&&h!=this&&!h.options.disabled)c.push([d.isFunction(h.options.items)?h.options.items.call(h.element):d(h.options.items,h.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),h])}c.push([d.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):d(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),
+this]);for(a=c.length-1;a>=0;a--)c[a][0].each(function(){b.push(this)});return d(b)},_removeCurrentsFromItems:function(){for(var a=this.currentItem.find(":data(sortable-item)"),b=0;b<this.items.length;b++)for(var c=0;c<a.length;c++)a[c]==this.items[b].item[0]&&this.items.splice(b,1)},_refreshItems:function(a){this.items=[];this.containers=[this];var b=this.items,c=[[d.isFunction(this.options.items)?this.options.items.call(this.element[0],a,{item:this.currentItem}):d(this.options.items,this.element),
+this]],e=this._connectWith();if(e)for(var f=e.length-1;f>=0;f--)for(var g=d(e[f]),h=g.length-1;h>=0;h--){var i=d.data(g[h],"sortable");if(i&&i!=this&&!i.options.disabled){c.push([d.isFunction(i.options.items)?i.options.items.call(i.element[0],a,{item:this.currentItem}):d(i.options.items,i.element),i]);this.containers.push(i)}}for(f=c.length-1;f>=0;f--){a=c[f][1];e=c[f][0];h=0;for(g=e.length;h<g;h++){i=d(e[h]);i.data("sortable-item",a);b.push({item:i,instance:a,width:0,height:0,left:0,top:0})}}},refreshPositions:function(a){if(this.offsetParent&&
+this.helper)this.offset.parent=this._getParentOffset();for(var b=this.items.length-1;b>=0;b--){var c=this.items[b];if(!(c.instance!=this.currentContainer&&this.currentContainer&&c.item[0]!=this.currentItem[0])){var e=this.options.toleranceElement?d(this.options.toleranceElement,c.item):c.item;if(!a){c.width=e.outerWidth();c.height=e.outerHeight()}e=e.offset();c.left=e.left;c.top=e.top}}if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(b=
+this.containers.length-1;b>=0;b--){e=this.containers[b].element.offset();this.containers[b].containerCache.left=e.left;this.containers[b].containerCache.top=e.top;this.containers[b].containerCache.width=this.containers[b].element.outerWidth();this.containers[b].containerCache.height=this.containers[b].element.outerHeight()}return this},_createPlaceholder:function(a){var b=a||this,c=b.options;if(!c.placeholder||c.placeholder.constructor==String){var e=c.placeholder;c.placeholder={element:function(){var f=
+d(document.createElement(b.currentItem[0].nodeName)).addClass(e||b.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper")[0];if(!e)f.style.visibility="hidden";return f},update:function(f,g){if(!(e&&!c.forcePlaceholderSize)){g.height()||g.height(b.currentItem.innerHeight()-parseInt(b.currentItem.css("paddingTop")||0,10)-parseInt(b.currentItem.css("paddingBottom")||0,10));g.width()||g.width(b.currentItem.innerWidth()-parseInt(b.currentItem.css("paddingLeft")||0,10)-parseInt(b.currentItem.css("paddingRight")||
+0,10))}}}}b.placeholder=d(c.placeholder.element.call(b.element,b.currentItem));b.currentItem.after(b.placeholder);c.placeholder.update(b,b.placeholder)},_contactContainers:function(a){for(var b=null,c=null,e=this.containers.length-1;e>=0;e--)if(!d.ui.contains(this.currentItem[0],this.containers[e].element[0]))if(this._intersectsWith(this.containers[e].containerCache)){if(!(b&&d.ui.contains(this.containers[e].element[0],b.element[0]))){b=this.containers[e];c=e}}else if(this.containers[e].containerCache.over){this.containers[e]._trigger("out",
+a,this._uiHash(this));this.containers[e].containerCache.over=0}if(b)if(this.containers.length===1){this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}else if(this.currentContainer!=this.containers[c]){b=1E4;e=null;for(var f=this.positionAbs[this.containers[c].floating?"left":"top"],g=this.items.length-1;g>=0;g--)if(d.ui.contains(this.containers[c].element[0],this.items[g].item[0])){var h=this.items[g][this.containers[c].floating?"left":"top"];if(Math.abs(h-
+f)<b){b=Math.abs(h-f);e=this.items[g]}}if(e||this.options.dropOnEmpty){this.currentContainer=this.containers[c];e?this._rearrange(a,e,null,true):this._rearrange(a,null,this.containers[c].element,true);this._trigger("change",a,this._uiHash());this.containers[c]._trigger("change",a,this._uiHash(this));this.options.placeholder.update(this.currentContainer,this.placeholder);this.containers[c]._trigger("over",a,this._uiHash(this));this.containers[c].containerCache.over=1}}},_createHelper:function(a){var b=
+this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a,this.currentItem])):b.helper=="clone"?this.currentItem.clone():this.currentItem;a.parents("body").length||d(b.appendTo!="parent"?b.appendTo:this.currentItem[0].parentNode)[0].appendChild(a[0]);if(a[0]==this.currentItem[0])this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")};if(a[0].style.width==
+""||b.forceHelperSize)a.width(this.currentItem.width());if(a[0].style.height==""||b.forceHelperSize)a.height(this.currentItem.height());return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=
+this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a=
+{top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.currentItem.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),
+10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,d(a.containment=="document"?
+document:window).width()-this.helperProportions.width-this.margins.left,(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)){var b=d(a.containment)[0];a=d(a.containment).offset();var c=d(b).css("overflow")!="hidden";this.containment=[a.left+(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(d(b).css("borderTopWidth"),
+10)||0)+(parseInt(d(b).css("paddingTop"),10)||0)-this.margins.top,a.left+(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"),10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top]}},_convertPositionTo:function(a,b){if(!b)b=
+this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);return{top:b.top+this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&
+this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,e=/(html|body)/i.test(c[0].tagName);if(this.cssPosition=="relative"&&!(this.scrollParent[0]!=document&&this.scrollParent[0]!=this.offsetParent[0]))this.offset.relative=this._getRelativeOffset();
+var f=a.pageX,g=a.pageY;if(this.originalPosition){if(this.containment){if(a.pageX-this.offset.click.left<this.containment[0])f=this.containment[0]+this.offset.click.left;if(a.pageY-this.offset.click.top<this.containment[1])g=this.containment[1]+this.offset.click.top;if(a.pageX-this.offset.click.left>this.containment[2])f=this.containment[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>this.containment[3])g=this.containment[3]+this.offset.click.top}if(b.grid){g=this.originalPageY+Math.round((g-
+this.originalPageY)/b.grid[1])*b.grid[1];g=this.containment?!(g-this.offset.click.top<this.containment[1]||g-this.offset.click.top>this.containment[3])?g:!(g-this.offset.click.top<this.containment[1])?g-b.grid[1]:g+b.grid[1]:g;f=this.originalPageX+Math.round((f-this.originalPageX)/b.grid[0])*b.grid[0];f=this.containment?!(f-this.offset.click.left<this.containment[0]||f-this.offset.click.left>this.containment[2])?f:!(f-this.offset.click.left<this.containment[0])?f-b.grid[0]:f+b.grid[0]:f}}return{top:g-
+this.offset.click.top-this.offset.relative.top-this.offset.parent.top+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollTop():e?0:c.scrollTop()),left:f-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+(d.browser.safari&&this.cssPosition=="fixed"?0:this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():e?0:c.scrollLeft())}},_rearrange:function(a,b,c,e){c?c[0].appendChild(this.placeholder[0]):b.item[0].parentNode.insertBefore(this.placeholder[0],
+this.direction=="down"?b.item[0]:b.item[0].nextSibling);this.counter=this.counter?++this.counter:1;var f=this,g=this.counter;window.setTimeout(function(){g==f.counter&&f.refreshPositions(!e)},0)},_clear:function(a,b){this.reverting=false;var c=[];!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem);this._noFinalSort=null;if(this.helper[0]==this.currentItem[0]){for(var e in this._storedCSS)if(this._storedCSS[e]=="auto"||this._storedCSS[e]=="static")this._storedCSS[e]=
+"";this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();this.fromOutside&&!b&&c.push(function(f){this._trigger("receive",f,this._uiHash(this.fromOutside))});if((this.fromOutside||this.domPosition.prev!=this.currentItem.prev().not(".ui-sortable-helper")[0]||this.domPosition.parent!=this.currentItem.parent()[0])&&!b)c.push(function(f){this._trigger("update",f,this._uiHash())});if(!d.ui.contains(this.element[0],this.currentItem[0])){b||c.push(function(f){this._trigger("remove",
+f,this._uiHash())});for(e=this.containers.length-1;e>=0;e--)if(d.ui.contains(this.containers[e].element[0],this.currentItem[0])&&!b){c.push(function(f){return function(g){f._trigger("receive",g,this._uiHash(this))}}.call(this,this.containers[e]));c.push(function(f){return function(g){f._trigger("update",g,this._uiHash(this))}}.call(this,this.containers[e]))}}for(e=this.containers.length-1;e>=0;e--){b||c.push(function(f){return function(g){f._trigger("deactivate",g,this._uiHash(this))}}.call(this,
+this.containers[e]));if(this.containers[e].containerCache.over){c.push(function(f){return function(g){f._trigger("out",g,this._uiHash(this))}}.call(this,this.containers[e]));this.containers[e].containerCache.over=0}}this._storedCursor&&d("body").css("cursor",this._storedCursor);this._storedOpacity&&this.helper.css("opacity",this._storedOpacity);if(this._storedZIndex)this.helper.css("zIndex",this._storedZIndex=="auto"?"":this._storedZIndex);this.dragging=false;if(this.cancelHelperRemoval){if(!b){this._trigger("beforeStop",
+a,this._uiHash());for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}return false}b||this._trigger("beforeStop",a,this._uiHash());this.placeholder[0].parentNode.removeChild(this.placeholder[0]);this.helper[0]!=this.currentItem[0]&&this.helper.remove();this.helper=null;if(!b){for(e=0;e<c.length;e++)c[e].call(this,a);this._trigger("stop",a,this._uiHash())}this.fromOutside=false;return true},_trigger:function(){d.Widget.prototype._trigger.apply(this,arguments)===false&&this.cancel()},
+_uiHash:function(a){var b=a||this;return{helper:b.helper,placeholder:b.placeholder||d([]),position:b.position,originalPosition:b.originalPosition,offset:b.positionAbs,item:b.currentItem,sender:a?a.element:null}}});d.extend(d.ui.sortable,{version:"1.8.14"})})(jQuery);
+;/*
+ * jQuery UI Accordion 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Accordion
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ */
+(function(c){c.widget("ui.accordion",{options:{active:0,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()===location.href.toLowerCase()}},_create:function(){var a=this,b=a.options;a.running=0;a.element.addClass("ui-accordion ui-widget ui-helper-reset").children("li").addClass("ui-accordion-li-fix");
+a.headers=a.element.find(b.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){b.disabled||c(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){b.disabled||c(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){b.disabled||c(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){b.disabled||c(this).removeClass("ui-state-focus")});a.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");
+if(b.navigation){var d=a.element.find("a").filter(b.navigationFilter).eq(0);if(d.length){var h=d.closest(".ui-accordion-header");a.active=h.length?h:d.closest(".ui-accordion-content").prev()}}a.active=a._findActive(a.active||b.active).addClass("ui-state-default ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");a.active.next().addClass("ui-accordion-content-active");a._createIcons();a.resize();a.element.attr("role","tablist");a.headers.attr("role","tab").bind("keydown.accordion",
+function(f){return a._keydown(f)}).next().attr("role","tabpanel");a.headers.not(a.active||"").attr({"aria-expanded":"false","aria-selected":"false",tabIndex:-1}).next().hide();a.active.length?a.active.attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}):a.headers.eq(0).attr("tabIndex",0);c.browser.safari||a.headers.find("a").attr("tabIndex",-1);b.event&&a.headers.bind(b.event.split(" ").join(".accordion ")+".accordion",function(f){a._clickHandler.call(a,f,this);f.preventDefault()})},_createIcons:function(){var a=
+this.options;if(a.icons){c("<span></span>").addClass("ui-icon "+a.icons.header).prependTo(this.headers);this.active.children(".ui-icon").toggleClass(a.icons.header).toggleClass(a.icons.headerSelected);this.element.addClass("ui-accordion-icons")}},_destroyIcons:function(){this.headers.children(".ui-icon").remove();this.element.removeClass("ui-accordion-icons")},destroy:function(){var a=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-accordion-disabled ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("tabIndex");
+this.headers.find("a").removeAttr("tabIndex");this._destroyIcons();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-accordion-disabled ui-state-disabled");if(a.autoHeight||a.fillHeight)b.css("height","");return c.Widget.prototype.destroy.call(this)},_setOption:function(a,b){c.Widget.prototype._setOption.apply(this,arguments);a=="active"&&this.activate(b);if(a=="icons"){this._destroyIcons();
+b&&this._createIcons()}if(a=="disabled")this.headers.add(this.headers.next())[b?"addClass":"removeClass"]("ui-accordion-disabled ui-state-disabled")},_keydown:function(a){if(!(this.options.disabled||a.altKey||a.ctrlKey)){var b=c.ui.keyCode,d=this.headers.length,h=this.headers.index(a.target),f=false;switch(a.keyCode){case b.RIGHT:case b.DOWN:f=this.headers[(h+1)%d];break;case b.LEFT:case b.UP:f=this.headers[(h-1+d)%d];break;case b.SPACE:case b.ENTER:this._clickHandler({target:a.target},a.target);
+a.preventDefault()}if(f){c(a.target).attr("tabIndex",-1);c(f).attr("tabIndex",0);f.focus();return false}return true}},resize:function(){var a=this.options,b;if(a.fillSpace){if(c.browser.msie){var d=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}b=this.element.parent().height();c.browser.msie&&this.element.parent().css("overflow",d);this.headers.each(function(){b-=c(this).outerHeight(true)});this.headers.next().each(function(){c(this).height(Math.max(0,b-c(this).innerHeight()+
+c(this).height()))}).css("overflow","auto")}else if(a.autoHeight){b=0;this.headers.next().each(function(){b=Math.max(b,c(this).height("").height())}).height(b)}return this},activate:function(a){this.options.active=a;a=this._findActive(a)[0];this._clickHandler({target:a},a);return this},_findActive:function(a){return a?typeof a==="number"?this.headers.filter(":eq("+a+")"):this.headers.not(this.headers.not(a)):a===false?c([]):this.headers.filter(":eq(0)")},_clickHandler:function(a,b){var d=this.options;
+if(!d.disabled)if(a.target){a=c(a.currentTarget||b);b=a[0]===this.active[0];d.active=d.collapsible&&b?false:this.headers.index(a);if(!(this.running||!d.collapsible&&b)){var h=this.active;j=a.next();g=this.active.next();e={options:d,newHeader:b&&d.collapsible?c([]):a,oldHeader:this.active,newContent:b&&d.collapsible?c([]):j,oldContent:g};var f=this.headers.index(this.active[0])>this.headers.index(a[0]);this.active=b?c([]):a;this._toggle(j,g,e,b,f);h.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);
+if(!b){a.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").children(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);a.next().addClass("ui-accordion-content-active")}}}else if(d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").children(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var g=this.active.next(),
+e={options:d,newHeader:c([]),oldHeader:d.active,newContent:c([]),oldContent:g},j=this.active=c([]);this._toggle(j,g,e)}},_toggle:function(a,b,d,h,f){var g=this,e=g.options;g.toShow=a;g.toHide=b;g.data=d;var j=function(){if(g)return g._completed.apply(g,arguments)};g._trigger("changestart",null,g.data);g.running=b.size()===0?a.size():b.size();if(e.animated){d={};d=e.collapsible&&h?{toShow:c([]),toHide:b,complete:j,down:f,autoHeight:e.autoHeight||e.fillSpace}:{toShow:a,toHide:b,complete:j,down:f,autoHeight:e.autoHeight||
+e.fillSpace};if(!e.proxied)e.proxied=e.animated;if(!e.proxiedDuration)e.proxiedDuration=e.duration;e.animated=c.isFunction(e.proxied)?e.proxied(d):e.proxied;e.duration=c.isFunction(e.proxiedDuration)?e.proxiedDuration(d):e.proxiedDuration;h=c.ui.accordion.animations;var i=e.duration,k=e.animated;if(k&&!h[k]&&!c.easing[k])k="slide";h[k]||(h[k]=function(l){this.slide(l,{easing:k,duration:i||700})});h[k](d)}else{if(e.collapsible&&h)a.toggle();else{b.hide();a.show()}j(true)}b.prev().attr({"aria-expanded":"false",
+"aria-selected":"false",tabIndex:-1}).blur();a.prev().attr({"aria-expanded":"true","aria-selected":"true",tabIndex:0}).focus()},_completed:function(a){this.running=a?0:--this.running;if(!this.running){this.options.clearStyle&&this.toShow.add(this.toHide).css({height:"",overflow:""});this.toHide.removeClass("ui-accordion-content-active");if(this.toHide.length)this.toHide.parent()[0].className=this.toHide.parent()[0].className;this._trigger("change",null,this.data)}}});c.extend(c.ui.accordion,{version:"1.8.14",
+animations:{slide:function(a,b){a=c.extend({easing:"swing",duration:300},a,b);if(a.toHide.size())if(a.toShow.size()){var d=a.toShow.css("overflow"),h=0,f={},g={},e;b=a.toShow;e=b[0].style.width;b.width(parseInt(b.parent().width(),10)-parseInt(b.css("paddingLeft"),10)-parseInt(b.css("paddingRight"),10)-(parseInt(b.css("borderLeftWidth"),10)||0)-(parseInt(b.css("borderRightWidth"),10)||0));c.each(["height","paddingTop","paddingBottom"],function(j,i){g[i]="hide";j=(""+c.css(a.toShow[0],i)).match(/^([\d+-.]+)(.*)$/);
+f[i]={value:j[1],unit:j[2]||"px"}});a.toShow.css({height:0,overflow:"hidden"}).show();a.toHide.filter(":hidden").each(a.complete).end().filter(":visible").animate(g,{step:function(j,i){if(i.prop=="height")h=i.end-i.start===0?0:(i.now-i.start)/(i.end-i.start);a.toShow[0].style[i.prop]=h*f[i.prop].value+f[i.prop].unit},duration:a.duration,easing:a.easing,complete:function(){a.autoHeight||a.toShow.css("height","");a.toShow.css({width:e,overflow:d});a.complete()}})}else a.toHide.animate({height:"hide",
+paddingTop:"hide",paddingBottom:"hide"},a);else a.toShow.animate({height:"show",paddingTop:"show",paddingBottom:"show"},a)},bounceslide:function(a){this.slide(a,{easing:a.down?"easeOutBounce":"swing",duration:a.down?1E3:200})}}})})(jQuery);
+;/*
+ * jQuery UI Autocomplete 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *	jquery.ui.position.js
+ */
+(function(d){var e=0;d.widget("ui.autocomplete",{options:{appendTo:"body",autoFocus:false,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null},pending:0,_create:function(){var a=this,b=this.element[0].ownerDocument,g;this.element.addClass("ui-autocomplete-input").attr("autocomplete","off").attr({role:"textbox","aria-autocomplete":"list","aria-haspopup":"true"}).bind("keydown.autocomplete",function(c){if(!(a.options.disabled||a.element.attr("readonly"))){g=
+false;var f=d.ui.keyCode;switch(c.keyCode){case f.PAGE_UP:a._move("previousPage",c);break;case f.PAGE_DOWN:a._move("nextPage",c);break;case f.UP:a._move("previous",c);c.preventDefault();break;case f.DOWN:a._move("next",c);c.preventDefault();break;case f.ENTER:case f.NUMPAD_ENTER:if(a.menu.active){g=true;c.preventDefault()}case f.TAB:if(!a.menu.active)return;a.menu.select(c);break;case f.ESCAPE:a.element.val(a.term);a.close(c);break;default:clearTimeout(a.searching);a.searching=setTimeout(function(){if(a.term!=
+a.element.val()){a.selectedItem=null;a.search(null,c)}},a.options.delay);break}}}).bind("keypress.autocomplete",function(c){if(g){g=false;c.preventDefault()}}).bind("focus.autocomplete",function(){if(!a.options.disabled){a.selectedItem=null;a.previous=a.element.val()}}).bind("blur.autocomplete",function(c){if(!a.options.disabled){clearTimeout(a.searching);a.closing=setTimeout(function(){a.close(c);a._change(c)},150)}});this._initSource();this.response=function(){return a._response.apply(a,arguments)};
+this.menu=d("<ul></ul>").addClass("ui-autocomplete").appendTo(d(this.options.appendTo||"body",b)[0]).mousedown(function(c){var f=a.menu.element[0];d(c.target).closest(".ui-menu-item").length||setTimeout(function(){d(document).one("mousedown",function(h){h.target!==a.element[0]&&h.target!==f&&!d.ui.contains(f,h.target)&&a.close()})},1);setTimeout(function(){clearTimeout(a.closing)},13)}).menu({focus:function(c,f){f=f.item.data("item.autocomplete");false!==a._trigger("focus",c,{item:f})&&/^key/.test(c.originalEvent.type)&&
+a.element.val(f.value)},selected:function(c,f){var h=f.item.data("item.autocomplete"),i=a.previous;if(a.element[0]!==b.activeElement){a.element.focus();a.previous=i;setTimeout(function(){a.previous=i;a.selectedItem=h},1)}false!==a._trigger("select",c,{item:h})&&a.element.val(h.value);a.term=a.element.val();a.close(c);a.selectedItem=h},blur:function(){a.menu.element.is(":visible")&&a.element.val()!==a.term&&a.element.val(a.term)}}).zIndex(this.element.zIndex()+1).css({top:0,left:0}).hide().data("menu");
+d.fn.bgiframe&&this.menu.element.bgiframe()},destroy:function(){this.element.removeClass("ui-autocomplete-input").removeAttr("autocomplete").removeAttr("role").removeAttr("aria-autocomplete").removeAttr("aria-haspopup");this.menu.element.remove();d.Widget.prototype.destroy.call(this)},_setOption:function(a,b){d.Widget.prototype._setOption.apply(this,arguments);a==="source"&&this._initSource();if(a==="appendTo")this.menu.element.appendTo(d(b||"body",this.element[0].ownerDocument)[0]);a==="disabled"&&
+b&&this.xhr&&this.xhr.abort()},_initSource:function(){var a=this,b,g;if(d.isArray(this.options.source)){b=this.options.source;this.source=function(c,f){f(d.ui.autocomplete.filter(b,c.term))}}else if(typeof this.options.source==="string"){g=this.options.source;this.source=function(c,f){a.xhr&&a.xhr.abort();a.xhr=d.ajax({url:g,data:c,dataType:"json",autocompleteRequest:++e,success:function(h){this.autocompleteRequest===e&&f(h)},error:function(){this.autocompleteRequest===e&&f([])}})}}else this.source=
+this.options.source},search:function(a,b){a=a!=null?a:this.element.val();this.term=this.element.val();if(a.length<this.options.minLength)return this.close(b);clearTimeout(this.closing);if(this._trigger("search",b)!==false)return this._search(a)},_search:function(a){this.pending++;this.element.addClass("ui-autocomplete-loading");this.source({term:a},this.response)},_response:function(a){if(!this.options.disabled&&a&&a.length){a=this._normalize(a);this._suggest(a);this._trigger("open")}else this.close();
+this.pending--;this.pending||this.element.removeClass("ui-autocomplete-loading")},close:function(a){clearTimeout(this.closing);if(this.menu.element.is(":visible")){this.menu.element.hide();this.menu.deactivate();this._trigger("close",a)}},_change:function(a){this.previous!==this.element.val()&&this._trigger("change",a,{item:this.selectedItem})},_normalize:function(a){if(a.length&&a[0].label&&a[0].value)return a;return d.map(a,function(b){if(typeof b==="string")return{label:b,value:b};return d.extend({label:b.label||
+b.value,value:b.value||b.label},b)})},_suggest:function(a){var b=this.menu.element.empty().zIndex(this.element.zIndex()+1);this._renderMenu(b,a);this.menu.deactivate();this.menu.refresh();b.show();this._resizeMenu();b.position(d.extend({of:this.element},this.options.position));this.options.autoFocus&&this.menu.next(new d.Event("mouseover"))},_resizeMenu:function(){var a=this.menu.element;a.outerWidth(Math.max(a.width("").outerWidth(),this.element.outerWidth()))},_renderMenu:function(a,b){var g=this;
+d.each(b,function(c,f){g._renderItem(a,f)})},_renderItem:function(a,b){return d("<li></li>").data("item.autocomplete",b).append(d("<a></a>").text(b.label)).appendTo(a)},_move:function(a,b){if(this.menu.element.is(":visible"))if(this.menu.first()&&/^previous/.test(a)||this.menu.last()&&/^next/.test(a)){this.element.val(this.term);this.menu.deactivate()}else this.menu[a](b);else this.search(null,b)},widget:function(){return this.menu.element}});d.extend(d.ui.autocomplete,{escapeRegex:function(a){return a.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,
+"\\$&")},filter:function(a,b){var g=new RegExp(d.ui.autocomplete.escapeRegex(b),"i");return d.grep(a,function(c){return g.test(c.label||c.value||c)})}})})(jQuery);
+(function(d){d.widget("ui.menu",{_create:function(){var e=this;this.element.addClass("ui-menu ui-widget ui-widget-content ui-corner-all").attr({role:"listbox","aria-activedescendant":"ui-active-menuitem"}).click(function(a){if(d(a.target).closest(".ui-menu-item a").length){a.preventDefault();e.select(a)}});this.refresh()},refresh:function(){var e=this;this.element.children("li:not(.ui-menu-item):has(a)").addClass("ui-menu-item").attr("role","menuitem").children("a").addClass("ui-corner-all").attr("tabindex",
+-1).mouseenter(function(a){e.activate(a,d(this).parent())}).mouseleave(function(){e.deactivate()})},activate:function(e,a){this.deactivate();if(this.hasScroll()){var b=a.offset().top-this.element.offset().top,g=this.element.scrollTop(),c=this.element.height();if(b<0)this.element.scrollTop(g+b);else b>=c&&this.element.scrollTop(g+b-c+a.height())}this.active=a.eq(0).children("a").addClass("ui-state-hover").attr("id","ui-active-menuitem").end();this._trigger("focus",e,{item:a})},deactivate:function(){if(this.active){this.active.children("a").removeClass("ui-state-hover").removeAttr("id");
+this._trigger("blur");this.active=null}},next:function(e){this.move("next",".ui-menu-item:first",e)},previous:function(e){this.move("prev",".ui-menu-item:last",e)},first:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},last:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},move:function(e,a,b){if(this.active){e=this.active[e+"All"](".ui-menu-item").eq(0);e.length?this.activate(b,e):this.activate(b,this.element.children(a))}else this.activate(b,
+this.element.children(a))},nextPage:function(e){if(this.hasScroll())if(!this.active||this.last())this.activate(e,this.element.children(".ui-menu-item:first"));else{var a=this.active.offset().top,b=this.element.height(),g=this.element.children(".ui-menu-item").filter(function(){var c=d(this).offset().top-a-b+d(this).height();return c<10&&c>-10});g.length||(g=this.element.children(".ui-menu-item:last"));this.activate(e,g)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
+this.last()?":first":":last"))},previousPage:function(e){if(this.hasScroll())if(!this.active||this.first())this.activate(e,this.element.children(".ui-menu-item:last"));else{var a=this.active.offset().top,b=this.element.height();result=this.element.children(".ui-menu-item").filter(function(){var g=d(this).offset().top-a+b-d(this).height();return g<10&&g>-10});result.length||(result=this.element.children(".ui-menu-item:first"));this.activate(e,result)}else this.activate(e,this.element.children(".ui-menu-item").filter(!this.active||
+this.first()?":last":":first"))},hasScroll:function(){return this.element.height()<this.element[d.fn.prop?"prop":"attr"]("scrollHeight")},select:function(e){this._trigger("selected",e,{item:this.active})}})})(jQuery);
+;/*
+ * jQuery UI Button 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Button
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ */
+(function(b){var h,i,j,g,l=function(){var a=b(this).find(":ui-button");setTimeout(function(){a.button("refresh")},1)},k=function(a){var c=a.name,e=a.form,f=b([]);if(c)f=e?b(e).find("[name='"+c+"']"):b("[name='"+c+"']",a.ownerDocument).filter(function(){return!this.form});return f};b.widget("ui.button",{options:{disabled:null,text:true,label:null,icons:{primary:null,secondary:null}},_create:function(){this.element.closest("form").unbind("reset.button").bind("reset.button",l);if(typeof this.options.disabled!==
+"boolean")this.options.disabled=this.element.attr("disabled");this._determineButtonType();this.hasTitle=!!this.buttonElement.attr("title");var a=this,c=this.options,e=this.type==="checkbox"||this.type==="radio",f="ui-state-hover"+(!e?" ui-state-active":"");if(c.label===null)c.label=this.buttonElement.html();if(this.element.is(":disabled"))c.disabled=true;this.buttonElement.addClass("ui-button ui-widget ui-state-default ui-corner-all").attr("role","button").bind("mouseenter.button",function(){if(!c.disabled){b(this).addClass("ui-state-hover");
+this===h&&b(this).addClass("ui-state-active")}}).bind("mouseleave.button",function(){c.disabled||b(this).removeClass(f)}).bind("click.button",function(d){if(c.disabled){d.preventDefault();d.stopImmediatePropagation()}});this.element.bind("focus.button",function(){a.buttonElement.addClass("ui-state-focus")}).bind("blur.button",function(){a.buttonElement.removeClass("ui-state-focus")});if(e){this.element.bind("change.button",function(){g||a.refresh()});this.buttonElement.bind("mousedown.button",function(d){if(!c.disabled){g=
+false;i=d.pageX;j=d.pageY}}).bind("mouseup.button",function(d){if(!c.disabled)if(i!==d.pageX||j!==d.pageY)g=true})}if(this.type==="checkbox")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).toggleClass("ui-state-active");a.buttonElement.attr("aria-pressed",a.element[0].checked)});else if(this.type==="radio")this.buttonElement.bind("click.button",function(){if(c.disabled||g)return false;b(this).addClass("ui-state-active");a.buttonElement.attr("aria-pressed",true);
+var d=a.element[0];k(d).not(d).map(function(){return b(this).button("widget")[0]}).removeClass("ui-state-active").attr("aria-pressed",false)});else{this.buttonElement.bind("mousedown.button",function(){if(c.disabled)return false;b(this).addClass("ui-state-active");h=this;b(document).one("mouseup",function(){h=null})}).bind("mouseup.button",function(){if(c.disabled)return false;b(this).removeClass("ui-state-active")}).bind("keydown.button",function(d){if(c.disabled)return false;if(d.keyCode==b.ui.keyCode.SPACE||
+d.keyCode==b.ui.keyCode.ENTER)b(this).addClass("ui-state-active")}).bind("keyup.button",function(){b(this).removeClass("ui-state-active")});this.buttonElement.is("a")&&this.buttonElement.keyup(function(d){d.keyCode===b.ui.keyCode.SPACE&&b(this).click()})}this._setOption("disabled",c.disabled);this._resetButton()},_determineButtonType:function(){this.type=this.element.is(":checkbox")?"checkbox":this.element.is(":radio")?"radio":this.element.is("input")?"input":"button";if(this.type==="checkbox"||this.type===
+"radio"){var a=this.element.parents().filter(":last"),c="label[for="+this.element.attr("id")+"]";this.buttonElement=a.find(c);if(!this.buttonElement.length){a=a.length?a.siblings():this.element.siblings();this.buttonElement=a.filter(c);if(!this.buttonElement.length)this.buttonElement=a.find(c)}this.element.addClass("ui-helper-hidden-accessible");(a=this.element.is(":checked"))&&this.buttonElement.addClass("ui-state-active");this.buttonElement.attr("aria-pressed",a)}else this.buttonElement=this.element},
+widget:function(){return this.buttonElement},destroy:function(){this.element.removeClass("ui-helper-hidden-accessible");this.buttonElement.removeClass("ui-button ui-widget ui-state-default ui-corner-all ui-state-hover ui-state-active  ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only").removeAttr("role").removeAttr("aria-pressed").html(this.buttonElement.find(".ui-button-text").html());this.hasTitle||this.buttonElement.removeAttr("title");
+b.Widget.prototype.destroy.call(this)},_setOption:function(a,c){b.Widget.prototype._setOption.apply(this,arguments);if(a==="disabled")c?this.element.attr("disabled",true):this.element.removeAttr("disabled");else this._resetButton()},refresh:function(){var a=this.element.is(":disabled");a!==this.options.disabled&&this._setOption("disabled",a);if(this.type==="radio")k(this.element[0]).each(function(){b(this).is(":checked")?b(this).button("widget").addClass("ui-state-active").attr("aria-pressed",true):
+b(this).button("widget").removeClass("ui-state-active").attr("aria-pressed",false)});else if(this.type==="checkbox")this.element.is(":checked")?this.buttonElement.addClass("ui-state-active").attr("aria-pressed",true):this.buttonElement.removeClass("ui-state-active").attr("aria-pressed",false)},_resetButton:function(){if(this.type==="input")this.options.label&&this.element.val(this.options.label);else{var a=this.buttonElement.removeClass("ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only"),
+c=b("<span></span>").addClass("ui-button-text").html(this.options.label).appendTo(a.empty()).text(),e=this.options.icons,f=e.primary&&e.secondary,d=[];if(e.primary||e.secondary){if(this.options.text)d.push("ui-button-text-icon"+(f?"s":e.primary?"-primary":"-secondary"));e.primary&&a.prepend("<span class='ui-button-icon-primary ui-icon "+e.primary+"'></span>");e.secondary&&a.append("<span class='ui-button-icon-secondary ui-icon "+e.secondary+"'></span>");if(!this.options.text){d.push(f?"ui-button-icons-only":
+"ui-button-icon-only");this.hasTitle||a.attr("title",c)}}else d.push("ui-button-text-only");a.addClass(d.join(" "))}}});b.widget("ui.buttonset",{options:{items:":button, :submit, :reset, :checkbox, :radio, a, :data(button)"},_create:function(){this.element.addClass("ui-buttonset")},_init:function(){this.refresh()},_setOption:function(a,c){a==="disabled"&&this.buttons.button("option",a,c);b.Widget.prototype._setOption.apply(this,arguments)},refresh:function(){var a=this.element.css("direction")===
+"ltr";this.buttons=this.element.find(this.options.items).filter(":ui-button").button("refresh").end().not(":ui-button").button().end().map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-all ui-corner-left ui-corner-right").filter(":first").addClass(a?"ui-corner-left":"ui-corner-right").end().filter(":last").addClass(a?"ui-corner-right":"ui-corner-left").end().end()},destroy:function(){this.element.removeClass("ui-buttonset");this.buttons.map(function(){return b(this).button("widget")[0]}).removeClass("ui-corner-left ui-corner-right").end().button("destroy");
+b.Widget.prototype.destroy.call(this)}})})(jQuery);
+;/*
+ * jQuery UI Dialog 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Dialog
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ *  jquery.ui.button.js
+ *	jquery.ui.draggable.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.position.js
+ *	jquery.ui.resizable.js
+ */
+(function(c,l){var m={buttons:true,height:true,maxHeight:true,maxWidth:true,minHeight:true,minWidth:true,width:true},n={maxHeight:true,maxWidth:true,minHeight:true,minWidth:true},o=c.attrFn||{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true,click:true};c.widget("ui.dialog",{options:{autoOpen:true,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,
+position:{my:"center",at:"center",collision:"fit",using:function(a){var b=c(this).css(a).offset().top;b<0&&c(this).css("top",a.top-b)}},resizable:true,show:null,stack:true,title:"",width:300,zIndex:1E3},_create:function(){this.originalTitle=this.element.attr("title");if(typeof this.originalTitle!=="string")this.originalTitle="";this.options.title=this.options.title||this.originalTitle;var a=this,b=a.options,d=b.title||"&#160;",e=c.ui.dialog.getTitleId(a.element),g=(a.uiDialog=c("<div></div>")).appendTo(document.body).hide().addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+
+b.dialogClass).css({zIndex:b.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(i){if(b.closeOnEscape&&i.keyCode&&i.keyCode===c.ui.keyCode.ESCAPE){a.close(i);i.preventDefault()}}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(i){a.moveToTop(false,i)});a.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(g);var f=(a.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(g),
+h=c('<a href="#"></a>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){h.addClass("ui-state-hover")},function(){h.removeClass("ui-state-hover")}).focus(function(){h.addClass("ui-state-focus")}).blur(function(){h.removeClass("ui-state-focus")}).click(function(i){a.close(i);return false}).appendTo(f);(a.uiDialogTitlebarCloseText=c("<span></span>")).addClass("ui-icon ui-icon-closethick").text(b.closeText).appendTo(h);c("<span></span>").addClass("ui-dialog-title").attr("id",
+e).html(d).prependTo(f);if(c.isFunction(b.beforeclose)&&!c.isFunction(b.beforeClose))b.beforeClose=b.beforeclose;f.find("*").add(f).disableSelection();b.draggable&&c.fn.draggable&&a._makeDraggable();b.resizable&&c.fn.resizable&&a._makeResizable();a._createButtons(b.buttons);a._isOpen=false;c.fn.bgiframe&&g.bgiframe()},_init:function(){this.options.autoOpen&&this.open()},destroy:function(){var a=this;a.overlay&&a.overlay.destroy();a.uiDialog.hide();a.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");
+a.uiDialog.remove();a.originalTitle&&a.element.attr("title",a.originalTitle);return a},widget:function(){return this.uiDialog},close:function(a){var b=this,d,e;if(false!==b._trigger("beforeClose",a)){b.overlay&&b.overlay.destroy();b.uiDialog.unbind("keypress.ui-dialog");b._isOpen=false;if(b.options.hide)b.uiDialog.hide(b.options.hide,function(){b._trigger("close",a)});else{b.uiDialog.hide();b._trigger("close",a)}c.ui.dialog.overlay.resize();if(b.options.modal){d=0;c(".ui-dialog").each(function(){if(this!==
+b.uiDialog[0]){e=c(this).css("z-index");isNaN(e)||(d=Math.max(d,e))}});c.ui.dialog.maxZ=d}return b}},isOpen:function(){return this._isOpen},moveToTop:function(a,b){var d=this,e=d.options;if(e.modal&&!a||!e.stack&&!e.modal)return d._trigger("focus",b);if(e.zIndex>c.ui.dialog.maxZ)c.ui.dialog.maxZ=e.zIndex;if(d.overlay){c.ui.dialog.maxZ+=1;d.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=c.ui.dialog.maxZ)}a={scrollTop:d.element.attr("scrollTop"),scrollLeft:d.element.attr("scrollLeft")};c.ui.dialog.maxZ+=
+1;d.uiDialog.css("z-index",c.ui.dialog.maxZ);d.element.attr(a);d._trigger("focus",b);return d},open:function(){if(!this._isOpen){var a=this,b=a.options,d=a.uiDialog;a.overlay=b.modal?new c.ui.dialog.overlay(a):null;a._size();a._position(b.position);d.show(b.show);a.moveToTop(true);b.modal&&d.bind("keypress.ui-dialog",function(e){if(e.keyCode===c.ui.keyCode.TAB){var g=c(":tabbable",this),f=g.filter(":first");g=g.filter(":last");if(e.target===g[0]&&!e.shiftKey){f.focus(1);return false}else if(e.target===
+f[0]&&e.shiftKey){g.focus(1);return false}}});c(a.element.find(":tabbable").get().concat(d.find(".ui-dialog-buttonpane :tabbable").get().concat(d.get()))).eq(0).focus();a._isOpen=true;a._trigger("open");return a}},_createButtons:function(a){var b=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix"),g=c("<div></div>").addClass("ui-dialog-buttonset").appendTo(e);b.uiDialog.find(".ui-dialog-buttonpane").remove();typeof a==="object"&&a!==null&&c.each(a,
+function(){return!(d=true)});if(d){c.each(a,function(f,h){h=c.isFunction(h)?{click:h,text:f}:h;var i=c('<button type="button"></button>').click(function(){h.click.apply(b.element[0],arguments)}).appendTo(g);c.each(h,function(j,k){if(j!=="click")j in o?i[j](k):i.attr(j,k)});c.fn.button&&i.button()});e.appendTo(b.uiDialog)}},_makeDraggable:function(){function a(f){return{position:f.position,offset:f.offset}}var b=this,d=b.options,e=c(document),g;b.uiDialog.draggable({cancel:".ui-dialog-content, .ui-dialog-titlebar-close",
+handle:".ui-dialog-titlebar",containment:"document",start:function(f,h){g=d.height==="auto"?"auto":c(this).height();c(this).height(c(this).height()).addClass("ui-dialog-dragging");b._trigger("dragStart",f,a(h))},drag:function(f,h){b._trigger("drag",f,a(h))},stop:function(f,h){d.position=[h.position.left-e.scrollLeft(),h.position.top-e.scrollTop()];c(this).removeClass("ui-dialog-dragging").height(g);b._trigger("dragStop",f,a(h));c.ui.dialog.overlay.resize()}})},_makeResizable:function(a){function b(f){return{originalPosition:f.originalPosition,
+originalSize:f.originalSize,position:f.position,size:f.size}}a=a===l?this.options.resizable:a;var d=this,e=d.options,g=d.uiDialog.css("position");a=typeof a==="string"?a:"n,e,s,w,se,sw,ne,nw";d.uiDialog.resizable({cancel:".ui-dialog-content",containment:"document",alsoResize:d.element,maxWidth:e.maxWidth,maxHeight:e.maxHeight,minWidth:e.minWidth,minHeight:d._minHeight(),handles:a,start:function(f,h){c(this).addClass("ui-dialog-resizing");d._trigger("resizeStart",f,b(h))},resize:function(f,h){d._trigger("resize",
+f,b(h))},stop:function(f,h){c(this).removeClass("ui-dialog-resizing");e.height=c(this).height();e.width=c(this).width();d._trigger("resizeStop",f,b(h));c.ui.dialog.overlay.resize()}}).css("position",g).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_minHeight:function(){var a=this.options;return a.height==="auto"?a.minHeight:Math.min(a.minHeight,a.height)},_position:function(a){var b=[],d=[0,0],e;if(a){if(typeof a==="string"||typeof a==="object"&&"0"in a){b=a.split?a.split(" "):
+[a[0],a[1]];if(b.length===1)b[1]=b[0];c.each(["left","top"],function(g,f){if(+b[g]===b[g]){d[g]=b[g];b[g]=f}});a={my:b.join(" "),at:b.join(" "),offset:d.join(" ")}}a=c.extend({},c.ui.dialog.prototype.options.position,a)}else a=c.ui.dialog.prototype.options.position;(e=this.uiDialog.is(":visible"))||this.uiDialog.show();this.uiDialog.css({top:0,left:0}).position(c.extend({of:window},a));e||this.uiDialog.hide()},_setOptions:function(a){var b=this,d={},e=false;c.each(a,function(g,f){b._setOption(g,f);
+if(g in m)e=true;if(g in n)d[g]=f});e&&this._size();this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option",d)},_setOption:function(a,b){var d=this,e=d.uiDialog;switch(a){case "beforeclose":a="beforeClose";break;case "buttons":d._createButtons(b);break;case "closeText":d.uiDialogTitlebarCloseText.text(""+b);break;case "dialogClass":e.removeClass(d.options.dialogClass).addClass("ui-dialog ui-widget ui-widget-content ui-corner-all "+b);break;case "disabled":b?e.addClass("ui-dialog-disabled"):
+e.removeClass("ui-dialog-disabled");break;case "draggable":var g=e.is(":data(draggable)");g&&!b&&e.draggable("destroy");!g&&b&&d._makeDraggable();break;case "position":d._position(b);break;case "resizable":(g=e.is(":data(resizable)"))&&!b&&e.resizable("destroy");g&&typeof b==="string"&&e.resizable("option","handles",b);!g&&b!==false&&d._makeResizable(b);break;case "title":c(".ui-dialog-title",d.uiDialogTitlebar).html(""+(b||"&#160;"));break}c.Widget.prototype._setOption.apply(d,arguments)},_size:function(){var a=
+this.options,b,d,e=this.uiDialog.is(":visible");this.element.show().css({width:"auto",minHeight:0,height:0});if(a.minWidth>a.width)a.width=a.minWidth;b=this.uiDialog.css({height:"auto",width:a.width}).height();d=Math.max(0,a.minHeight-b);if(a.height==="auto")if(c.support.minHeight)this.element.css({minHeight:d,height:"auto"});else{this.uiDialog.show();a=this.element.css("height","auto").height();e||this.uiDialog.hide();this.element.height(Math.max(a,d))}else this.element.height(Math.max(a.height-
+b,0));this.uiDialog.is(":data(resizable)")&&this.uiDialog.resizable("option","minHeight",this._minHeight())}});c.extend(c.ui.dialog,{version:"1.8.14",uuid:0,maxZ:0,getTitleId:function(a){a=a.attr("id");if(!a){this.uuid+=1;a=this.uuid}return"ui-dialog-title-"+a},overlay:function(a){this.$el=c.ui.dialog.overlay.create(a)}});c.extend(c.ui.dialog.overlay,{instances:[],oldInstances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(a){return a+".dialog-overlay"}).join(" "),
+create:function(a){if(this.instances.length===0){setTimeout(function(){c.ui.dialog.overlay.instances.length&&c(document).bind(c.ui.dialog.overlay.events,function(d){if(c(d.target).zIndex()<c.ui.dialog.overlay.maxZ)return false})},1);c(document).bind("keydown.dialog-overlay",function(d){if(a.options.closeOnEscape&&d.keyCode&&d.keyCode===c.ui.keyCode.ESCAPE){a.close(d);d.preventDefault()}});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var b=(this.oldInstances.pop()||c("<div></div>").addClass("ui-widget-overlay")).appendTo(document.body).css({width:this.width(),
+height:this.height()});c.fn.bgiframe&&b.bgiframe();this.instances.push(b);return b},destroy:function(a){var b=c.inArray(a,this.instances);b!=-1&&this.oldInstances.push(this.instances.splice(b,1)[0]);this.instances.length===0&&c([document,window]).unbind(".dialog-overlay");a.remove();var d=0;c.each(this.instances,function(){d=Math.max(d,this.css("z-index"))});this.maxZ=d},height:function(){var a,b;if(c.browser.msie&&c.browser.version<7){a=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);
+b=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);return a<b?c(window).height()+"px":a+"px"}else return c(document).height()+"px"},width:function(){var a,b;if(c.browser.msie){a=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);b=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);return a<b?c(window).width()+"px":a+"px"}else return c(document).width()+"px"},resize:function(){var a=c([]);c.each(c.ui.dialog.overlay.instances,function(){a=
+a.add(this)});a.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);
+;/*
+ * jQuery UI Slider 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Slider
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.mouse.js
+ *	jquery.ui.widget.js
+ */
+(function(d){d.widget("ui.slider",d.ui.mouse,{widgetEventPrefix:"slide",options:{animate:false,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null},_create:function(){var b=this,a=this.options,c=this.element.find(".ui-slider-handle").addClass("ui-state-default ui-corner-all"),f=a.values&&a.values.length||1,e=[];this._mouseSliding=this._keySliding=false;this._animateOff=true;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+
+this.orientation+" ui-widget ui-widget-content ui-corner-all"+(a.disabled?" ui-slider-disabled ui-disabled":""));this.range=d([]);if(a.range){if(a.range===true){if(!a.values)a.values=[this._valueMin(),this._valueMin()];if(a.values.length&&a.values.length!==2)a.values=[a.values[0],a.values[0]]}this.range=d("<div></div>").appendTo(this.element).addClass("ui-slider-range ui-widget-header"+(a.range==="min"||a.range==="max"?" ui-slider-range-"+a.range:""))}for(var j=c.length;j<f;j+=1)e.push("<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>");
+this.handles=c.add(d(e.join("")).appendTo(b.element));this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(g){g.preventDefault()}).hover(function(){a.disabled||d(this).addClass("ui-state-hover")},function(){d(this).removeClass("ui-state-hover")}).focus(function(){if(a.disabled)d(this).blur();else{d(".ui-slider .ui-state-focus").removeClass("ui-state-focus");d(this).addClass("ui-state-focus")}}).blur(function(){d(this).removeClass("ui-state-focus")});this.handles.each(function(g){d(this).data("index.ui-slider-handle",
+g)});this.handles.keydown(function(g){var k=true,l=d(this).data("index.ui-slider-handle"),i,h,m;if(!b.options.disabled){switch(g.keyCode){case d.ui.keyCode.HOME:case d.ui.keyCode.END:case d.ui.keyCode.PAGE_UP:case d.ui.keyCode.PAGE_DOWN:case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:k=false;if(!b._keySliding){b._keySliding=true;d(this).addClass("ui-state-active");i=b._start(g,l);if(i===false)return}break}m=b.options.step;i=b.options.values&&b.options.values.length?
+(h=b.values(l)):(h=b.value());switch(g.keyCode){case d.ui.keyCode.HOME:h=b._valueMin();break;case d.ui.keyCode.END:h=b._valueMax();break;case d.ui.keyCode.PAGE_UP:h=b._trimAlignValue(i+(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.PAGE_DOWN:h=b._trimAlignValue(i-(b._valueMax()-b._valueMin())/5);break;case d.ui.keyCode.UP:case d.ui.keyCode.RIGHT:if(i===b._valueMax())return;h=b._trimAlignValue(i+m);break;case d.ui.keyCode.DOWN:case d.ui.keyCode.LEFT:if(i===b._valueMin())return;h=b._trimAlignValue(i-
+m);break}b._slide(g,l,h);return k}}).keyup(function(g){var k=d(this).data("index.ui-slider-handle");if(b._keySliding){b._keySliding=false;b._stop(g,k);b._change(g,k);d(this).removeClass("ui-state-active")}});this._refreshValue();this._animateOff=false},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy();
+return this},_mouseCapture:function(b){var a=this.options,c,f,e,j,g;if(a.disabled)return false;this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();c=this._normValueFromMouse({x:b.pageX,y:b.pageY});f=this._valueMax()-this._valueMin()+1;j=this;this.handles.each(function(k){var l=Math.abs(c-j.values(k));if(f>l){f=l;e=d(this);g=k}});if(a.range===true&&this.values(1)===a.min){g+=1;e=d(this.handles[g])}if(this._start(b,g)===false)return false;
+this._mouseSliding=true;j._handleIndex=g;e.addClass("ui-state-active").focus();a=e.offset();this._clickOffset=!d(b.target).parents().andSelf().is(".ui-slider-handle")?{left:0,top:0}:{left:b.pageX-a.left-e.width()/2,top:b.pageY-a.top-e.height()/2-(parseInt(e.css("borderTopWidth"),10)||0)-(parseInt(e.css("borderBottomWidth"),10)||0)+(parseInt(e.css("marginTop"),10)||0)};this.handles.hasClass("ui-state-hover")||this._slide(b,g,c);return this._animateOff=true},_mouseStart:function(){return true},_mouseDrag:function(b){var a=
+this._normValueFromMouse({x:b.pageX,y:b.pageY});this._slide(b,this._handleIndex,a);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._mouseSliding=false;this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._clickOffset=this._handleIndex=null;return this._animateOff=false},_detectOrientation:function(){this.orientation=this.options.orientation==="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(b){var a;if(this.orientation==="horizontal"){a=
+this.elementSize.width;b=b.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{a=this.elementSize.height;b=b.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}a=b/a;if(a>1)a=1;if(a<0)a=0;if(this.orientation==="vertical")a=1-a;b=this._valueMax()-this._valueMin();return this._trimAlignValue(this._valueMin()+a*b)},_start:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a);
+c.values=this.values()}return this._trigger("start",b,c)},_slide:function(b,a,c){var f;if(this.options.values&&this.options.values.length){f=this.values(a?0:1);if(this.options.values.length===2&&this.options.range===true&&(a===0&&c>f||a===1&&c<f))c=f;if(c!==this.values(a)){f=this.values();f[a]=c;b=this._trigger("slide",b,{handle:this.handles[a],value:c,values:f});this.values(a?0:1);b!==false&&this.values(a,c,true)}}else if(c!==this.value()){b=this._trigger("slide",b,{handle:this.handles[a],value:c});
+b!==false&&this.value(c)}},_stop:function(b,a){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a);c.values=this.values()}this._trigger("stop",b,c)},_change:function(b,a){if(!this._keySliding&&!this._mouseSliding){var c={handle:this.handles[a],value:this.value()};if(this.options.values&&this.options.values.length){c.value=this.values(a);c.values=this.values()}this._trigger("change",b,c)}},value:function(b){if(arguments.length){this.options.value=
+this._trimAlignValue(b);this._refreshValue();this._change(null,0)}else return this._value()},values:function(b,a){var c,f,e;if(arguments.length>1){this.options.values[b]=this._trimAlignValue(a);this._refreshValue();this._change(null,b)}else if(arguments.length)if(d.isArray(arguments[0])){c=this.options.values;f=arguments[0];for(e=0;e<c.length;e+=1){c[e]=this._trimAlignValue(f[e]);this._change(null,e)}this._refreshValue()}else return this.options.values&&this.options.values.length?this._values(b):
+this.value();else return this._values()},_setOption:function(b,a){var c,f=0;if(d.isArray(this.options.values))f=this.options.values.length;d.Widget.prototype._setOption.apply(this,arguments);switch(b){case "disabled":if(a){this.handles.filter(".ui-state-focus").blur();this.handles.removeClass("ui-state-hover");this.handles.attr("disabled","disabled");this.element.addClass("ui-disabled")}else{this.handles.removeAttr("disabled");this.element.removeClass("ui-disabled")}break;case "orientation":this._detectOrientation();
+this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue();break;case "value":this._animateOff=true;this._refreshValue();this._change(null,0);this._animateOff=false;break;case "values":this._animateOff=true;this._refreshValue();for(c=0;c<f;c+=1)this._change(null,c);this._animateOff=false;break}},_value:function(){var b=this.options.value;return b=this._trimAlignValue(b)},_values:function(b){var a,c;if(arguments.length){a=this.options.values[b];
+return a=this._trimAlignValue(a)}else{a=this.options.values.slice();for(c=0;c<a.length;c+=1)a[c]=this._trimAlignValue(a[c]);return a}},_trimAlignValue:function(b){if(b<=this._valueMin())return this._valueMin();if(b>=this._valueMax())return this._valueMax();var a=this.options.step>0?this.options.step:1,c=(b-this._valueMin())%a;alignValue=b-c;if(Math.abs(c)*2>=a)alignValue+=c>0?a:-a;return parseFloat(alignValue.toFixed(5))},_valueMin:function(){return this.options.min},_valueMax:function(){return this.options.max},
+_refreshValue:function(){var b=this.options.range,a=this.options,c=this,f=!this._animateOff?a.animate:false,e,j={},g,k,l,i;if(this.options.values&&this.options.values.length)this.handles.each(function(h){e=(c.values(h)-c._valueMin())/(c._valueMax()-c._valueMin())*100;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";d(this).stop(1,1)[f?"animate":"css"](j,a.animate);if(c.options.range===true)if(c.orientation==="horizontal"){if(h===0)c.range.stop(1,1)[f?"animate":"css"]({left:e+"%"},a.animate);
+if(h===1)c.range[f?"animate":"css"]({width:e-g+"%"},{queue:false,duration:a.animate})}else{if(h===0)c.range.stop(1,1)[f?"animate":"css"]({bottom:e+"%"},a.animate);if(h===1)c.range[f?"animate":"css"]({height:e-g+"%"},{queue:false,duration:a.animate})}g=e});else{k=this.value();l=this._valueMin();i=this._valueMax();e=i!==l?(k-l)/(i-l)*100:0;j[c.orientation==="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[f?"animate":"css"](j,a.animate);if(b==="min"&&this.orientation==="horizontal")this.range.stop(1,
+1)[f?"animate":"css"]({width:e+"%"},a.animate);if(b==="max"&&this.orientation==="horizontal")this.range[f?"animate":"css"]({width:100-e+"%"},{queue:false,duration:a.animate});if(b==="min"&&this.orientation==="vertical")this.range.stop(1,1)[f?"animate":"css"]({height:e+"%"},a.animate);if(b==="max"&&this.orientation==="vertical")this.range[f?"animate":"css"]({height:100-e+"%"},{queue:false,duration:a.animate})}}});d.extend(d.ui.slider,{version:"1.8.14"})})(jQuery);
+;/*
+ * jQuery UI Tabs 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Tabs
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ *	jquery.ui.widget.js
+ */
+(function(d,p){function u(){return++v}function w(){return++x}var v=0,x=0;d.widget("ui.tabs",{options:{add:null,ajaxOptions:null,cache:false,cookie:null,collapsible:false,disable:null,disabled:[],enable:null,event:"click",fx:null,idPrefix:"ui-tabs-",load:null,panelTemplate:"<div></div>",remove:null,select:null,show:null,spinner:"<em>Loading&#8230;</em>",tabTemplate:"<li><a href='#{href}'><span>#{label}</span></a></li>"},_create:function(){this._tabify(true)},_setOption:function(b,e){if(b=="selected")this.options.collapsible&&
+e==this.options.selected||this.select(e);else{this.options[b]=e;this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^\w\u00c0-\uFFFF-]/g,"")||this.options.idPrefix+u()},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+w());return d.cookie.apply(null,[b].concat(d.makeArray(arguments)))},_ui:function(b,e){return{tab:b,panel:e,index:this.anchors.index(b)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=
+d(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(b){function e(g,f){g.css("display","");!d.support.opacity&&f.opacity&&g[0].style.removeAttribute("filter")}var a=this,c=this.options,h=/^#.+/;this.list=this.element.find("ol,ul").eq(0);this.lis=d(" > li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return d("a",this)[0]});this.panels=d([]);this.anchors.each(function(g,f){var i=d(f).attr("href"),l=i.split("#")[0],q;if(l&&(l===location.toString().split("#")[0]||
+(q=d("base")[0])&&l===q.href)){i=f.hash;f.href=i}if(h.test(i))a.panels=a.panels.add(a.element.find(a._sanitizeSelector(i)));else if(i&&i!=="#"){d.data(f,"href.tabs",i);d.data(f,"load.tabs",i.replace(/#.*$/,""));i=a._tabId(f);f.href="#"+i;f=a.element.find("#"+i);if(!f.length){f=d(c.panelTemplate).attr("id",i).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(a.panels[g-1]||a.list);f.data("destroy.tabs",true)}a.panels=a.panels.add(f)}else c.disabled.push(g)});if(b){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");
+this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(c.selected===p){location.hash&&this.anchors.each(function(g,f){if(f.hash==location.hash){c.selected=g;return false}});if(typeof c.selected!=="number"&&c.cookie)c.selected=parseInt(a._cookie(),10);if(typeof c.selected!=="number"&&this.lis.filter(".ui-tabs-selected").length)c.selected=
+this.lis.index(this.lis.filter(".ui-tabs-selected"));c.selected=c.selected||(this.lis.length?0:-1)}else if(c.selected===null)c.selected=-1;c.selected=c.selected>=0&&this.anchors[c.selected]||c.selected<0?c.selected:0;c.disabled=d.unique(c.disabled.concat(d.map(this.lis.filter(".ui-state-disabled"),function(g){return a.lis.index(g)}))).sort();d.inArray(c.selected,c.disabled)!=-1&&c.disabled.splice(d.inArray(c.selected,c.disabled),1);this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");
+if(c.selected>=0&&this.anchors.length){a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash)).removeClass("ui-tabs-hide");this.lis.eq(c.selected).addClass("ui-tabs-selected ui-state-active");a.element.queue("tabs",function(){a._trigger("show",null,a._ui(a.anchors[c.selected],a.element.find(a._sanitizeSelector(a.anchors[c.selected].hash))[0]))});this.load(c.selected)}d(window).bind("unload",function(){a.lis.add(a.anchors).unbind(".tabs");a.lis=a.anchors=a.panels=null})}else c.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"));
+this.element[c.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");c.cookie&&this._cookie(c.selected,c.cookie);b=0;for(var j;j=this.lis[b];b++)d(j)[d.inArray(b,c.disabled)!=-1&&!d(j).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled");c.cache===false&&this.anchors.removeData("cache.tabs");this.lis.add(this.anchors).unbind(".tabs");if(c.event!=="mouseover"){var k=function(g,f){f.is(":not(.ui-state-disabled)")&&f.addClass("ui-state-"+g)},n=function(g,f){f.removeClass("ui-state-"+
+g)};this.lis.bind("mouseover.tabs",function(){k("hover",d(this))});this.lis.bind("mouseout.tabs",function(){n("hover",d(this))});this.anchors.bind("focus.tabs",function(){k("focus",d(this).closest("li"))});this.anchors.bind("blur.tabs",function(){n("focus",d(this).closest("li"))})}var m,o;if(c.fx)if(d.isArray(c.fx)){m=c.fx[0];o=c.fx[1]}else m=o=c.fx;var r=o?function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.hide().removeClass("ui-tabs-hide").animate(o,o.duration||"normal",
+function(){e(f,o);a._trigger("show",null,a._ui(g,f[0]))})}:function(g,f){d(g).closest("li").addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");a._trigger("show",null,a._ui(g,f[0]))},s=m?function(g,f){f.animate(m,m.duration||"normal",function(){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");e(f,m);a.element.dequeue("tabs")})}:function(g,f){a.lis.removeClass("ui-tabs-selected ui-state-active");f.addClass("ui-tabs-hide");a.element.dequeue("tabs")};
+this.anchors.bind(c.event+".tabs",function(){var g=this,f=d(g).closest("li"),i=a.panels.filter(":not(.ui-tabs-hide)"),l=a.element.find(a._sanitizeSelector(g.hash));if(f.hasClass("ui-tabs-selected")&&!c.collapsible||f.hasClass("ui-state-disabled")||f.hasClass("ui-state-processing")||a.panels.filter(":animated").length||a._trigger("select",null,a._ui(this,l[0]))===false){this.blur();return false}c.selected=a.anchors.index(this);a.abort();if(c.collapsible)if(f.hasClass("ui-tabs-selected")){c.selected=
+-1;c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){s(g,i)}).dequeue("tabs");this.blur();return false}else if(!i.length){c.cookie&&a._cookie(c.selected,c.cookie);a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this));this.blur();return false}c.cookie&&a._cookie(c.selected,c.cookie);if(l.length){i.length&&a.element.queue("tabs",function(){s(g,i)});a.element.queue("tabs",function(){r(g,l)});a.load(a.anchors.index(this))}else throw"jQuery UI Tabs: Mismatching fragment identifier.";
+d.browser.msie&&this.blur()});this.anchors.bind("click.tabs",function(){return false})},_getIndex:function(b){if(typeof b=="string")b=this.anchors.index(this.anchors.filter("[href$="+b+"]"));return b},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var e=
+d.data(this,"href.tabs");if(e)this.href=e;var a=d(this).unbind(".tabs");d.each(["href","load","cache"],function(c,h){a.removeData(h+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){d.data(this,"destroy.tabs")?d(this).remove():d(this).removeClass("ui-state-default ui-corner-top ui-tabs-selected ui-state-active ui-state-hover ui-state-focus ui-state-disabled ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide")});b.cookie&&this._cookie(null,b.cookie);return this},add:function(b,
+e,a){if(a===p)a=this.anchors.length;var c=this,h=this.options;e=d(h.tabTemplate.replace(/#\{href\}/g,b).replace(/#\{label\}/g,e));b=!b.indexOf("#")?b.replace("#",""):this._tabId(d("a",e)[0]);e.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var j=c.element.find("#"+b);j.length||(j=d(h.panelTemplate).attr("id",b).data("destroy.tabs",true));j.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(a>=this.lis.length){e.appendTo(this.list);j.appendTo(this.list[0].parentNode)}else{e.insertBefore(this.lis[a]);
+j.insertBefore(this.panels[a])}h.disabled=d.map(h.disabled,function(k){return k>=a?++k:k});this._tabify();if(this.anchors.length==1){h.selected=0;e.addClass("ui-tabs-selected ui-state-active");j.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){c._trigger("show",null,c._ui(c.anchors[0],c.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[a],this.panels[a]));return this},remove:function(b){b=this._getIndex(b);var e=this.options,a=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();
+if(a.hasClass("ui-tabs-selected")&&this.anchors.length>1)this.select(b+(b+1<this.anchors.length?1:-1));e.disabled=d.map(d.grep(e.disabled,function(h){return h!=b}),function(h){return h>=b?--h:h});this._tabify();this._trigger("remove",null,this._ui(a.find("a")[0],c[0]));return this},enable:function(b){b=this._getIndex(b);var e=this.options;if(d.inArray(b,e.disabled)!=-1){this.lis.eq(b).removeClass("ui-state-disabled");e.disabled=d.grep(e.disabled,function(a){return a!=b});this._trigger("enable",null,
+this._ui(this.anchors[b],this.panels[b]));return this}},disable:function(b){b=this._getIndex(b);var e=this.options;if(b!=e.selected){this.lis.eq(b).addClass("ui-state-disabled");e.disabled.push(b);e.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[b],this.panels[b]))}return this},select:function(b){b=this._getIndex(b);if(b==-1)if(this.options.collapsible&&this.options.selected!=-1)b=this.options.selected;else return this;this.anchors.eq(b).trigger(this.options.event+".tabs");return this},
+load:function(b){b=this._getIndex(b);var e=this,a=this.options,c=this.anchors.eq(b)[0],h=d.data(c,"load.tabs");this.abort();if(!h||this.element.queue("tabs").length!==0&&d.data(c,"cache.tabs"))this.element.dequeue("tabs");else{this.lis.eq(b).addClass("ui-state-processing");if(a.spinner){var j=d("span",c);j.data("label.tabs",j.html()).html(a.spinner)}this.xhr=d.ajax(d.extend({},a.ajaxOptions,{url:h,success:function(k,n){e.element.find(e._sanitizeSelector(c.hash)).html(k);e._cleanup();a.cache&&d.data(c,
+"cache.tabs",true);e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.success(k,n)}catch(m){}},error:function(k,n){e._cleanup();e._trigger("load",null,e._ui(e.anchors[b],e.panels[b]));try{a.ajaxOptions.error(k,n,b,c)}catch(m){}}}));e.element.dequeue("tabs");return this}},abort:function(){this.element.queue([]);this.panels.stop(false,true);this.element.queue("tabs",this.element.queue("tabs").splice(-2,2));if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup();return this},
+url:function(b,e){this.anchors.eq(b).removeData("cache.tabs").data("load.tabs",e);return this},length:function(){return this.anchors.length}});d.extend(d.ui.tabs,{version:"1.8.14"});d.extend(d.ui.tabs.prototype,{rotation:null,rotate:function(b,e){var a=this,c=this.options,h=a._rotate||(a._rotate=function(j){clearTimeout(a.rotation);a.rotation=setTimeout(function(){var k=c.selected;a.select(++k<a.anchors.length?k:0)},b);j&&j.stopPropagation()});e=a._unrotate||(a._unrotate=!e?function(j){j.clientX&&
+a.rotate(null)}:function(){t=c.selected;h()});if(b){this.element.bind("tabsshow",h);this.anchors.bind(c.event+".tabs",e);h()}else{clearTimeout(a.rotation);this.element.unbind("tabsshow",h);this.anchors.unbind(c.event+".tabs",e);delete this._rotate;delete this._unrotate}return this}})})(jQuery);
+;/*
+ * jQuery UI Datepicker 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Datepicker
+ *
+ * Depends:
+ *	jquery.ui.core.js
+ */
+(function(d,C){function M(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._inDialog=this._datepickerShowing=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass=
+"ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su",
+"Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:false,showMonthAfterYear:false,yearSuffix:""};this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,yearRange:"c-10:c+10",showOtherMonths:false,selectOtherMonths:false,showWeek:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",
+minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false,autoSize:false};d.extend(this._defaults,this.regional[""]);this.dpDiv=N(d('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}function N(a){return a.bind("mouseout",function(b){b=
+d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");b.length&&b.removeClass("ui-state-hover ui-datepicker-prev-hover ui-datepicker-next-hover")}).bind("mouseover",function(b){b=d(b.target).closest("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a");if(!(d.datepicker._isDisabledDatepicker(J.inline?a.parent()[0]:J.input[0])||!b.length)){b.parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");b.addClass("ui-state-hover");
+b.hasClass("ui-datepicker-prev")&&b.addClass("ui-datepicker-prev-hover");b.hasClass("ui-datepicker-next")&&b.addClass("ui-datepicker-next-hover")}})}function H(a,b){d.extend(a,b);for(var c in b)if(b[c]==null||b[c]==C)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.14"}});var A=(new Date).getTime(),J;d.extend(M.prototype,{markerClassName:"hasDatepicker",maxRows:4,log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){H(this._defaults,
+a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase();f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0,
+selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:N(d('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}},_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=
+h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&&b.append.remove();if(c){b.append=d('<span class="'+this._appendClass+'">'+c+"</span>");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=
+this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("<img/>").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('<button type="button"></button>').addClass(this._triggerClass).html(f==""?c:d("<img/>").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,
+"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;g<f.length;g++)if(f[g].length>h){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a,c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",
+function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b),true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('<input type="text" id="'+("dp"+this.uuid)+'" style="position: absolute; top: -100px; width: 0px; z-index: -10;"/>');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);
+a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}H(a.settings,e||{});b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",
+this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",
+this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup",this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span"){b=
+b.children("."+this._inlineClass);b.children().removeClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").removeAttr("disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",
+cursor:"default"})}else if(e=="div"||e=="span"){b=b.children("."+this._inlineClass);b.children().addClass("ui-state-disabled");b.find("select.ui-datepicker-month, select.ui-datepicker-year").attr("disabled","disabled")}this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null:f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b<this._disabledInputs.length;b++)if(this._disabledInputs[b]==a)return true;return false},
+_getInst:function(a){try{return d.data(a,"datepicker")}catch(b){throw"Missing instance data for this datepicker";}},_optionDatepicker:function(a,b,c){var e=this._getInst(a);if(arguments.length==2&&typeof b=="string")return b=="defaults"?d.extend({},d.datepicker._defaults):e?b=="all"?d.extend({},e.settings):this._get(e,b):null;var f=b||{};if(typeof b=="string"){f={};f[b]=c}if(e){this._curInst==e&&this._hideDatepicker();var h=this._getDateDatepicker(a,true),i=this._getMinMaxDate(e,"min"),g=this._getMinMaxDate(e,
+"max");H(e.settings,f);if(i!==null&&f.dateFormat!==C&&f.minDate===C)e.settings.minDate=this._formatDate(e,i);if(g!==null&&f.dateFormat!==C&&f.maxDate===C)e.settings.maxDate=this._formatDate(e,g);this._attachments(d(a),e);this._autoSize(e);this._setDate(e,h);this._updateAlternate(e);this._updateDatepicker(e)}},_changeDatepicker:function(a,b,c){this._optionDatepicker(a,b,c)},_refreshDatepicker:function(a){(a=this._getInst(a))&&this._updateDatepicker(a)},_setDateDatepicker:function(a,b){if(a=this._getInst(a)){this._setDate(a,
+b);this._updateDatepicker(a);this._updateAlternate(a)}},_getDateDatepicker:function(a,b){(a=this._getInst(a))&&!a.inline&&this._setDateFromField(a,b);return a?this._getDate(a):null},_doKeyDown:function(a){var b=d.datepicker._getInst(a.target),c=true,e=b.dpDiv.is(".ui-datepicker-rtl");b._keyEvent=true;if(d.datepicker._datepickerShowing)switch(a.keyCode){case 9:d.datepicker._hideDatepicker();c=false;break;case 13:c=d("td."+d.datepicker._dayOverClass+":not(."+d.datepicker._currentClass+")",b.dpDiv);
+c[0]?d.datepicker._selectDay(a.target,b.selectedMonth,b.selectedYear,c[0]):d.datepicker._hideDatepicker();return false;case 27:d.datepicker._hideDatepicker();break;case 33:d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 34:d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 35:if(a.ctrlKey||a.metaKey)d.datepicker._clearDate(a.target);
+c=a.ctrlKey||a.metaKey;break;case 36:if(a.ctrlKey||a.metaKey)d.datepicker._gotoToday(a.target);c=a.ctrlKey||a.metaKey;break;case 37:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,e?+1:-1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?-d.datepicker._get(b,"stepBigMonths"):-d.datepicker._get(b,"stepMonths"),"M");break;case 38:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,-7,"D");c=a.ctrlKey||a.metaKey;break;case 39:if(a.ctrlKey||
+a.metaKey)d.datepicker._adjustDate(a.target,e?-1:+1,"D");c=a.ctrlKey||a.metaKey;if(a.originalEvent.altKey)d.datepicker._adjustDate(a.target,a.ctrlKey?+d.datepicker._get(b,"stepBigMonths"):+d.datepicker._get(b,"stepMonths"),"M");break;case 40:if(a.ctrlKey||a.metaKey)d.datepicker._adjustDate(a.target,+7,"D");c=a.ctrlKey||a.metaKey;break;default:c=false}else if(a.keyCode==36&&a.ctrlKey)d.datepicker._showDatepicker(this);else c=false;if(c){a.preventDefault();a.stopPropagation()}},_doKeyPress:function(a){var b=
+d.datepicker._getInst(a.target);if(d.datepicker._get(b,"constrainInput")){b=d.datepicker._possibleChars(d.datepicker._get(b,"dateFormat"));var c=String.fromCharCode(a.charCode==C?a.keyCode:a.charCode);return a.ctrlKey||a.metaKey||c<" "||!b||b.indexOf(c)>-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);
+d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true},_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);if(d.datepicker._curInst&&d.datepicker._curInst!=b){d.datepicker._datepickerShowing&&d.datepicker._triggerOnClose(d.datepicker._curInst);d.datepicker._curInst.dpDiv.stop(true,true)}var c=
+d.datepicker._get(b,"beforeShow");H(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos=d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c=
+{left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b,c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){var i=b.dpDiv.find("iframe.ui-datepicker-cover");
+if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.datepicker._datepickerShowing=true;d.effects&&d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){this.maxRows=4;var b=d.datepicker._getBorders(a.dpDiv);
+J=a;a.dpDiv.empty().append(this._generateHTML(a));var c=a.dpDiv.find("iframe.ui-datepicker-cover");c.length&&c.css({left:-b[0],top:-b[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("."+this._dayOverClass+" a").mouseover();b=this._getNumberOfMonths(a);c=b[1];a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");c>1&&a.dpDiv.addClass("ui-datepicker-multi-"+c).css("width",17*c+"em");a.dpDiv[(b[0]!=1||b[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");
+a.dpDiv[(this._get(a,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input[0]!=document.activeElement&&a.input.focus();if(a.yearshtml){var e=a.yearshtml;setTimeout(function(){e===a.yearshtml&&a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);e=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||
+c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]},_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+
+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e-g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1||d.expr.filters.hidden(a));)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_triggerOnClose:function(a){var b=this._get(a,"onClose");if(b)b.apply(a.input?a.input[0]:null,[a.input?a.input.val():"",a])},_hideDatepicker:function(a){var b=
+this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]?b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();d.datepicker._triggerOnClose(b);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",
+left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&
+d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"):0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=
+b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear=false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=
+!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay=d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);
+a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a);else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));
+d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b=a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%
+100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort,g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=B+1<a.length&&a.charAt(B+1)==p)&&B++;return p},m=function(p){var D=o(p);p=new RegExp("^\\d{1,"+(p=="@"?14:p=="!"?20:p=="y"&&D?4:p=="o"?3:2)+"}");p=b.substring(q).match(p);if(!p)throw"Missing number at position "+q;q+=
+p[0].length;return parseInt(p[0],10)},n=function(p,D,K){p=d.map(o(p)?K:D,function(w,x){return[[x,w]]}).sort(function(w,x){return-(w[1].length-x[1].length)});var E=-1;d.each(p,function(w,x){w=x[1];if(b.substr(q,w.length).toLowerCase()==w.toLowerCase()){E=x[0];q+=w.length;return false}});if(E!=-1)return E+1;else throw"Unknown name at position "+q;},s=function(){if(b.charAt(q)!=a.charAt(B))throw"Unexpected literal at position "+q;q++},q=0,B=0;B<a.length;B++)if(k)if(a.charAt(B)=="'"&&!o("'"))k=false;
+else s();else switch(a.charAt(B)){case "d":l=m("d");break;case "D":n("D",f,h);break;case "o":u=m("o");break;case "m":j=m("m");break;case "M":j=n("M",i,g);break;case "y":c=m("y");break;case "@":var v=new Date(m("@"));c=v.getFullYear();j=v.getMonth()+1;l=v.getDate();break;case "!":v=new Date((m("!")-this._ticksTo1970)/1E4);c=v.getFullYear();j=v.getMonth()+1;l=v.getDate();break;case "'":if(o("'"))s();else k=true;break;default:s()}if(q<b.length)throw"Extra/unparsed characters found in date: "+b.substring(q);
+if(c==-1)c=(new Date).getFullYear();else if(c<100)c+=(new Date).getFullYear()-(new Date).getFullYear()%100+(c<=e?0:-100);if(u>-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}v=this._daylightSavingAdjust(new Date(c,j-1,l));if(v.getFullYear()!=c||v.getMonth()+1!=j||v.getDate()!=l)throw"Invalid date";return v},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",
+TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=k+1<a.length&&a.charAt(k+1)==o)&&k++;return o},g=function(o,m,n){m=""+m;if(i(o))for(;m.length<
+n;)m="0"+m;return m},j=function(o,m,n,s){return i(o)?s[m]:n[m]},l="",u=false;if(b)for(var k=0;k<a.length;k++)if(u)if(a.charAt(k)=="'"&&!i("'"))u=false;else l+=a.charAt(k);else switch(a.charAt(k)){case "d":l+=g("d",b.getDate(),2);break;case "D":l+=j("D",b.getDay(),e,f);break;case "o":l+=g("o",Math.round(((new Date(b.getFullYear(),b.getMonth(),b.getDate())).getTime()-(new Date(b.getFullYear(),0,0)).getTime())/864E5),3);break;case "m":l+=g("m",b.getMonth()+1,2);break;case "M":l+=j("M",b.getMonth(),h,
+c);break;case "y":l+=i("y")?b.getFullYear():(b.getYear()%100<10?"0":"")+b.getYear()%100;break;case "@":l+=b.getTime();break;case "!":l+=b.getTime()*1E4+this._ticksTo1970;break;case "'":if(i("'"))l+="'";else u=true;break;default:l+=a.charAt(k)}return l},_possibleChars:function(a){for(var b="",c=false,e=function(h){(h=f+1<a.length&&a.charAt(f+1)==h)&&f++;return h},f=0;f<a.length;f++)if(c)if(a.charAt(f)=="'"&&!e("'"))c=false;else b+=a.charAt(f);else switch(a.charAt(f)){case "d":case "m":case "y":case "@":b+=
+"0123456789";break;case "D":case "M":return null;case "'":if(e("'"))b+="'";else c=true;break;default:b+=a.charAt(f)}return b},_get:function(a,b){return a.settings[b]!==C?a.settings[b]:this._defaults[b]},_setDateFromField:function(a,b){if(a.input.val()!=a.lastVal){var c=this._get(a,"dateFormat"),e=a.lastVal=a.input?a.input.val():null,f,h;f=h=this._getDefaultDate(a);var i=this._getFormatConfig(a);try{f=this.parseDate(c,e,i)||h}catch(g){this.log(g);e=b?"":e}a.selectedDay=f.getDate();a.drawMonth=a.selectedMonth=
+f.getMonth();a.drawYear=a.selectedYear=f.getFullYear();a.currentDay=e?f.getDate():0;a.currentMonth=e?f.getMonth():0;a.currentYear=e?f.getFullYear():0;this._adjustInstDate(a)}},_getDefaultDate:function(a){return this._restrictMinMax(a,this._determineDate(a,this._get(a,"defaultDate"),new Date))},_determineDate:function(a,b,c){var e=function(h){var i=new Date;i.setDate(i.getDate()+h);return i},f=function(h){try{return d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),h,d.datepicker._getFormatConfig(a))}catch(i){}var g=
+(h.toLowerCase().match(/^c/)?d.datepicker._getDate(a):null)||new Date,j=g.getFullYear(),l=g.getMonth();g=g.getDate();for(var u=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,k=u.exec(h);k;){switch(k[2]||"d"){case "d":case "D":g+=parseInt(k[1],10);break;case "w":case "W":g+=parseInt(k[1],10)*7;break;case "m":case "M":l+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break;case "y":case "Y":j+=parseInt(k[1],10);g=Math.min(g,d.datepicker._getDaysInMonth(j,l));break}k=u.exec(h)}return new Date(j,
+l,g)};if(b=(b=b==null||b===""?c:typeof b=="string"?f(b):typeof b=="number"?isNaN(b)?c:e(b):new Date(b.getTime()))&&b.toString()=="Invalid Date"?c:b){b.setHours(0);b.setMinutes(0);b.setSeconds(0);b.setMilliseconds(0)}return this._daylightSavingAdjust(b)},_daylightSavingAdjust:function(a){if(!a)return null;a.setHours(a.getHours()>12?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay=
+a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(),
+b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n=
+this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&n<k?k:n;this._daylightSavingAdjust(new Date(m,g,1))>n;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery_'+A+".datepicker._adjustDate('#"+a.id+"', -"+j+", 'M');\" title=\""+n+'"><span class="ui-icon ui-icon-circle-triangle-'+
+(c?"e":"w")+'">'+n+"</span></a>":f?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+n+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"e":"w")+'">'+n+"</span></a>";var s=this._get(a,"nextText");s=!h?s:this.formatDate(s,this._daylightSavingAdjust(new Date(m,g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery_'+A+".datepicker._adjustDate('#"+a.id+"', +"+j+", 'M');\" title=\""+s+'"><span class="ui-icon ui-icon-circle-triangle-'+
+(c?"w":"e")+'">'+s+"</span></a>":f?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+s+'"><span class="ui-icon ui-icon-circle-triangle-'+(c?"w":"e")+'">'+s+"</span></a>";j=this._get(a,"currentText");s=this._get(a,"gotoCurrent")&&a.currentDay?u:b;j=!h?j:this.formatDate(j,s,this._getFormatConfig(a));h=!a.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery_'+A+'.datepicker._hideDatepicker();">'+this._get(a,
+"closeText")+"</button>":"";e=e?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(c?h:"")+(this._isInRange(a,s)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery_'+A+".datepicker._gotoToday('#"+a.id+"');\">"+j+"</button>":"")+(c?"":h)+"</div>":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");s=this._get(a,"dayNames");this._get(a,"dayNamesShort");var q=this._get(a,"dayNamesMin"),B=
+this._get(a,"monthNames"),v=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),D=this._get(a,"showOtherMonths"),K=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var E=this._getDefaultDate(a),w="",x=0;x<i[0];x++){var O="";this.maxRows=4;for(var G=0;G<i[1];G++){var P=this._daylightSavingAdjust(new Date(m,g,a.selectedDay)),t=" ui-corner-all",y="";if(l){y+='<div class="ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":
+"left");break;case i[1]-1:y+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:y+=" ui-datepicker-group-middle";t="";break}y+='">'}y+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+t+'">'+(/all|left/.test(t)&&x==0?c?f:n:"")+(/all|right/.test(t)&&x==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,x>0||G>0,B,v)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var z=j?'<th class="ui-datepicker-week-col">'+this._get(a,"weekHeader")+"</th>":
+"";for(t=0;t<7;t++){var r=(t+h)%7;z+="<th"+((t+h+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+s[r]+'">'+q[r]+"</span></th>"}y+=z+"</tr></thead><tbody>";z=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,z);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;z=Math.ceil((t+z)/7);this.maxRows=z=l?this.maxRows>z?this.maxRows:z:z;r=this._daylightSavingAdjust(new Date(m,g,1-t));for(var Q=0;Q<z;Q++){y+="<tr>";var R=!j?"":'<td class="ui-datepicker-week-col">'+
+this._get(a,"calculateWeek")(r)+"</td>";for(t=0;t<7;t++){var I=p?p.apply(a.input?a.input[0]:null,[r]):[true,""],F=r.getMonth()!=g,L=F&&!K||!I[0]||k&&r<k||o&&r>o;R+='<td class="'+((t+h+6)%7>=5?" ui-datepicker-week-end":"")+(F?" ui-datepicker-other-month":"")+(r.getTime()==P.getTime()&&g==a.selectedMonth&&a._keyEvent||E.getTime()==r.getTime()&&E.getTime()==P.getTime()?" "+this._dayOverClass:"")+(L?" "+this._unselectableClass+" ui-state-disabled":"")+(F&&!D?"":" "+I[1]+(r.getTime()==u.getTime()?" "+
+this._currentClass:"")+(r.getTime()==b.getTime()?" ui-datepicker-today":""))+'"'+((!F||D)&&I[2]?' title="'+I[2]+'"':"")+(L?"":' onclick="DP_jQuery_'+A+".datepicker._selectDay('#"+a.id+"',"+r.getMonth()+","+r.getFullYear()+', this);return false;"')+">"+(F&&!D?"&#xa0;":L?'<span class="ui-state-default">'+r.getDate()+"</span>":'<a class="ui-state-default'+(r.getTime()==b.getTime()?" ui-state-highlight":"")+(r.getTime()==u.getTime()?" ui-state-active":"")+(F?" ui-priority-secondary":"")+'" href="#">'+
+r.getDate()+"</a>")+"</td>";r.setDate(r.getDate()+1);r=this._daylightSavingAdjust(r)}y+=R+"</tr>"}g++;if(g>11){g=0;m++}y+="</tbody></table>"+(l?"</div>"+(i[0]>0&&G==i[1]-1?'<div class="ui-datepicker-row-break"></div>':""):"");O+=y}w+=O}w+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':"");a._keyEvent=false;return w},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),
+l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='<div class="ui-datepicker-title">',o="";if(h||!j)o+='<span class="ui-datepicker-month">'+i[b]+"</span>";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='<select class="ui-datepicker-month" onchange="DP_jQuery_'+A+".datepicker._selectMonthYear('#"+a.id+"', this, 'M');\" onclick=\"DP_jQuery_"+A+".datepicker._clickMonthYear('#"+a.id+"');\">";for(var n=0;n<12;n++)if((!i||n>=e.getMonth())&&(!m||n<=f.getMonth()))o+='<option value="'+
+n+'"'+(n==b?' selected="selected"':"")+">"+g[n]+"</option>";o+="</select>"}u||(k+=o+(h||!(j&&l)?"&#xa0;":""));if(!a.yearshtml){a.yearshtml="";if(h||!l)k+='<span class="ui-datepicker-year">'+c+"</span>";else{g=this._get(a,"yearRange").split(":");var s=(new Date).getFullYear();i=function(q){q=q.match(/c[+-].*/)?c+parseInt(q.substring(1),10):q.match(/[+-].*/)?s+parseInt(q,10):parseInt(q,10);return isNaN(q)?s:q};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):
+g;for(a.yearshtml+='<select class="ui-datepicker-year" onchange="DP_jQuery_'+A+".datepicker._selectMonthYear('#"+a.id+"', this, 'Y');\" onclick=\"DP_jQuery_"+A+".datepicker._clickMonthYear('#"+a.id+"');\">";b<=g;b++)a.yearshtml+='<option value="'+b+'"'+(b==c?' selected="selected"':"")+">"+b+"</option>";a.yearshtml+="</select>";k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?"&#xa0;":"")+o;k+="</div>";return k},_adjustInstDate:function(a,b,c){var e=a.drawYear+(c==
+"Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&b<c?c:b;return b=a&&b>a?a:b},_notifyChange:function(a){var b=this._get(a,"onChangeMonthYear");
+if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-this._daylightSavingAdjust(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a);
+c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a,
+"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker=
+function(a){if(!this.length)return this;if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,
+[this[0]].concat(b));return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new M;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.14";window["DP_jQuery_"+A]=d})(jQuery);
+;/*
+ * jQuery UI Progressbar 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Progressbar
+ *
+ * Depends:
+ *   jquery.ui.core.js
+ *   jquery.ui.widget.js
+ */
+(function(b,d){b.widget("ui.progressbar",{options:{value:0,max:100},min:0,_create:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this.min,"aria-valuemax":this.options.max,"aria-valuenow":this._value()});this.valueDiv=b("<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>").appendTo(this.element);this.oldValue=this._value();this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow");
+this.valueDiv.remove();b.Widget.prototype.destroy.apply(this,arguments)},value:function(a){if(a===d)return this._value();this._setOption("value",a);return this},_setOption:function(a,c){if(a==="value"){this.options.value=c;this._refreshValue();this._value()===this.options.max&&this._trigger("complete")}b.Widget.prototype._setOption.apply(this,arguments)},_value:function(){var a=this.options.value;if(typeof a!=="number")a=0;return Math.min(this.options.max,Math.max(this.min,a))},_percentage:function(){return 100*
+this._value()/this.options.max},_refreshValue:function(){var a=this.value(),c=this._percentage();if(this.oldValue!==a){this.oldValue=a;this._trigger("change")}this.valueDiv.toggle(a>this.min).toggleClass("ui-corner-right",a===this.options.max).width(c.toFixed(0)+"%");this.element.attr("aria-valuenow",a)}});b.extend(b.ui.progressbar,{version:"1.8.14"})})(jQuery);
+;/*
+ * jQuery UI Effects 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/
+ */
+jQuery.effects||function(f,j){function m(c){var a;if(c&&c.constructor==Array&&c.length==3)return c;if(a=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(c))return[parseInt(a[1],10),parseInt(a[2],10),parseInt(a[3],10)];if(a=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(c))return[parseFloat(a[1])*2.55,parseFloat(a[2])*2.55,parseFloat(a[3])*2.55];if(a=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(c))return[parseInt(a[1],
+16),parseInt(a[2],16),parseInt(a[3],16)];if(a=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(c))return[parseInt(a[1]+a[1],16),parseInt(a[2]+a[2],16),parseInt(a[3]+a[3],16)];if(/rgba\(0, 0, 0, 0\)/.exec(c))return n.transparent;return n[f.trim(c).toLowerCase()]}function s(c,a){var b;do{b=f.curCSS(c,a);if(b!=""&&b!="transparent"||f.nodeName(c,"body"))break;a="backgroundColor"}while(c=c.parentNode);return m(b)}function o(){var c=document.defaultView?document.defaultView.getComputedStyle(this,null):this.currentStyle,
+a={},b,d;if(c&&c.length&&c[0]&&c[c[0]])for(var e=c.length;e--;){b=c[e];if(typeof c[b]=="string"){d=b.replace(/\-(\w)/g,function(g,h){return h.toUpperCase()});a[d]=c[b]}}else for(b in c)if(typeof c[b]==="string")a[b]=c[b];return a}function p(c){var a,b;for(a in c){b=c[a];if(b==null||f.isFunction(b)||a in t||/scrollbar/.test(a)||!/color/i.test(a)&&isNaN(parseFloat(b)))delete c[a]}return c}function u(c,a){var b={_:0},d;for(d in a)if(c[d]!=a[d])b[d]=a[d];return b}function k(c,a,b,d){if(typeof c=="object"){d=
+a;b=null;a=c;c=a.effect}if(f.isFunction(a)){d=a;b=null;a={}}if(typeof a=="number"||f.fx.speeds[a]){d=b;b=a;a={}}if(f.isFunction(b)){d=b;b=null}a=a||{};b=b||a.duration;b=f.fx.off?0:typeof b=="number"?b:b in f.fx.speeds?f.fx.speeds[b]:f.fx.speeds._default;d=d||a.complete;return[c,a,b,d]}function l(c){if(!c||typeof c==="number"||f.fx.speeds[c])return true;if(typeof c==="string"&&!f.effects[c])return true;return false}f.effects={};f.each(["backgroundColor","borderBottomColor","borderLeftColor","borderRightColor",
+"borderTopColor","borderColor","color","outlineColor"],function(c,a){f.fx.step[a]=function(b){if(!b.colorInit){b.start=s(b.elem,a);b.end=m(b.end);b.colorInit=true}b.elem.style[a]="rgb("+Math.max(Math.min(parseInt(b.pos*(b.end[0]-b.start[0])+b.start[0],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[1]-b.start[1])+b.start[1],10),255),0)+","+Math.max(Math.min(parseInt(b.pos*(b.end[2]-b.start[2])+b.start[2],10),255),0)+")"}});var n={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,
+0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,
+211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0],transparent:[255,255,255]},q=["add","remove","toggle"],t={border:1,borderBottom:1,borderColor:1,borderLeft:1,borderRight:1,borderTop:1,borderWidth:1,margin:1,padding:1};f.effects.animateClass=function(c,a,b,
+d){if(f.isFunction(b)){d=b;b=null}return this.queue(function(){var e=f(this),g=e.attr("style")||" ",h=p(o.call(this)),r,v=e.attr("class");f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});r=p(o.call(this));e.attr("class",v);e.animate(u(h,r),{queue:false,duration:a,easing:b,complete:function(){f.each(q,function(w,i){c[i]&&e[i+"Class"](c[i])});if(typeof e.attr("style")=="object"){e.attr("style").cssText="";e.attr("style").cssText=g}else e.attr("style",g);d&&d.apply(this,arguments);f.dequeue(this)}})})};
+f.fn.extend({_addClass:f.fn.addClass,addClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{add:c},a,b,d]):this._addClass(c)},_removeClass:f.fn.removeClass,removeClass:function(c,a,b,d){return a?f.effects.animateClass.apply(this,[{remove:c},a,b,d]):this._removeClass(c)},_toggleClass:f.fn.toggleClass,toggleClass:function(c,a,b,d,e){return typeof a=="boolean"||a===j?b?f.effects.animateClass.apply(this,[a?{add:c}:{remove:c},b,d,e]):this._toggleClass(c,a):f.effects.animateClass.apply(this,
+[{toggle:c},a,b,d])},switchClass:function(c,a,b,d,e){return f.effects.animateClass.apply(this,[{add:a,remove:c},b,d,e])}});f.extend(f.effects,{version:"1.8.14",save:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.data("ec.storage."+a[b],c[0].style[a[b]])},restore:function(c,a){for(var b=0;b<a.length;b++)a[b]!==null&&c.css(a[b],c.data("ec.storage."+a[b]))},setMode:function(c,a){if(a=="toggle")a=c.is(":hidden")?"show":"hide";return a},getBaseline:function(c,a){var b;switch(c[0]){case "top":b=
+0;break;case "middle":b=0.5;break;case "bottom":b=1;break;default:b=c[0]/a.height}switch(c[1]){case "left":c=0;break;case "center":c=0.5;break;case "right":c=1;break;default:c=c[1]/a.width}return{x:c,y:b}},createWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent();var a={width:c.outerWidth(true),height:c.outerHeight(true),"float":c.css("float")},b=f("<div></div>").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0});
+c.wrap(b);b=c.parent();if(c.css("position")=="static"){b.css({position:"relative"});c.css({position:"relative"})}else{f.extend(a,{position:c.css("position"),zIndex:c.css("z-index")});f.each(["top","left","bottom","right"],function(d,e){a[e]=c.css(e);if(isNaN(parseInt(a[e],10)))a[e]="auto"});c.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})}return b.css(a).show()},removeWrapper:function(c){if(c.parent().is(".ui-effects-wrapper"))return c.parent().replaceWith(c);return c},setTransition:function(c,
+a,b,d){d=d||{};f.each(a,function(e,g){unit=c.cssUnit(g);if(unit[0]>0)d[g]=unit[0]*b+unit[1]});return d}});f.fn.extend({effect:function(c){var a=k.apply(this,arguments),b={options:a[1],duration:a[2],callback:a[3]};a=b.options.mode;var d=f.effects[c];if(f.fx.off||!d)return a?this[a](b.duration,b.callback):this.each(function(){b.callback&&b.callback.call(this)});return d.call(this,b)},_show:f.fn.show,show:function(c){if(l(c))return this._show.apply(this,arguments);else{var a=k.apply(this,arguments);
+a[1].mode="show";return this.effect.apply(this,a)}},_hide:f.fn.hide,hide:function(c){if(l(c))return this._hide.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="hide";return this.effect.apply(this,a)}},__toggle:f.fn.toggle,toggle:function(c){if(l(c)||typeof c==="boolean"||f.isFunction(c))return this.__toggle.apply(this,arguments);else{var a=k.apply(this,arguments);a[1].mode="toggle";return this.effect.apply(this,a)}},cssUnit:function(c){var a=this.css(c),b=[];f.each(["em","px","%",
+"pt"],function(d,e){if(a.indexOf(e)>0)b=[parseFloat(a),e]});return b}});f.easing.jswing=f.easing.swing;f.extend(f.easing,{def:"easeOutQuad",swing:function(c,a,b,d,e){return f.easing[f.easing.def](c,a,b,d,e)},easeInQuad:function(c,a,b,d,e){return d*(a/=e)*a+b},easeOutQuad:function(c,a,b,d,e){return-d*(a/=e)*(a-2)+b},easeInOutQuad:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a+b;return-d/2*(--a*(a-2)-1)+b},easeInCubic:function(c,a,b,d,e){return d*(a/=e)*a*a+b},easeOutCubic:function(c,a,b,d,e){return d*
+((a=a/e-1)*a*a+1)+b},easeInOutCubic:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a+b;return d/2*((a-=2)*a*a+2)+b},easeInQuart:function(c,a,b,d,e){return d*(a/=e)*a*a*a+b},easeOutQuart:function(c,a,b,d,e){return-d*((a=a/e-1)*a*a*a-1)+b},easeInOutQuart:function(c,a,b,d,e){if((a/=e/2)<1)return d/2*a*a*a*a+b;return-d/2*((a-=2)*a*a*a-2)+b},easeInQuint:function(c,a,b,d,e){return d*(a/=e)*a*a*a*a+b},easeOutQuint:function(c,a,b,d,e){return d*((a=a/e-1)*a*a*a*a+1)+b},easeInOutQuint:function(c,a,b,d,e){if((a/=
+e/2)<1)return d/2*a*a*a*a*a+b;return d/2*((a-=2)*a*a*a*a+2)+b},easeInSine:function(c,a,b,d,e){return-d*Math.cos(a/e*(Math.PI/2))+d+b},easeOutSine:function(c,a,b,d,e){return d*Math.sin(a/e*(Math.PI/2))+b},easeInOutSine:function(c,a,b,d,e){return-d/2*(Math.cos(Math.PI*a/e)-1)+b},easeInExpo:function(c,a,b,d,e){return a==0?b:d*Math.pow(2,10*(a/e-1))+b},easeOutExpo:function(c,a,b,d,e){return a==e?b+d:d*(-Math.pow(2,-10*a/e)+1)+b},easeInOutExpo:function(c,a,b,d,e){if(a==0)return b;if(a==e)return b+d;if((a/=
+e/2)<1)return d/2*Math.pow(2,10*(a-1))+b;return d/2*(-Math.pow(2,-10*--a)+2)+b},easeInCirc:function(c,a,b,d,e){return-d*(Math.sqrt(1-(a/=e)*a)-1)+b},easeOutCirc:function(c,a,b,d,e){return d*Math.sqrt(1-(a=a/e-1)*a)+b},easeInOutCirc:function(c,a,b,d,e){if((a/=e/2)<1)return-d/2*(Math.sqrt(1-a*a)-1)+b;return d/2*(Math.sqrt(1-(a-=2)*a)+1)+b},easeInElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/
+h);return-(h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g))+b},easeOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e)==1)return b+d;g||(g=e*0.3);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);return h*Math.pow(2,-10*a)*Math.sin((a*e-c)*2*Math.PI/g)+d+b},easeInOutElastic:function(c,a,b,d,e){c=1.70158;var g=0,h=d;if(a==0)return b;if((a/=e/2)==2)return b+d;g||(g=e*0.3*1.5);if(h<Math.abs(d)){h=d;c=g/4}else c=g/(2*Math.PI)*Math.asin(d/h);if(a<1)return-0.5*
+h*Math.pow(2,10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)+b;return h*Math.pow(2,-10*(a-=1))*Math.sin((a*e-c)*2*Math.PI/g)*0.5+d+b},easeInBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*(a/=e)*a*((g+1)*a-g)+b},easeOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;return d*((a=a/e-1)*a*((g+1)*a+g)+1)+b},easeInOutBack:function(c,a,b,d,e,g){if(g==j)g=1.70158;if((a/=e/2)<1)return d/2*a*a*(((g*=1.525)+1)*a-g)+b;return d/2*((a-=2)*a*(((g*=1.525)+1)*a+g)+2)+b},easeInBounce:function(c,a,b,d,e){return d-f.easing.easeOutBounce(c,
+e-a,0,d,e)+b},easeOutBounce:function(c,a,b,d,e){return(a/=e)<1/2.75?d*7.5625*a*a+b:a<2/2.75?d*(7.5625*(a-=1.5/2.75)*a+0.75)+b:a<2.5/2.75?d*(7.5625*(a-=2.25/2.75)*a+0.9375)+b:d*(7.5625*(a-=2.625/2.75)*a+0.984375)+b},easeInOutBounce:function(c,a,b,d,e){if(a<e/2)return f.easing.easeInBounce(c,a*2,0,d,e)*0.5+b;return f.easing.easeOutBounce(c,a*2-e,0,d,e)*0.5+d*0.5+b}})}(jQuery);
+;/*
+ * jQuery UI Effects Blind 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Blind
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.blind=function(c){return this.queue(function(){var a=b(this),g=["position","top","bottom","left","right"],f=b.effects.setMode(a,c.options.mode||"hide"),d=c.options.direction||"vertical";b.effects.save(a,g);a.show();var e=b.effects.createWrapper(a).css({overflow:"hidden"}),h=d=="vertical"?"height":"width";d=d=="vertical"?e.height():e.width();f=="show"&&e.css(h,0);var i={};i[h]=f=="show"?d:0;e.animate(i,c.duration,c.options.easing,function(){f=="hide"&&a.hide();b.effects.restore(a,
+g);b.effects.removeWrapper(a);c.callback&&c.callback.apply(a[0],arguments);a.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Bounce 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Bounce
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(e){e.effects.bounce=function(b){return this.queue(function(){var a=e(this),l=["position","top","bottom","left","right"],h=e.effects.setMode(a,b.options.mode||"effect"),d=b.options.direction||"up",c=b.options.distance||20,m=b.options.times||5,i=b.duration||250;/show|hide/.test(h)&&l.push("opacity");e.effects.save(a,l);a.show();e.effects.createWrapper(a);var f=d=="up"||d=="down"?"top":"left";d=d=="up"||d=="left"?"pos":"neg";c=b.options.distance||(f=="top"?a.outerHeight({margin:true})/3:a.outerWidth({margin:true})/
+3);if(h=="show")a.css("opacity",0).css(f,d=="pos"?-c:c);if(h=="hide")c/=m*2;h!="hide"&&m--;if(h=="show"){var g={opacity:1};g[f]=(d=="pos"?"+=":"-=")+c;a.animate(g,i/2,b.options.easing);c/=2;m--}for(g=0;g<m;g++){var j={},k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing);c=h=="hide"?c*2:c/2}if(h=="hide"){g={opacity:0};g[f]=(d=="pos"?"-=":"+=")+c;a.animate(g,i/2,b.options.easing,function(){a.hide();e.effects.restore(a,l);e.effects.removeWrapper(a);
+b.callback&&b.callback.apply(this,arguments)})}else{j={};k={};j[f]=(d=="pos"?"-=":"+=")+c;k[f]=(d=="pos"?"+=":"-=")+c;a.animate(j,i/2,b.options.easing).animate(k,i/2,b.options.easing,function(){e.effects.restore(a,l);e.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments)})}a.queue("fx",function(){a.dequeue()});a.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Clip 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Clip
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.clip=function(e){return this.queue(function(){var a=b(this),i=["position","top","bottom","left","right","height","width"],f=b.effects.setMode(a,e.options.mode||"hide"),c=e.options.direction||"vertical";b.effects.save(a,i);a.show();var d=b.effects.createWrapper(a).css({overflow:"hidden"});d=a[0].tagName=="IMG"?d:a;var g={size:c=="vertical"?"height":"width",position:c=="vertical"?"top":"left"};c=c=="vertical"?d.height():d.width();if(f=="show"){d.css(g.size,0);d.css(g.position,
+c/2)}var h={};h[g.size]=f=="show"?c:0;h[g.position]=f=="show"?0:c/2;d.animate(h,{queue:false,duration:e.duration,easing:e.options.easing,complete:function(){f=="hide"&&a.hide();b.effects.restore(a,i);b.effects.removeWrapper(a);e.callback&&e.callback.apply(a[0],arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Drop 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Drop
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.drop=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right","opacity"],e=c.effects.setMode(a,d.options.mode||"hide"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a);var f=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var g=d.options.distance||(f=="top"?a.outerHeight({margin:true})/2:a.outerWidth({margin:true})/2);if(e=="show")a.css("opacity",0).css(f,b=="pos"?-g:g);var i={opacity:e==
+"show"?1:0};i[f]=(e=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+g;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){e=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Explode 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Explode
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(j){j.effects.explode=function(a){return this.queue(function(){var c=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3,d=a.options.pieces?Math.round(Math.sqrt(a.options.pieces)):3;a.options.mode=a.options.mode=="toggle"?j(this).is(":visible")?"hide":"show":a.options.mode;var b=j(this).show().css("visibility","hidden"),g=b.offset();g.top-=parseInt(b.css("marginTop"),10)||0;g.left-=parseInt(b.css("marginLeft"),10)||0;for(var h=b.outerWidth(true),i=b.outerHeight(true),e=0;e<c;e++)for(var f=
+0;f<d;f++)b.clone().appendTo("body").wrap("<div></div>").css({position:"absolute",visibility:"visible",left:-f*(h/d),top:-e*(i/c)}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:h/d,height:i/c,left:g.left+f*(h/d)+(a.options.mode=="show"?(f-Math.floor(d/2))*(h/d):0),top:g.top+e*(i/c)+(a.options.mode=="show"?(e-Math.floor(c/2))*(i/c):0),opacity:a.options.mode=="show"?0:1}).animate({left:g.left+f*(h/d)+(a.options.mode=="show"?0:(f-Math.floor(d/2))*(h/d)),top:g.top+
+e*(i/c)+(a.options.mode=="show"?0:(e-Math.floor(c/2))*(i/c)),opacity:a.options.mode=="show"?1:0},a.duration||500);setTimeout(function(){a.options.mode=="show"?b.css({visibility:"visible"}):b.css({visibility:"visible"}).hide();a.callback&&a.callback.apply(b[0]);b.dequeue();j("div.ui-effects-explode").remove()},a.duration||500)})}})(jQuery);
+;/*
+ * jQuery UI Effects Fade 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fade
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.fade=function(a){return this.queue(function(){var c=b(this),d=b.effects.setMode(c,a.options.mode||"hide");c.animate({opacity:d},{queue:false,duration:a.duration,easing:a.options.easing,complete:function(){a.callback&&a.callback.apply(this,arguments);c.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Fold 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Fold
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.fold=function(a){return this.queue(function(){var b=c(this),j=["position","top","bottom","left","right"],d=c.effects.setMode(b,a.options.mode||"hide"),g=a.options.size||15,h=!!a.options.horizFirst,k=a.duration?a.duration/2:c.fx.speeds._default/2;c.effects.save(b,j);b.show();var e=c.effects.createWrapper(b).css({overflow:"hidden"}),f=d=="show"!=h,l=f?["width","height"]:["height","width"];f=f?[e.width(),e.height()]:[e.height(),e.width()];var i=/([0-9]+)%/.exec(g);if(i)g=parseInt(i[1],
+10)/100*f[d=="hide"?0:1];if(d=="show")e.css(h?{height:0,width:g}:{height:g,width:0});h={};i={};h[l[0]]=d=="show"?f[0]:g;i[l[1]]=d=="show"?f[1]:0;e.animate(h,k,a.options.easing).animate(i,k,a.options.easing,function(){d=="hide"&&b.hide();c.effects.restore(b,j);c.effects.removeWrapper(b);a.callback&&a.callback.apply(b[0],arguments);b.dequeue()})})}})(jQuery);
+;/*
+ * jQuery UI Effects Highlight 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Highlight
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(b){b.effects.highlight=function(c){return this.queue(function(){var a=b(this),e=["backgroundImage","backgroundColor","opacity"],d=b.effects.setMode(a,c.options.mode||"show"),f={backgroundColor:a.css("backgroundColor")};if(d=="hide")f.opacity=0;b.effects.save(a,e);a.show().css({backgroundImage:"none",backgroundColor:c.options.color||"#ffff99"}).animate(f,{queue:false,duration:c.duration,easing:c.options.easing,complete:function(){d=="hide"&&a.hide();b.effects.restore(a,e);d=="show"&&!b.support.opacity&&
+this.style.removeAttribute("filter");c.callback&&c.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Pulsate 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Pulsate
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(d){d.effects.pulsate=function(a){return this.queue(function(){var b=d(this),c=d.effects.setMode(b,a.options.mode||"show");times=(a.options.times||5)*2-1;duration=a.duration?a.duration/2:d.fx.speeds._default/2;isVisible=b.is(":visible");animateTo=0;if(!isVisible){b.css("opacity",0).show();animateTo=1}if(c=="hide"&&isVisible||c=="show"&&!isVisible)times--;for(c=0;c<times;c++){b.animate({opacity:animateTo},duration,a.options.easing);animateTo=(animateTo+1)%2}b.animate({opacity:animateTo},duration,
+a.options.easing,function(){animateTo==0&&b.hide();a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()}).dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Scale 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Scale
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.puff=function(b){return this.queue(function(){var a=c(this),e=c.effects.setMode(a,b.options.mode||"hide"),g=parseInt(b.options.percent,10)||150,h=g/100,i={height:a.height(),width:a.width()};c.extend(b.options,{fade:true,mode:e,percent:e=="hide"?g:100,from:e=="hide"?i:{height:i.height*h,width:i.width*h}});a.effect("scale",b.options,b.duration,b.callback);a.dequeue()})};c.effects.scale=function(b){return this.queue(function(){var a=c(this),e=c.extend(true,{},b.options),g=c.effects.setMode(a,
+b.options.mode||"effect"),h=parseInt(b.options.percent,10)||(parseInt(b.options.percent,10)==0?0:g=="hide"?0:100),i=b.options.direction||"both",f=b.options.origin;if(g!="effect"){e.origin=f||["middle","center"];e.restore=true}f={height:a.height(),width:a.width()};a.from=b.options.from||(g=="show"?{height:0,width:0}:f);h={y:i!="horizontal"?h/100:1,x:i!="vertical"?h/100:1};a.to={height:f.height*h.y,width:f.width*h.x};if(b.options.fade){if(g=="show"){a.from.opacity=0;a.to.opacity=1}if(g=="hide"){a.from.opacity=
+1;a.to.opacity=0}}e.from=a.from;e.to=a.to;e.mode=g;a.effect("size",e,b.duration,b.callback);a.dequeue()})};c.effects.size=function(b){return this.queue(function(){var a=c(this),e=["position","top","bottom","left","right","width","height","overflow","opacity"],g=["position","top","bottom","left","right","overflow","opacity"],h=["width","height","overflow"],i=["fontSize"],f=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],k=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],
+p=c.effects.setMode(a,b.options.mode||"effect"),n=b.options.restore||false,m=b.options.scale||"both",l=b.options.origin,j={height:a.height(),width:a.width()};a.from=b.options.from||j;a.to=b.options.to||j;if(l){l=c.effects.getBaseline(l,j);a.from.top=(j.height-a.from.height)*l.y;a.from.left=(j.width-a.from.width)*l.x;a.to.top=(j.height-a.to.height)*l.y;a.to.left=(j.width-a.to.width)*l.x}var d={from:{y:a.from.height/j.height,x:a.from.width/j.width},to:{y:a.to.height/j.height,x:a.to.width/j.width}};
+if(m=="box"||m=="both"){if(d.from.y!=d.to.y){e=e.concat(f);a.from=c.effects.setTransition(a,f,d.from.y,a.from);a.to=c.effects.setTransition(a,f,d.to.y,a.to)}if(d.from.x!=d.to.x){e=e.concat(k);a.from=c.effects.setTransition(a,k,d.from.x,a.from);a.to=c.effects.setTransition(a,k,d.to.x,a.to)}}if(m=="content"||m=="both")if(d.from.y!=d.to.y){e=e.concat(i);a.from=c.effects.setTransition(a,i,d.from.y,a.from);a.to=c.effects.setTransition(a,i,d.to.y,a.to)}c.effects.save(a,n?e:g);a.show();c.effects.createWrapper(a);
+a.css("overflow","hidden").css(a.from);if(m=="content"||m=="both"){f=f.concat(["marginTop","marginBottom"]).concat(i);k=k.concat(["marginLeft","marginRight"]);h=e.concat(f).concat(k);a.find("*[width]").each(function(){child=c(this);n&&c.effects.save(child,h);var o={height:child.height(),width:child.width()};child.from={height:o.height*d.from.y,width:o.width*d.from.x};child.to={height:o.height*d.to.y,width:o.width*d.to.x};if(d.from.y!=d.to.y){child.from=c.effects.setTransition(child,f,d.from.y,child.from);
+child.to=c.effects.setTransition(child,f,d.to.y,child.to)}if(d.from.x!=d.to.x){child.from=c.effects.setTransition(child,k,d.from.x,child.from);child.to=c.effects.setTransition(child,k,d.to.x,child.to)}child.css(child.from);child.animate(child.to,b.duration,b.options.easing,function(){n&&c.effects.restore(child,h)})})}a.animate(a.to,{queue:false,duration:b.duration,easing:b.options.easing,complete:function(){a.to.opacity===0&&a.css("opacity",a.from.opacity);p=="hide"&&a.hide();c.effects.restore(a,
+n?e:g);c.effects.removeWrapper(a);b.callback&&b.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Shake 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Shake
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(d){d.effects.shake=function(a){return this.queue(function(){var b=d(this),j=["position","top","bottom","left","right"];d.effects.setMode(b,a.options.mode||"effect");var c=a.options.direction||"left",e=a.options.distance||20,l=a.options.times||3,f=a.duration||a.options.duration||140;d.effects.save(b,j);b.show();d.effects.createWrapper(b);var g=c=="up"||c=="down"?"top":"left",h=c=="up"||c=="left"?"pos":"neg";c={};var i={},k={};c[g]=(h=="pos"?"-=":"+=")+e;i[g]=(h=="pos"?"+=":"-=")+e*2;k[g]=
+(h=="pos"?"-=":"+=")+e*2;b.animate(c,f,a.options.easing);for(e=1;e<l;e++)b.animate(i,f,a.options.easing).animate(k,f,a.options.easing);b.animate(i,f,a.options.easing).animate(c,f/2,a.options.easing,function(){d.effects.restore(b,j);d.effects.removeWrapper(b);a.callback&&a.callback.apply(this,arguments)});b.queue("fx",function(){b.dequeue()});b.dequeue()})}})(jQuery);
+;/*
+ * jQuery UI Effects Slide 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Slide
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(c){c.effects.slide=function(d){return this.queue(function(){var a=c(this),h=["position","top","bottom","left","right"],f=c.effects.setMode(a,d.options.mode||"show"),b=d.options.direction||"left";c.effects.save(a,h);a.show();c.effects.createWrapper(a).css({overflow:"hidden"});var g=b=="up"||b=="down"?"top":"left";b=b=="up"||b=="left"?"pos":"neg";var e=d.options.distance||(g=="top"?a.outerHeight({margin:true}):a.outerWidth({margin:true}));if(f=="show")a.css(g,b=="pos"?isNaN(e)?"-"+e:-e:e);
+var i={};i[g]=(f=="show"?b=="pos"?"+=":"-=":b=="pos"?"-=":"+=")+e;a.animate(i,{queue:false,duration:d.duration,easing:d.options.easing,complete:function(){f=="hide"&&a.hide();c.effects.restore(a,h);c.effects.removeWrapper(a);d.callback&&d.callback.apply(this,arguments);a.dequeue()}})})}})(jQuery);
+;/*
+ * jQuery UI Effects Transfer 1.8.14
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Effects/Transfer
+ *
+ * Depends:
+ *	jquery.effects.core.js
+ */
+(function(e){e.effects.transfer=function(a){return this.queue(function(){var b=e(this),c=e(a.options.to),d=c.offset();c={top:d.top,left:d.left,height:c.innerHeight(),width:c.innerWidth()};d=b.offset();var f=e('<div class="ui-effects-transfer"></div>').appendTo(document.body).addClass(a.options.className).css({top:d.top,left:d.left,height:b.innerHeight(),width:b.innerWidth(),position:"absolute"}).animate(c,a.duration,a.options.easing,function(){f.remove();a.callback&&a.callback.apply(b[0],arguments);
+b.dequeue()})})}})(jQuery);
+;
\ No newline at end of file
diff --git a/core/js/js.js b/core/js/js.js
new file mode 100644
index 0000000000000000000000000000000000000000..9d2b20d10f095804200b6b52544b2e8f97e89039
--- /dev/null
+++ b/core/js/js.js
@@ -0,0 +1,372 @@
+/**
+ * translate a string
+ * @param app the id of the app for which to translate the string
+ * @param text the string to translate
+ * @return string
+ */
+function t(app,text){
+	if( !( app in t.cache )){
+		
+		$.post( OC.filePath('core','ajax','translations.php'), {'app': app}, function(jsondata){
+			t.cache[app] = jsondata.data;
+		});
+
+		// Bad answer ...
+		if( !( app in t.cache )){
+			t.cache[app] = [];
+		}
+	}
+	if( typeof( t.cache[app][text] ) !== 'undefined' ){
+		return t.cache[app][text];
+	}
+	else{
+		return text;
+	}
+}
+t.cache={};
+
+OC={
+	webroot:oc_webroot,
+	currentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false,
+	coreApps:['files','admin','log','search','settings','core','3rdparty'],
+	/**
+	 * get an absolute url to a file in an appen
+	 * @param app the id of the app the file belongs to
+	 * @param file the file path relative to the app folder
+	 * @return string
+	 */
+	linkTo:function(app,file){
+		return OC.filePath(app,'',file);
+	},
+	/**
+	 * get the absolute url for a file in an app
+	 * @param app the id of the app
+	 * @param type the type of the file to link to (e.g. css,img,ajax.template)
+	 * @param file the filename
+	 * @return string
+	 */
+	filePath:function(app,type,file){
+		var isCore=OC.coreApps.indexOf(app)!=-1;
+		app+='/';
+		var link=OC.webroot+'/';
+		if(!isCore){
+			link+='apps/';
+		}
+		link+=app;
+		if(type){
+			link+=type+'/'
+		}
+		link+=file;
+		return link;
+	},
+	/**
+	 * get the absolute path to an image file
+	 * @param app the app id to which the image belongs
+	 * @param file the name of the image file
+	 * @return string
+	 * 
+	 * if no extention is given for the image, it will automatically decide between .png and .svg based on what the browser supports
+	 */ 
+	imagePath:function(app,file){
+		if(file.indexOf('.')==-1){//if no extention is given, use png or svg depending on browser support
+			file+=(SVGSupport())?'.svg':'.png'
+		}
+		return OC.filePath(app,'img',file);
+	},
+	/**
+	 * load a script for the server and load it
+	 * @param app the app id to which the script belongs
+	 * @param script the filename of the script
+	 * @param ready event handeler to be called when the script is loaded
+	 * 
+	 * if the script is already loaded, the event handeler will be called directly
+	 */
+	addScript:function(app,script,ready){
+		var path=OC.filePath(app,'js',script+'.js');
+		if(OC.addStyle.loaded.indexOf(path)==-1){
+			OC.addStyle.loaded.push(path);
+			if(ready){
+				$.getScript(path,ready);
+			}else{
+				$.getScript(path);
+			}
+		}else{
+			if(ready){
+				ready();
+			}
+		}
+	},
+	/**
+	 * load a css file and load it
+	 * @param app the app id to which the css style belongs
+	 * @param style the filename of the css file
+	 */
+	addStyle:function(app,style){
+		var path=OC.filePath(app,'css',style+'.css');
+		if(OC.addScript.loaded.indexOf(path)==-1){
+			OC.addScript.loaded.push(path);
+			var style=$('<link rel="stylesheet" type="text/css" href="'+path+'"/>');
+			$('head').append(style);
+		}
+	},
+	/**
+	 * do a search query and display the results
+	 * @param query the search query
+	 */
+	search:function(query){
+		if(query){
+			OC.addStyle('search','results');
+			$.getJSON(OC.filePath('search','ajax','search.php')+'?query='+encodeURIComponent(query), function(results){
+				OC.search.lastResults=results;
+				OC.search.showResults(results);
+			});
+		}
+	}
+}
+OC.search.customResults={};
+OC.search.currentResult=-1;
+OC.search.lastQuery='';
+OC.search.lastResults={};
+OC.addStyle.loaded=[];
+OC.addScript.loaded=[];
+
+/**
+ * implement Array.filter for browsers without native support
+ */
+if (!Array.prototype.filter) {
+	Array.prototype.filter = function(fun /*, thisp*/) {
+		var len = this.length >>> 0;
+		if (typeof fun != "function")
+			throw new TypeError();
+		
+		var res = [];
+		var thisp = arguments[1];
+		for (var i = 0; i < len; i++) {
+			if (i in this) {
+				var val = this[i]; // in case fun mutates this
+				if (fun.call(thisp, val, i, this))
+					res.push(val);
+			}
+		}
+		return res;
+	}
+}
+/**
+ * implement Array.indexOf for browsers without native support
+ */
+if (!Array.prototype.indexOf){
+	Array.prototype.indexOf = function(elt /*, from*/)
+	{
+		var len = this.length;
+		
+		var from = Number(arguments[1]) || 0;
+		from = (from < 0)
+		? Math.ceil(from)
+		: Math.floor(from);
+		if (from < 0)
+			from += len;
+		
+		for (; from < len; from++)
+		{
+			if (from in this &&
+				this[from] === elt)
+				return from;
+		}
+		return -1;
+	};
+}
+
+/**
+ * check if the browser support svg images
+ */
+function SVGSupport() {
+	return SVGSupport.checkMimeType.correct && !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', "svg").createSVGRect;
+}
+SVGSupport.checkMimeType=function(){
+	$.ajax({
+		url: OC.imagePath('core','breadcrumb.svg'),
+		success:function(data,text,xhr){
+			var headerParts=xhr.getAllResponseHeaders().split("\n");
+			var headers={};
+			$.each(headerParts,function(i,text){
+				if(text){
+					var parts=text.split(':',2);
+					var value=parts[1].trim();
+					if(value[0]=='"'){
+						value=value.substr(1,value.length-2);
+					}
+					headers[parts[0]]=value;
+				}
+			});
+			if(headers["Content-Type"]!='image/svg+xml'){
+				replaceSVG();
+				SVGSupport.checkMimeType.correct=false
+			}
+		}
+	});
+}
+SVGSupport.checkMimeType.correct=true;
+
+//replace all svg images with png for browser compatibility
+function replaceSVG(){
+	$('img.svg').each(function(index,element){
+		element=$(element);
+		var src=element.attr('src');
+		element.attr('src',src.substr(0,src.length-3)+'png');
+	});
+	$('.svg').each(function(index,element){
+		element=$(element);
+		var background=element.css('background-image');
+		if(background && background!='none'){
+			background=background.substr(0,background.length-4)+'png)';
+			element.css('background-image',background);
+		}
+		element.find('*').each(function(index,element) {
+			element=$(element);
+			var background=element.css('background-image');
+			if(background && background!='none'){
+				background=background.substr(0,background.length-4)+'png)';
+				element.css('background-image',background);
+			}
+		});
+	});
+}
+
+/**
+ * prototypal inharitence functions
+ * 
+ * usage:
+ * MySubObject=object(MyObject)
+ */
+function object(o) {
+	function F() {}
+    F.prototype = o;
+	return new F();
+}
+
+$(document).ready(function(){
+	if(!SVGSupport()){//replace all svg images with png images for browser that dont support svg
+		replaceSVG();
+	}else{
+		SVGSupport.checkMimeType();
+	}
+	$('form.searchbox').submit(function(event){
+		event.preventDefault();
+	})
+	$('#searchbox').keyup(function(event){
+		if(event.keyCode==13){//enter
+			if(OC.search.currentResult>-1){
+				var result=$('#searchresults tr.result a')[OC.search.currentResult];
+				window.location = $(result).attr('href');
+			}
+		}else if(event.keyCode==38){//up
+			if(OC.search.currentResult>0){
+				OC.search.currentResult--;
+				OC.search.renderCurrent();
+			}
+		}else if(event.keyCode==40){//down
+			if(OC.search.lastResults.length>OC.search.currentResult+1){
+				OC.search.currentResult++;
+				OC.search.renderCurrent();
+			}
+		}else if(event.keyCode==27){//esc
+			OC.search.hide();
+		}else{
+			var query=$('#searchbox').val();
+			if(OC.search.lastQuery!=query){
+				OC.search.lastQuery=query;
+				OC.search.currentResult=-1;
+				if(query.length>2){
+					OC.search(query);
+				}else{
+					if(OC.search.hide){
+						OC.search.hide();
+					}
+				}
+			}
+		}
+	});
+
+	// 'show password' checkbox	
+	$('#pass2').showPassword();
+
+	// hide log in button etc. when form fields not filled
+	$('#submit').hide();
+	$('#remember_login').hide();
+	$('#remember_login+label').hide();
+	$('#body-login input').keyup(function() {
+		var empty = false;
+		$('#body-login input').each(function() {
+			if ($(this).val() == '') {
+				empty = true;
+			}
+		});
+
+		if(empty) {
+			$('#submit').fadeOut();
+			$('#remember_login').fadeOut();
+			$('#remember_login+label').fadeOut();
+		} else {
+			$('#submit').fadeIn();
+			$('#remember_login').fadeIn();
+			$('#remember_login+label').fadeIn();
+		}
+	});
+
+	if($('body').attr("id")=="body-user") { $('#settings #expanddiv').hide(); }
+	$('#settings #expand').click(function(event) {
+		$('#settings #expanddiv').slideToggle();
+		event.stopPropagation();
+	});
+	$('#settings #expanddiv').click(function(event){
+		event.stopPropagation();
+	})
+	$('#settings #expand').hover(function(){
+		$('#settings #expand+span').fadeToggle();
+	});
+	$(window).click(function(){//hide the settings menu when clicking oustide it
+		if($('body').attr("id")=="body-user"){
+			$('#settings #expanddiv').slideUp();
+		}
+	});
+
+	// all the tipsy stuff needs to be here (in reverse order) to work
+	$('.jp-controls .jp-previous').tipsy({gravity:'nw', fade:true, live:true});
+	$('.jp-controls .jp-next').tipsy({gravity:'n', fade:true, live:true});
+	$('.password .action').tipsy({gravity:'se', fade:true, live:true});
+	$('.file_upload_button_wrapper').tipsy({gravity:'e', fade:true}); 
+	$('.selectedActions a.delete').tipsy({gravity: 'se', fade:true, live:true});
+	$('.selectedActions a').tipsy({gravity:'s', fade:true, live:true});
+	$('#headerSize').tipsy({gravity:'s', fade:true, live:true});
+	$('td.filesize').tipsy({gravity:'s', fade:true, live:true});
+	$('td .modified').tipsy({gravity:'s', fade:true, live:true});
+
+	$('input').tipsy({gravity:'w', fade:true});
+	$('input[type=text]').focus(function(){
+		this.select();
+	});
+});
+
+if (!Array.prototype.map){
+	Array.prototype.map = function(fun /*, thisp */){
+		"use strict";
+		
+		if (this === void 0 || this === null)
+			throw new TypeError();
+		
+		var t = Object(this);
+		var len = t.length >>> 0;
+		if (typeof fun !== "function")
+			throw new TypeError();
+		
+		var res = new Array(len);
+		var thisp = arguments[1];
+		for (var i = 0; i < len; i++){
+			if (i in t){
+				res[i] = fun.call(thisp, t[i], i, t);
+			}
+		}
+		
+	    return res;
+	};
+}
diff --git a/core/js/listview.js b/core/js/listview.js
new file mode 100644
index 0000000000000000000000000000000000000000..e3e5ebdab8f9635d0310838ea25303e5adb95e9f
--- /dev/null
+++ b/core/js/listview.js
@@ -0,0 +1,71 @@
+function ListView(element){
+	this.element=element;
+}
+
+ListView.generateTable=function(collumns){
+	var html='<table>';
+	html+='<thead>';
+	$.each(collumns,function(index,collumn){
+		html+='<th>'+collumn+'</th>';
+	});
+	html+='<thead>';
+	html+='</head>';
+	html+='<tbody>';
+	html+'<tr class="template">'
+	$.each(collumns,function(index,collumn){
+		html+='<th class="'+collumn.toLower()+'"</th>';
+	});
+	html+'</tr>'
+	html+='</tbody>';
+	html='</table>';
+	return $(html);
+}
+
+ListView.prototype={
+	rows:{},
+	hoverElements:{},
+	addRow:function(id,data,extraData){
+		var tr=this.element.find('tr.template').clone();
+		tr.removeClass('template');
+		$.each(data,function(name,value){
+			tr.children('td.'+name).text(value);
+			tr.attr('data-'+name,value);
+		});
+		$.each(extraData,function(name,value){
+			tr.attr('data-'+name,value);
+		});
+		this.rows[id]=data;
+		tr.data('id',id);
+		this.element.children('tbody').append(tr);
+	},
+	removeRow:function(id){
+		this.rows[id].remove();
+		delete this.rows[id];
+	},
+	hoverHandeler:function(tr){
+		$.each(this.hoverElement,function(index,collumn){
+			$.each(collumn,function(index,element){
+				var html='<a href="#" title="'+element.title+'" class="hoverElement"/>';
+				var element=$(html);
+				element.append($('<img src="'+element.icon+'"/>'));
+				element.click(element.callback);
+				tr.children('td.'+collumn).append(element)
+			});
+		});
+		if(this.deleteCallback){
+			
+		}
+	},
+	hoverHandelerOut:function(tr){
+		tr.find('*.hoverElement').remove();
+	},
+	addHoverElement:function(collumn,icon,title,callback){
+		if(!this.hoverElements[collumn]){
+			this.hoverElements[collumn]=[];
+		}
+		this.hoverElements[row].push({icon:icon,callback:callback,title:title});
+	},
+	empty:function(){
+		this.element.children('tr:not(.template)').remove();
+	}
+}
\ No newline at end of file
diff --git a/core/js/multiselect.js b/core/js/multiselect.js
new file mode 100644
index 0000000000000000000000000000000000000000..559cdf9b167ef0189d1bbfcfddb2ab77f278164d
--- /dev/null
+++ b/core/js/multiselect.js
@@ -0,0 +1,160 @@
+(function( $ ){
+	var multiSelectId=-1;
+	$.fn.multiSelect=function(options){
+		multiSelectId++;
+		var settings = {
+			'createCallback':false,
+			'createText':false,
+			'title':this.attr('title'),
+			'checked':[],
+			'oncheck':false,
+			'onuncheck':false,
+		};
+		$.extend(settings,options);			
+		var button=$('<div class="multiselect button"><span>'+settings.title+'</span><span>â–¾</span></div>');
+		if(settings.checked.length>0){
+			button.children('span').first().text(settings.checked.join(', '));
+		}
+		var span=$('<span/>');
+		span.append(button);
+		button.data('id',multiSelectId);
+		button.selectedItems=[];
+		this.hide();
+		this.before(span);
+		settings.minWidth=button.width();
+		button.css('min-width',settings.minWidth);
+		settings.minOuterWidth=button.outerWidth()-2;
+		button.data('settings',settings);
+		
+		button.click(function(event){
+			var button=$(this);
+			if(button.parent().children('ul').length>0){
+				button.parent().children('ul').slideUp(400,function(){
+					button.parent().children('ul').remove();
+					button.removeClass('active');
+				});
+				return;
+			}
+			var lists=$('ul.multiselectoptions');
+			lists.slideUp(400,function(){
+				lists.remove();
+				$('div.multiselect').removeClass('active');
+				button.addClass('active');
+			});
+			button.addClass('active');
+			event.stopPropagation();
+			var options=$(this).parent().next().children().map(function(){return $(this).val()});
+			var list=$('<ul class="multiselectoptions"/>').hide().appendTo($(this).parent());
+			function createItem(item,checked){
+				var id='ms'+multiSelectId+'-option-'+item;
+				var input=$('<input id="'+id+'" type="checkbox"/>');
+				var label=$('<label for="'+id+'">'+item+'</label>');
+				if(settings.checked.indexOf(item)!=-1 || checked){
+					input.attr('checked','checked');
+				}
+				if(checked){
+					settings.checked.push(item);
+				}
+				input.change(function(){
+					var groupname=$(this).next().text();
+					if($(this).attr('checked')){
+						settings.checked.push(groupname);
+						if(settings.oncheck){
+							if(settings.oncheck(groupname)===false){
+								$(this).removeAttr('checked');
+								return;
+							}
+						}
+					}else{
+						var index=settings.checked.indexOf(groupname);
+						settings.checked.splice(index,1);
+						if(settings.onuncheck){
+							if(settings.onuncheck(groupname)===false){
+								$(this).attr('checked','checked');
+								return;
+							}
+						}
+					}
+					var oldWidth=button.width();
+					if(settings.checked.length>0){
+						button.children('span').first().text(settings.checked.join(', '));
+					}else{
+						button.children('span').first().text(settings.title);
+					}
+					var newOuterWidth=Math.max((button.outerWidth()-2),settings.minOuterWidth)+'px'
+					var newWidth=Math.max(button.width(),settings.minWidth);
+					button.css('height',button.height());
+					button.css('white-space','nowrap');
+					button.css('width',oldWidth);
+					button.animate({'width':newWidth},undefined,undefined,function(){
+						button.css('width','');
+					});
+					list.animate({'width':newOuterWidth});
+				});
+				var li=$('<li></li>');
+				li.append(input).append(label);
+				return li;
+			}
+			$.each(options,function(index,item){
+				list.append(createItem(item));
+			});
+			button.parent().data('preventHide',false);
+			if(settings.createText){
+				var li=$('<li>+ <em>'+settings.createText+'<em></li>');
+				li.click(function(event){
+					li.empty();
+					var input=$('<input class="new">');
+					li.append(input);
+					input.focus();
+					input.css('width',button.width());
+					button.parent().data('preventHide',true);
+					input.keypress(function(event) {
+						if(event.keyCode == 13) {
+							event.preventDefault();
+							event.stopPropagation();
+							var li=$(this).parent();
+							$(this).remove();
+							li.text('+ '+settings.createText);
+							li.before(createItem($(this).val()));
+							li.prev().children('input').trigger('click');
+							button.parent().data('preventHide',false);
+							var select=button.parent().next();
+							select.append($('<option value="'+$(this).val()+'">'+$(this).val()+'</option>'));
+							if(settings.createCallback){
+								settings.createCallback();
+							}
+						}
+					});
+					input.blur(function(){
+						event.preventDefault();
+						event.stopPropagation();
+						$(this).remove();
+						li.text('+ '+settings.createText);
+						setTimeout(function(){
+							button.parent().data('preventHide',false);
+						},100);
+					});
+				});
+				list.append(li);
+			}
+			var pos=button.position();
+			list.css('top',pos.top+button.outerHeight()-5);
+			list.css('left',pos.left+3);
+			list.css('width',(button.outerWidth()-2)+'px');
+			list.slideDown();
+			list.click(function(event){
+				event.stopPropagation();
+			});
+		});
+		$(window).click(function(){
+			if(!button.parent().data('preventHide')){
+				button.parent().children('ul').slideUp(400,function(){
+					button.parent().children('ul').remove();
+					button.removeClass('active');
+				});
+			}
+		});
+		
+		return span;
+	};
+})( jQuery );
\ No newline at end of file
diff --git a/core/js/setup.js b/core/js/setup.js
new file mode 100644
index 0000000000000000000000000000000000000000..736bedac755477dce7d53c56a3d499baa9b8e6ee
--- /dev/null
+++ b/core/js/setup.js
@@ -0,0 +1,27 @@
+$(document).ready(function() {
+	$('#selectDbType').buttonset();
+	$('#datadirField').hide(250);
+	if($('#hasSQLite').val()=='true'){
+		$('#databaseField').hide(250);
+		$('#use_other_db').slideUp(250);
+	}
+
+	$('#sqlite').click(function() {
+		$('#use_other_db').slideUp(250);
+	});
+
+	$('#mysql').click(function() {
+		$('#use_other_db').slideDown(250);
+	});
+	
+	$('#pgsql').click(function() {
+		$('#use_other_db').slideDown(250);
+	});
+
+	$('#showAdvanced').click(function() {
+		$('#datadirField').slideToggle(250);
+		if($('#hasSQLite').val()=='true'){
+			$('#databaseField').slideToggle(250);
+		}
+	});
+});
diff --git a/core/l10n/bg_BG.php b/core/l10n/bg_BG.php
new file mode 100644
index 0000000000000000000000000000000000000000..da8f1265498615655d57a26425ce3f731b0237c5
--- /dev/null
+++ b/core/l10n/bg_BG.php
@@ -0,0 +1,13 @@
+<?php $TRANSLATIONS = array(
+"Login failed!" => "Входа пропадна!",
+"Database name" => "Име на базата",
+"Advanced" => "Разширено",
+"Host" => "хост",
+"Table prefix" => "Префикс за таблиците",
+"Data folder" => "Директория за данни",
+"Finish setup" => "Завършване на настройките",
+"Cloud not found" => "обклакът не намерен",
+"prev" => "пред.",
+"next" => "следващо",
+"You are logged out." => "Вие излязохте."
+);
diff --git a/core/l10n/ca.php b/core/l10n/ca.php
new file mode 100644
index 0000000000000000000000000000000000000000..5d4b67d421b75458fcfc9c87e5e953848905f85f
--- /dev/null
+++ b/core/l10n/ca.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Personal",
+"Users" => "Usuaris",
+"Apps" => "Aplicacions",
+"Admin" => "Administrador",
+"Help" => "Ajuda",
+"Cloud not found" => "No s'ha trobat el núvol",
+"Create an <strong>admin account</strong>" => "Crea un <strong>compte d'administrador</strong>",
+"Username" => "Nom d'usuari",
+"Password" => "Contrasenya",
+"Configure the database" => "Configura la base de dades",
+"will be used" => "s'usarà",
+"Database user" => "Usuari de la base de dades",
+"Database password" => "Contrasenya de la base de dades",
+"Database name" => "Nom de la base de dades",
+"Advanced" => "Avançat",
+"Host" => "Host",
+"Table prefix" => "Prefix de les taules",
+"Data folder" => "Carpeta de dades",
+"Finish setup" => "Acaba la configuració",
+"gives you the freedom to control your own data on the internet" => "us dóna la llibertat per controlar les vostres dades a internet",
+"Log out" => "Sortir",
+"Settings" => "Arranjament",
+"Login failed!" => "L'inici de sessió ha fallat!",
+"remember" => "recorda'm",
+"You are logged out." => "Heu tancat la sessió.",
+"prev" => "anterior",
+"next" => "següent"
+);
diff --git a/core/l10n/da.php b/core/l10n/da.php
new file mode 100644
index 0000000000000000000000000000000000000000..081ee79df9fa0cb5377df3b394f20895941a7aba
--- /dev/null
+++ b/core/l10n/da.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Personlig",
+"Users" => "Brugere",
+"Apps" => "Apps",
+"Admin" => "Admin",
+"Help" => "Hjælp",
+"Cloud not found" => "Sky ikke fundet",
+"Create an <strong>admin account</strong>" => "Lav en <strong>administrator konto</strong>",
+"Username" => "Brugernavn",
+"Password" => "Kodeord",
+"Configure the database" => "Konfigurer databasen",
+"will be used" => "vil blive brugt",
+"Database user" => "Database-bruger",
+"Database password" => "Database-kodeord",
+"Database name" => "Database-navn",
+"Advanced" => "Avanceret",
+"Host" => "Vært",
+"Table prefix" => "Tabel præfiks",
+"Data folder" => "Data mappe",
+"Finish setup" => "Afslut installation",
+"gives you the freedom to control your own data on the internet" => "giver dig friheden til at kontrollere dine egne data på internettet",
+"Log out" => "Log ud",
+"Settings" => "Indstillinger",
+"Login failed!" => "Login mislykkedes!",
+"remember" => "husk",
+"You are logged out." => "Du er nu logget ud",
+"prev" => "forrige",
+"next" => "næste"
+);
diff --git a/core/l10n/de.php b/core/l10n/de.php
new file mode 100644
index 0000000000000000000000000000000000000000..07634508b1bd4037fbe8fdc8a21f733d8f08a29b
--- /dev/null
+++ b/core/l10n/de.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Persönlich",
+"Users" => "Nutzer",
+"Apps" => "Anwendungen",
+"Admin" => "Verwaltung",
+"Help" => "Hilfe",
+"Login failed!" => "Anmeldung fehlgeschlagen!",
+"remember" => "merken",
+"Create an <strong>admin account</strong>" => "<strong>Admin-Konto</strong> anlegen",
+"Username" => "Nutzername",
+"Password" => "Passwort",
+"Configure the database" => "Datenbank einrichten",
+"will be used" => "wird genutzt",
+"Database user" => "Datenbanknutzer",
+"Database password" => "Datenbankpasswort",
+"Database name" => "Datenbankname",
+"Advanced" => "Erweitert",
+"Host" => "Host",
+"Table prefix" => "Tabellenpräfix",
+"Data folder" => "Datenverzeichnis",
+"Finish setup" => "Installation abschließen",
+"Cloud not found" => "Cloud nicht verfügbar",
+"gives you the freedom to control your own data on the internet" => "gibt dir die Freiheit, deine eigenen Daten im Internet zu kontrollieren.",
+"prev" => "Zurück",
+"next" => "Weiter",
+"You are logged out." => "Erfolgreich abgemeldet.",
+"Log out" => "Abmelden",
+"Settings" => "Einstellungen"
+);
diff --git a/core/l10n/el.php b/core/l10n/el.php
new file mode 100644
index 0000000000000000000000000000000000000000..ab83146c534d8465263dd54fec107c34453b445f
--- /dev/null
+++ b/core/l10n/el.php
@@ -0,0 +1,28 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Προσωπικά",
+"Users" => "Χρήστες",
+"Apps" => "Εφαρμογές",
+"Help" => "Βοήθεια",
+"Login failed!" => "Η σύνδεση απέτυχε!",
+"remember" => "να με θυμάσαι",
+"Create an <strong>admin account</strong>" => "Δημιουργήστε έναν <strong>λογαριασμό διαχειριστή</strong>",
+"Username" => "Όνομα Χρήστη",
+"Password" => "Κωδικός",
+"Configure the database" => "Διαμόρφωση της βάσης δεδομένων",
+"will be used" => "θα χρησιμοποιηθούν",
+"Database user" => "Χρήστης της βάσης δεδομένων",
+"Database password" => "Κωδικός πρόσβασης βάσης δεδομένων",
+"Database name" => "Όνομα βάσης δεδομένων",
+"Advanced" => "Για προχωρημένους",
+"Host" => "Εξυπηρετητής",
+"Table prefix" => "Πρόθεμα πίνακα",
+"Data folder" => "Φάκελος δεδομένων",
+"Finish setup" => "Ολοκλήρωση εγκατάστασης",
+"Cloud not found" => "Δεν βρέθηκε σύννεφο",
+"gives you the freedom to control your own data on the internet" => "σας δίνει την ελευθερία να ελέγχετε τα δικά σας δεδομένα στο διαδίκτυο",
+"prev" => "προηγούμενο",
+"next" => "επόμενο",
+"You are logged out." => "Έχετε αποσυνδεθεί.",
+"Log out" => "Αποσύνδεση",
+"Settings" => "Ρυθμίσεις"
+);
diff --git a/core/l10n/es.php b/core/l10n/es.php
new file mode 100644
index 0000000000000000000000000000000000000000..f53d56c50a008f59509248cfc495465e8ccc2a78
--- /dev/null
+++ b/core/l10n/es.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Personal",
+"Users" => "Usuarios",
+"Apps" => "Aplicaciones",
+"Admin" => "Administrador",
+"Help" => "Ayuda",
+"Cloud not found" => "No se encontró la nube",
+"Create an <strong>admin account</strong>" => "Crear una <strong>cuenta de administrador</strong>",
+"Username" => "Nombre de usuario",
+"Password" => "Contraseña",
+"Configure the database" => "Configurar la base de datos",
+"will be used" => "serán utilizados",
+"Database user" => "Usuario de la base de datos",
+"Database password" => "Contraseña de la base de datos",
+"Database name" => "Nombre de la base de datos",
+"Advanced" => "Avanzado",
+"Host" => "Host",
+"Table prefix" => "Prefijo de la tabla",
+"Data folder" => "Directorio de almacenamiento",
+"Finish setup" => "Completar instalación",
+"gives you the freedom to control your own data on the internet" => "te da la libertad del control de tus propios datos en internet",
+"Log out" => "Salir",
+"Settings" => "Ajustes",
+"Login failed!" => "¡No se pudo iniciar sesión!",
+"remember" => "recuérdame",
+"You are logged out." => "Has cerrado sesión.",
+"prev" => "anterior",
+"next" => "siguiente"
+);
diff --git a/core/l10n/fr.php b/core/l10n/fr.php
new file mode 100644
index 0000000000000000000000000000000000000000..50ea4990715f007f0f1e6229b98b22259726ba81
--- /dev/null
+++ b/core/l10n/fr.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Personnel",
+"Users" => "Utilisateurs",
+"Apps" => "Applications",
+"Admin" => "Administration",
+"Help" => "Aide",
+"Cloud not found" => "Introuvable",
+"Create an <strong>admin account</strong>" => "Créer un <strong>compte administrateur</strong>",
+"Username" => "Nom d'utilisateur",
+"Password" => "Mot de passe",
+"Configure the database" => "Configurer la base de données",
+"will be used" => "sera utilisé",
+"Database user" => "Utilisateur de la base de données",
+"Database password" => "Mot de passe de la base de données",
+"Database name" => "Nom de la base de données",
+"Advanced" => "Avancé",
+"Host" => "Hôte",
+"Table prefix" => "Préfixe des tables",
+"Data folder" => "Répertoire des données",
+"Finish setup" => "Terminer l'installation",
+"gives you the freedom to control your own data on the internet" => "vous rend libre de contrôler vos propres données sur internet",
+"Log out" => "Se déconnecter",
+"Settings" => "Paramètres",
+"Login failed!" => "Échec de la connexion !",
+"remember" => "se souvenir de moi",
+"You are logged out." => "Vous êtes désormais déconnecté.",
+"prev" => "précédent",
+"next" => "suivant"
+);
diff --git a/core/l10n/id.php b/core/l10n/id.php
new file mode 100644
index 0000000000000000000000000000000000000000..a8620f3dbfa67e55ef7367ec1a3fa07272eea42f
--- /dev/null
+++ b/core/l10n/id.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Pribadi",
+"Users" => "Pengguna",
+"Apps" => "Aplikasi",
+"Admin" => "Admin",
+"Help" => "Bantuan",
+"Cloud not found" => "Cloud tidak ditemukan",
+"Create an <strong>admin account</strong>" => "Buat sebuah <strong>akun admin</strong>",
+"Username" => "Username",
+"Password" => "Password",
+"Configure the database" => "Konfigurasi database",
+"will be used" => "akan digunakan",
+"Database user" => "Pengguna database",
+"Database password" => "Password database",
+"Database name" => "Nama database",
+"Advanced" => "Tingkat Lanjut",
+"Host" => "Host",
+"Table prefix" => "Awalan tabel",
+"Data folder" => "Folder data",
+"Finish setup" => "Selesaikan instalasi",
+"gives you the freedom to control your own data on the internet" => "memberikan anda kebebasan dalam mengendalikan data anda di internet",
+"Log out" => "Keluar",
+"Settings" => "Setelan",
+"Login failed!" => "Gagal masuk!",
+"remember" => "selalu login",
+"You are logged out." => "Anda telah keluar.",
+"prev" => "sebelum",
+"next" => "selanjutnya"
+);
diff --git a/core/l10n/it.php b/core/l10n/it.php
new file mode 100644
index 0000000000000000000000000000000000000000..68349e17c47a5b3ec38ae5a78849c2559ece8450
--- /dev/null
+++ b/core/l10n/it.php
@@ -0,0 +1,28 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Personale",
+"Users" => "Utenti",
+"Apps" => "Applicazioni",
+"Help" => "Aiuto",
+"Login failed!" => "Login fallito!",
+"remember" => "ricorda",
+"Create an <strong>admin account</strong>" => "Crea un &lt;strong&gt;account amministratore&lt;/strong&gt;",
+"Username" => "Nome utente",
+"Password" => "Password",
+"Configure the database" => "Configura il database",
+"will be used" => "sarà usato",
+"Database user" => "Utente database",
+"Database password" => "Password database",
+"Database name" => "Nome database",
+"Advanced" => "Opzioni avanzate",
+"Host" => "Host",
+"Table prefix" => "Prefisso tabella",
+"Data folder" => "Cartella dati",
+"Finish setup" => "Termina",
+"Cloud not found" => "Cloud non trovata",
+"gives you the freedom to control your own data on the internet" => "da la libertà di controllare i tuoi dati su internet",
+"prev" => "precedente",
+"next" => "successivo",
+"You are logged out." => "Sei uscito.",
+"Log out" => "Log out",
+"Settings" => "Impostazioni"
+);
diff --git a/core/l10n/l10n-de.php b/core/l10n/l10n-de.php
new file mode 100644
index 0000000000000000000000000000000000000000..f3084b05df8a06c00d9e5837aa1e9588c324524c
--- /dev/null
+++ b/core/l10n/l10n-de.php
@@ -0,0 +1,5 @@
+<?php
+$LOCALIZATIONS = array(
+	'date' => 'd.m.Y',
+	'datetime' => 'd.m.Y H:i:s',
+	'time' => 'H:i:s' );
diff --git a/core/l10n/nl.php b/core/l10n/nl.php
new file mode 100644
index 0000000000000000000000000000000000000000..7dae374a4d03feb9c6b186c5b16c5d4909a46e0f
--- /dev/null
+++ b/core/l10n/nl.php
@@ -0,0 +1,28 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Persoonlijk",
+"Users" => "Gebruikers",
+"Apps" => "Apps",
+"Help" => "Help",
+"Login failed!" => "Aanmelden mislukt.",
+"remember" => "onthoud gegevens",
+"Create an <strong>admin account</strong>" => "Maak een <strong>admin-account</strong>",
+"Username" => "Gebruikersnaam",
+"Password" => "Wachtwoord",
+"Configure the database" => "Configureren van de database",
+"will be used" => "zal gebruikt woorden",
+"Database user" => "Database gebruiker",
+"Database password" => "Database wachtwoord",
+"Database name" => "Database naam",
+"Advanced" => "Geavanceerd",
+"Host" => "Host",
+"Table prefix" => "Tabelnaam voorvoegsel",
+"Data folder" => "Gegevens folder",
+"Finish setup" => "Installatie afronden",
+"Cloud not found" => "Cloud niet gevonden",
+"gives you the freedom to control your own data on the internet" => "geeft je de vrijheid om je eigen data te controleren op het internet",
+"prev" => "vorige",
+"next" => "volgende",
+"You are logged out." => "U bent afgemeld.",
+"Log out" => "Afmelden",
+"Settings" => "Instellingen"
+);
diff --git a/core/l10n/pl.php b/core/l10n/pl.php
new file mode 100644
index 0000000000000000000000000000000000000000..d63782b873ee02256b7699d0dc24752f34701b08
--- /dev/null
+++ b/core/l10n/pl.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Ustawienia osobiste",
+"Users" => "Użytkownicy",
+"Apps" => "Aplikacje",
+"Admin" => "Administrator",
+"Help" => "Pomoc",
+"Cloud not found" => "Konta nie znaleziono ",
+"Create an <strong>admin account</strong>" => "Stwórz jako <strong>konto administratora</strong>",
+"Username" => "Użytkownik",
+"Password" => "Hasło",
+"Configure the database" => "Konfiguracja bazy danych",
+"will be used" => "zostanie użyte",
+"Database user" => "Użytkownik bazy danych",
+"Database password" => "Hasło do bazy danych",
+"Database name" => "Nazwa bazy danych",
+"Advanced" => "Zaawansowane",
+"Host" => "Host",
+"Table prefix" => "Prefiks tablicy",
+"Data folder" => "Katalog danych",
+"Finish setup" => "Zakończ instalację",
+"gives you the freedom to control your own data on the internet" => "daje Ci wolność kontroli nad Twoimi danymi w Internecie",
+"Log out" => "Wyloguj siÄ™",
+"Settings" => "Ustawienia",
+"Login failed!" => "Nie udało się zalogować!",
+"remember" => "zapamiętaj",
+"You are logged out." => "Zostałeś wylogowany.",
+"prev" => "wstecz",
+"next" => "dalej"
+);
diff --git a/core/l10n/pt_BR.php b/core/l10n/pt_BR.php
new file mode 100644
index 0000000000000000000000000000000000000000..292f698c0874c575eae096b739c8bbaadddc2190
--- /dev/null
+++ b/core/l10n/pt_BR.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Personal" => "Pessoal",
+"Users" => "Usuários",
+"Apps" => "Apps",
+"Admin" => "Admin",
+"Help" => "Ajuda",
+"Cloud not found" => "Cloud não encontrado",
+"Create an <strong>admin account</strong>" => "Criar uma <strong>conta</strong> de <strong>administrador</strong>",
+"Username" => "Nome de Usuário",
+"Password" => "Senha",
+"Configure the database" => "Configurar o banco de dados",
+"will be used" => "será usado",
+"Database user" => "Usuário de banco de dados",
+"Database password" => "Senha do banco de dados",
+"Database name" => "Nome do banco de dados",
+"Advanced" => "Avançado",
+"Host" => "Host 'servidor'",
+"Table prefix" => "Prefixo da tabela",
+"Data folder" => "Pasta de dados",
+"Finish setup" => "Concluir configuração",
+"gives you the freedom to control your own data on the internet" => "te dá a liberdade de controlar seus próprios dados na internet",
+"Log out" => "Sair",
+"Settings" => "Configurações",
+"Login failed!" => "Login sem sucesso",
+"remember" => "lembrete",
+"You are logged out." => "Você está desconectado.",
+"prev" => "anterior",
+"next" => "próximo"
+);
diff --git a/core/l10n/sv.php b/core/l10n/sv.php
new file mode 100644
index 0000000000000000000000000000000000000000..62b46642a1910d13b27b68fa8eae34b6e9621cb6
--- /dev/null
+++ b/core/l10n/sv.php
@@ -0,0 +1,26 @@
+<?php $TRANSLATIONS = array(
+"Users" => "Användare",
+"Apps" => "Program",
+"Help" => "Hjälp",
+"Login failed!" => "Inloggning misslyckades!",
+"remember" => "kom ihåg",
+"Create an <strong>admin account</strong>" => "Skapa ett <strong>administratörskonto</strong>",
+"Username" => "Användarnamn",
+"Password" => "Lösenord",
+"Configure the database" => "Konfigurera databasen",
+"will be used" => "kommer att användas",
+"Database user" => "Databas-användare",
+"Database password" => "Databas-lösenord",
+"Database name" => "Databasnamn",
+"Advanced" => "Avancerat",
+"Host" => "Värd",
+"Table prefix" => "Tabellprefix",
+"Data folder" => "Datamapp",
+"Finish setup" => "Avsluta installation",
+"Cloud not found" => "Hittade inget moln",
+"gives you the freedom to control your own data on the internet" => "ger dig friheten att hantera ditt eget data på internet",
+"prev" => "föregående",
+"next" => "nästa",
+"You are logged out." => "Du är utloggad",
+"Settings" => "Inställningar"
+);
diff --git a/core/l10n/xgettextfiles b/core/l10n/xgettextfiles
new file mode 100644
index 0000000000000000000000000000000000000000..0741830e9cc185fead7b79079732cb9ef6a9803d
--- /dev/null
+++ b/core/l10n/xgettextfiles
@@ -0,0 +1,10 @@
+../strings.php
+../templates/404.php
+../templates/error.php
+../templates/installation.php
+../templates/layout.guest.php
+../templates/layout.user.php
+../templates/login.php
+../templates/logout.php
+../templates/part.pagenavi.php
+../../lib/app.php
diff --git a/core/strings.php b/core/strings.php
new file mode 100644
index 0000000000000000000000000000000000000000..9b4290db4769785a39f884d10b9d81d5f8e5a2eb
--- /dev/null
+++ b/core/strings.php
@@ -0,0 +1,10 @@
+<?php
+
+//some strings that are used in /lib but wont be translatable unless they are in /core too
+$l=new OC_L10N('core');
+$l->t("Personal");
+$l->t("Users");
+$l->t("Apps");
+$l->t("Admin");
+$l->t("Help");
+?>
diff --git a/core/templates/404.php b/core/templates/404.php
new file mode 100644
index 0000000000000000000000000000000000000000..13a81010343449ec60658b55083529e582b35eb5
--- /dev/null
+++ b/core/templates/404.php
@@ -0,0 +1,15 @@
+<?php
+if(!isset($_)){//also provide standalone error page
+	require_once '../../lib/base.php';
+	
+	$tmpl = new OC_Template( '', '404', 'guest' );
+	$tmpl->printPage();
+	exit;
+}
+?>
+<ul>
+	<li class='error'>
+		<?php echo $l->t( 'Cloud not found' ); ?><br/>
+		<p class='hint'><?php if(isset($_['file'])) echo $_['file']?></p>
+	</li>
+</ul>
diff --git a/core/templates/error.php b/core/templates/error.php
new file mode 100644
index 0000000000000000000000000000000000000000..4f05e008f99b0857536adb37af798ab86945ffa6
--- /dev/null
+++ b/core/templates/error.php
@@ -0,0 +1,8 @@
+<ul>
+	<?php foreach($_["errors"] as $error):?>
+		<li class='error'>
+			<?php echo $error['error'] ?><br/>
+			<p class='hint'><?php if(isset($error['hint']))echo $error['hint'] ?></p>
+		</li>
+	<?php endforeach ?>
+</ul>
diff --git a/core/templates/installation.php b/core/templates/installation.php
new file mode 100644
index 0000000000000000000000000000000000000000..c3cd2968c859db01fea0587413264bf20a1605ba
--- /dev/null
+++ b/core/templates/installation.php
@@ -0,0 +1,84 @@
+<form action="index.php" method="post">
+
+<input type="hidden" name="install" value="true" />
+	<?php if(count($_['errors']) > 0): ?>
+	<ul class="errors">
+		<?php foreach($_['errors'] as $err): ?>
+		<li>
+			<?php if(is_array($err)):?>
+				<?php print $err['error']; ?>
+				<p class='hint'><?php print $err['hint']; ?></p>
+			<?php else: ?>
+				<?php print $err; ?>
+			<?php endif; ?>
+		</li>
+		<?php endforeach; ?>
+	</ul>
+	<?php endif; ?>
+
+	<fieldset>
+		<legend><?php echo $l->t( 'Create an <strong>admin account</strong>' ); ?></legend>
+		<input type="text" name="adminlogin" id="adminlogin" value="<?php print OC_Helper::init_var('adminlogin'); ?>" placeholder="<?php echo $l->t( 'Username' ); ?>" autocomplete="off" autofocus required />
+		<input type="password" name="adminpass" id="adminpass" value="<?php print OC_Helper::init_var('adminpass'); ?>" placeholder="<?php echo $l->t( 'Password' ); ?>" required />
+	</fieldset>
+	
+	<fieldset id='databaseField'>
+		<?php if($_['hasMySQL'] or $_['hasPostgreSQL']) $hasOtherDB = true; //other than SQLite ?>
+		<legend><?php echo $l->t( 'Configure the database' ); ?></legend>
+		<div id="selectDbType">
+		<?php if($_['hasSQLite']): ?>
+		<input type='hidden' id='hasSQLite' value='true' />
+		<?php if(!$hasOtherDB): ?>
+		<p>SQLite <?php echo $l->t( 'will be used' ); ?>.</p>
+		<input type="hidden" id="dbtype" name="dbtype" value="sqlite" />
+		<?php else: ?>
+		<input type="radio" name="dbtype" value='sqlite' id="sqlite" <?php OC_Helper::init_radio('dbtype', 'sqlite', 'sqlite'); ?>/>
+		<label class="sqlite" for="sqlite">SQLite</label>
+		<?php endif; ?>
+		<?php endif; ?>
+
+		<?php if($_['hasMySQL']): ?>
+		<input type='hidden' id='hasMySQL' value='true'/>
+		<?php if(!$_['hasSQLite'] and !$_['hasPostgreSQL']): ?>
+		<p>MySQL <?php echo $l->t( 'will be used' ); ?>.</p>
+		<input type="hidden" id="dbtype" name="dbtype" value="mysql" />
+		<?php else: ?>
+		<input type="radio" name="dbtype" value='mysql' id="mysql" <?php OC_Helper::init_radio('dbtype','pgsql', 'mysql', 'sqlite'); ?>/>
+		<label class="mysql" for="mysql">MySQL</label>
+		<?php endif; ?>
+		<?php endif; ?>
+
+		<?php if($_['hasPostgreSQL']): ?>
+		<?php if(!$_['hasSQLite'] and !$_['hasMySQL']): ?>
+		<p>PostgreSQL <?php echo $l->t( 'will be used' ); ?>.</p>
+		<input type="hidden" id="dbtype" name="dbtype" value="pgsql" />
+		<?php else: ?>
+		<label class="pgsql" for="pgsql">PostgreSQL</label>
+		<input type="radio" name="dbtype" value='pgsql' id="pgsql" <?php OC_Helper::init_radio('dbtype','pgsql', 'mysql', 'sqlite'); ?>/>
+		<?php endif; ?>
+		<?php endif; ?>
+		</div>
+
+		<?php if($hasOtherDB): ?>
+		<div id="use_other_db">
+			<input type="text" name="dbuser" id="dbuser" value="<?php print OC_Helper::init_var('dbuser'); ?>" placeholder="<?php echo $l->t( 'Database user' ); ?>" autocomplete="off" />
+			<input type="password" name="dbpass" id="dbpass" value="<?php print OC_Helper::init_var('dbpass'); ?>" placeholder="<?php echo $l->t( 'Database password' ); ?>" />
+			<input type="text" name="dbname" id="dbname" value="<?php print OC_Helper::init_var('dbname'); ?>" placeholder="<?php echo $l->t( 'Database name' ); ?>" autocomplete="off" />
+			
+			
+		</div>
+		<?php endif; ?>
+
+	</fieldset>
+	
+	<a id='showAdvanced'><strong><?php echo $l->t( 'Advanced' ); ?> â–¾</strong></a>
+
+	<fieldset id='datadirField'>
+		<label id="dbhostlabel" for="dbhost"><?php echo $l->t( 'Database host' ); ?></label><input type="text" name="dbhost" id="dbhost" value="<?php print OC_Helper::init_var('dbhost', 'localhost'); ?>" placeholder="<?php echo $l->t( 'Database host' ); ?>" />
+		<label id="dbtableprefixlabel" for="dbtableprefix"><?php echo $l->t( 'Table prefix' ); ?></label><input type="text" name="dbtableprefix" id="dbtableprefix" value="<?php print OC_Helper::init_var('dbtableprefix', 'oc_'); ?>" placeholder="<?php echo $l->t( 'Table prefix' ); ?>" />
+
+		<label id="directorylabel" for="directory"><?php echo $l->t( 'Data folder' ); ?></label><input type="text" name="directory" id="directory" value="<?php print OC_Helper::init_var('directory', $_['directory']); ?>" placeholder="<?php echo $l->t( 'Data folder' ); ?>" />
+	</fieldset>
+
+	<input type="submit" value="<?php echo $l->t( 'Finish setup' ); ?>" />
+</form>
diff --git a/core/templates/layout.guest.php b/core/templates/layout.guest.php
new file mode 100644
index 0000000000000000000000000000000000000000..35c4378fc987c80da1f1826b5e2bbeecd9518be4
--- /dev/null
+++ b/core/templates/layout.guest.php
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>ownCloud</title>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+		<link rel="shortcut icon" href="<?php echo image_path('', 'favicon.png'); ?>" /><link rel="apple-touch-icon-precomposed" href="<?php echo image_path('', 'favicon-touch.png'); ?>" />
+		<?php foreach($_['cssfiles'] as $cssfile): ?>
+			<link rel="stylesheet" href="<?php echo $cssfile; ?>" type="text/css" media="screen" />
+		<?php endforeach; ?>
+		<script type="text/javascript">
+			var oc_webroot = '<?php global $WEBROOT; echo $WEBROOT; ?>';
+		</script>
+		<?php foreach($_['jsfiles'] as $jsfile): ?>
+			<script type="text/javascript" src="<?php echo $jsfile; ?>"></script>
+		<?php endforeach; ?>
+	
+		<?php foreach($_['headers'] as $header): ?>
+			<?php
+				echo '<'.$header['tag'].' ';
+				foreach($header['attributes'] as $name=>$value){
+					echo "$name='$value' ";
+				};
+				echo '/>';
+			?>
+		<?php endforeach; ?>
+	</head>
+
+	<body id="body-login">
+		<div id="login">
+			<header><div id="header">
+				<img src="<?php echo image_path('', 'owncloud-logo-medium-white.png'); ?>" alt="ownCloud" />
+			</div></header>
+			<?php echo $_['content']; ?>
+		</div>
+		<footer><p class="info"><a href="http://owncloud.org/">ownCloud</a> <?php echo $l->t( 'gives you the freedom to control your own data on the internet' ); ?></p></footer>
+	</body>
+</html>
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
new file mode 100644
index 0000000000000000000000000000000000000000..90b19259292aa0ce1814ec92a8070c0422804d47
--- /dev/null
+++ b/core/templates/layout.user.php
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+	<head>
+		<title>ownCloud</title>
+		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+		<link rel="shortcut icon" href="<?php echo image_path('', 'favicon.png'); ?>" /><link rel="apple-touch-icon-precomposed" href="<?php echo image_path('', 'favicon-touch.png'); ?>" />
+		<?php foreach($_['cssfiles'] as $cssfile): ?>
+			<link rel="stylesheet" href="<?php echo $cssfile; ?>" type="text/css" media="screen" />
+		<?php endforeach; ?>
+		<script type="text/javascript">
+			var oc_webroot = '<?php global $WEBROOT; echo $WEBROOT; ?>';
+			var oc_current_user = '<?php echo OC_User::getUser() ?>';
+		</script>
+		<?php foreach($_['jsfiles'] as $jsfile): ?>
+			<script type="text/javascript" src="<?php echo $jsfile; ?>"></script>
+		<?php endforeach; ?>
+		<?php foreach($_['headers'] as $header): ?>
+			<?php
+				echo '<'.$header['tag'].' ';
+				foreach($header['attributes'] as $name=>$value){
+					echo "$name='$value' ";
+				};
+				echo '/>';
+			?>
+		<?php endforeach; ?>
+	</head>
+
+	<body id="<?php echo $_['bodyid'];?>">
+		<header><div id="header">
+			<a href="<?php echo link_to('', 'index.php'); ?>" title="" id="owncloud"><img class="svg" src="<?php echo image_path('', 'logo-wide.svg'); ?>" alt="ownCloud" /></a>
+			<form class="searchbox" action="#" method="post">
+				<input id="searchbox" class="svg" type="search" name="query" value="<?php if(isset($_POST['query'])){echo $_POST['query'];};?>" autocomplete="off" />
+			</form>
+			<a id="logout" href="<?php echo link_to('', 'index.php'); ?>?logout=true"><img class="svg" alt="<?php echo $l->t('Log out');?>" src="<?php echo image_path('', 'actions/logout.svg'); ?>" /></a>
+		</div></header>
+
+		<nav><div id="navigation">
+			<ul id="apps" class="svg">
+				<?php foreach($_['navigation'] as $entry): ?>
+					<li><a style="background-image:url(<?php echo $entry['icon']; ?>)" href="<?php echo $entry['href']; ?>" title="" <?php if( $entry['active'] ): ?> class="active"<?php endif; ?>><?php echo $entry['name']; ?></a>
+					</li>
+				<?php endforeach; ?>
+			</ul>
+
+			<ul id="settings" class="svg">
+				<img id="expand" class="svg" alt="<?php echo $l->t('Settings');?>" src="<?php echo image_path('', 'actions/settings.svg'); ?>" />
+				<span style="display:none"><?php echo $l->t('Settings');?></span>
+				<div id="expanddiv">
+				<?php foreach($_['settingsnavigation'] as $entry):?>
+					<li><a style="background-image:url(<?php echo $entry['icon']; ?>)" href="<?php echo $entry['href']; ?>" title="" <?php if( $entry["active"] ): ?> class="active"<?php endif; ?>><?php echo $entry['name'] ?></a></li>
+				<?php endforeach; ?>
+				</div>
+			</ul>
+		</div></nav>
+
+		<div id="content">
+			<?php echo $_['content']; ?>
+		</div>
+	</body>
+</html>
diff --git a/core/templates/login.php b/core/templates/login.php
new file mode 100644
index 0000000000000000000000000000000000000000..717f6bcabdaf8cb7b42ffc8e1fe3b5668e0bdc1b
--- /dev/null
+++ b/core/templates/login.php
@@ -0,0 +1,17 @@
+<form action="index.php" method="post">
+	<fieldset>
+		<?php if($_['error']): ?>
+			<a href="index.php?lostpassword"><?php echo $l->t('Lost your password?'); ?></a>
+		<?php endif; ?>
+		<?php if(empty($_["username"])): ?>
+			<input type="text" name="user" id="user" placeholder="Username" value="" autocomplete="off" required autofocus />
+			<input type="password" name="password" id="password" placeholder="Password" value="" required />
+			<input type="checkbox" name="remember_login" id="remember_login" /><label for="remember_login"><?php echo $l->t('remember'); ?></label>
+		<?php else: ?>
+		      <input type="text" name="user" id="user" placeholder="Username" value="<?php echo $_['username']; ?>" autocomplete="off" required >
+		      <input type="password" name="password" id="password" placeholder="Password" value="" required autofocus />
+		      <input type="checkbox" name="remember_login" id="remember_login" checked /><label for="remember_login"><?php echo $l->t('remember'); ?></label>
+		<?php endif; ?>
+		<input type="submit" id="submit" value="Log in" />
+	</fieldset>
+</form>
diff --git a/core/templates/logout.php b/core/templates/logout.php
new file mode 100644
index 0000000000000000000000000000000000000000..8cbbdd9cc8d9b3c759adea07bce7a2986b216b2f
--- /dev/null
+++ b/core/templates/logout.php
@@ -0,0 +1 @@
+<?php echo $l->t( 'You are logged out.' ); ?>
\ No newline at end of file
diff --git a/core/templates/lostpassword.php b/core/templates/lostpassword.php
new file mode 100644
index 0000000000000000000000000000000000000000..67e34164d0815543821e8acf2ec69f02901b27e2
--- /dev/null
+++ b/core/templates/lostpassword.php
@@ -0,0 +1,14 @@
+<form action="index.php?lostpassword" method="post">
+	<fieldset>
+		<?php echo $l->t('You will receive a link to reset your password via Email.'); ?>
+		<?php if ($_['requested']): ?>
+			<?php echo $l->t('Requested'); ?>
+		<?php else: ?>
+			<?php if ($_['error']): ?>
+				<?php echo $l->t('Login failed!'); ?>
+			<?php endif; ?>
+			<input type="text" name="user" id="user" placeholder="<?php echo $l->t('Username or Email'); ?>" value="" autocomplete="off" required autofocus />
+			<input type="submit" id="submit" value="<?php echo $l->t('Request reset'); ?>" />
+		<?php endif; ?>
+	</fieldset>
+</form>
\ No newline at end of file
diff --git a/core/templates/part.pagenavi.php b/core/templates/part.pagenavi.php
new file mode 100644
index 0000000000000000000000000000000000000000..d43023a7822cc3e2e172e8ab8a5e4ceabfe9717e
--- /dev/null
+++ b/core/templates/part.pagenavi.php
@@ -0,0 +1,22 @@
+<ol class="pager">
+	<?php if($_['page']>0):?>
+	<li class="pagerbutton1"><a href="<?php echo $_['url'].($_['page']-1);?>"><?php echo $l->t( 'prev' ); ?></a></li>
+	<?php endif; ?>
+	<?php if ($_['pagestart']>0):?>
+	&hellip;
+	<?php endif;?>
+	<?php for ($i=$_['pagestart']; $i < $_['pagestop'];$i++):?>
+		<?php if ($_['page']!=$i):?>
+		<li><a href="<?php echo $_['url'].$i;?>"><?php echo $i+1;?></a></li>
+		<?php else:?>
+		<li><?php echo $i+1;?></li>
+		<?php endif?>
+	<?php endfor;?>
+	<?php if ($_['pagestop']<$_['pagecount']):?>
+	&hellip;
+	<?php endif;?>
+
+	<?php if(($_['page']+1)<$_['pagecount']):?>
+	<li class="pagerbutton2"><a href="<?php echo $_['url'].($_['page']+1);?>"><?php echo $l->t( 'next' ); ?></a></li>
+	<?php endif; ?>
+</ol>
diff --git a/core/templates/resetpassword.php b/core/templates/resetpassword.php
new file mode 100644
index 0000000000000000000000000000000000000000..47d0e69b7d3c5421e64da5af126c6f1e58fe8107
--- /dev/null
+++ b/core/templates/resetpassword.php
@@ -0,0 +1,10 @@
+<form action="<?php echo "index.php?".$_SERVER['QUERY_STRING']; ?>" method="post">
+	<fieldset>
+		<?php if($_['success']): ?>
+			<?php echo $l->t('Your password was successfully reset'); ?>
+		<?php else: ?>
+			<input type="password" name="password" id="password" placeholder="<?php echo $l->t('New password'); ?>" value="" required />
+			<input type="submit" id="submit" value="<?php echo $l->t('Reset password'); ?>" />
+		<?php endif; ?>
+	</fieldset>
+</form>
\ No newline at end of file
diff --git a/db_structure.xml b/db_structure.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ddb8c44d19dbb888a9510fe84507d7b30544e01b
--- /dev/null
+++ b/db_structure.xml
@@ -0,0 +1,484 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<database>
+
+ <name>*dbname*</name>
+ <create>true</create>
+ <overwrite>false</overwrite>
+
+ <charset>utf8</charset>
+
+ <table>
+
+  <name>*dbprefix*appconfig</name>
+
+  <declaration>
+
+   <field>
+    <name>appid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>configkey</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>configvalue</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*foldersize</name>
+
+  <declaration>
+
+   <field>
+    <name>path</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>512</length>
+   </field>
+
+   <field>
+    <name>size</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>4</length>
+   </field>
+
+   <index>
+    <name>path_index</name>
+    <field>
+     <name>path</name>
+     <sorting>ascending</sorting>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*group_user</name>
+
+  <declaration>
+
+   <field>
+    <name>gid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+   <field>
+    <name>uid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*groups</name>
+
+  <declaration>
+
+   <field>
+    <name>gid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+   <index>
+    <name>groups_pKey</name>
+    <primary>true</primary>
+    <field>
+     <name>gid</name>
+     <sorting>ascending</sorting>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*locks</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>userid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>200</length>
+   </field>
+
+   <field>
+    <name>owner</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>timeout</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>created</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>token</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>100</length>
+   </field>
+
+   <field>
+    <name>scope</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>1</length>
+   </field>
+
+   <field>
+    <name>depth</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>1</length>
+   </field>
+
+   <field>
+    <name>uri</name>
+    <type>clob</type>
+    <notnull>false</notnull>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*log</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>moment</name>
+    <type>timestamp</type>
+    <default>0000-00-00 00:00:00</default>
+    <notnull>true</notnull>
+   </field>
+
+   <field>
+    <name>appid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>user</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>action</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>info</name>
+    <type>clob</type>
+    <notnull>true</notnull>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*preferences</name>
+
+  <declaration>
+
+   <field>
+    <name>userid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>appid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>configkey</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>configvalue</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*principalgroups</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>principal_id</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>member_id</name>
+    <type>integer</type>
+    <default></default>
+    <notnull>true</notnull>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <index>
+    <name>principals_members_index</name>
+    <unique>true</unique>
+    <field>
+     <name>principal_id</name>
+     <sorting>ascending</sorting>
+    </field>
+    <field>
+     <name>member_id</name>
+     <sorting>ascending</sorting>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*principals</name>
+
+  <declaration>
+
+   <field>
+    <name>id</name>
+    <type>integer</type>
+    <default>0</default>
+    <notnull>true</notnull>
+    <autoincrement>1</autoincrement>
+    <unsigned>true</unsigned>
+    <length>4</length>
+   </field>
+
+   <field>
+    <name>uri</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>displayname</name>
+    <type>text</type>
+    <default></default>
+    <notnull>false</notnull>
+    <length>255</length>
+   </field>
+
+   <index>
+    <name>uri</name>
+    <unique>true</unique>
+    <field>
+     <name>uri</name>
+     <sorting>ascending</sorting>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+ 
+ <table>
+
+  <name>*dbprefix*properties</name>
+
+  <declaration>
+
+   <field>
+    <name>userid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>200</length>
+   </field>
+
+   <field>
+    <name>propertypath</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>propertyname</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <field>
+    <name>propertyvalue</name>
+    <type>clob</type>
+    <notnull>true</notnull>
+   </field>
+
+  </declaration>
+
+ </table>
+
+ <table>
+
+  <name>*dbprefix*users</name>
+
+  <declaration>
+
+   <field>
+    <name>uid</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>64</length>
+   </field>
+
+   <field>
+    <name>password</name>
+    <type>text</type>
+    <default></default>
+    <notnull>true</notnull>
+    <length>255</length>
+   </field>
+
+   <index>
+    <name>users_pKey</name>
+    <primary>true</primary>
+    <field>
+     <name>uid</name>
+     <sorting>ascending</sorting>
+    </field>
+   </index>
+
+  </declaration>
+
+ </table>
+
+</database>
diff --git a/files/admin.php b/files/admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..db47bc7c033584cf5fa40f319a99dc0d4580dcce
--- /dev/null
+++ b/files/admin.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+
+// Init owncloud
+require_once('../lib/base.php');
+
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	header( "Location: ".OC_Helper::linkTo( "files", "index.php" ));
+	exit();
+}
+
+$htaccessWorking=(getenv('htaccessWorking')=='true');
+if(isset($_POST['maxUploadSize'])){
+	$maxUploadFilesize=$_POST['maxUploadSize'];
+	OC_Files::setUploadLimit(OC_Helper::computerFileSize($maxUploadFilesize));
+}else{
+	$maxUploadFilesize = ini_get('upload_max_filesize').'B';
+}
+
+OC_App::setActiveNavigationEntry( "files_administration" );
+// return template
+$tmpl = new OC_Template( "files", "admin", "user" );
+$tmpl->assign( 'htaccessWorking', $htaccessWorking );
+$tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
+$tmpl->printPage();
+
+?>
diff --git a/files/ajax/autocomplete.php b/files/ajax/autocomplete.php
new file mode 100644
index 0000000000000000000000000000000000000000..183ee86c7889d119c5fc1c586de95f673c6112ef
--- /dev/null
+++ b/files/ajax/autocomplete.php
@@ -0,0 +1,63 @@
+<?php
+//provide auto completion of paths for use with jquer ui autocomplete
+
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+// header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Get data
+$query = $_GET['term'];
+$dirOnly=(isset($_GET['dironly']))?($_GET['dironly']=='true'):false;
+
+if($query[0]!='/'){
+	$query='/'.$query;
+}
+
+if(substr($query,-1,1)=='/'){
+	$base=$query;
+}else{
+	$base=dirname($query);
+}
+
+$query=substr($query,strlen($base));
+
+if($base!='/'){
+	$query=substr($query,1);
+}
+$queryLen=strlen($query);
+$query=strtolower($query);
+
+// echo "$base - $query";
+
+$files=array();
+
+if(OC_Filesystem::file_exists($base) and OC_Filesystem::is_dir($base)){
+	$dh = OC_Filesystem::opendir($base);
+	if($dh){
+		if(substr($base,-1,1)!='/'){
+			$base=$base.'/';
+		}
+		while (($file = readdir($dh)) !== false) {
+			if ($file != "." && $file != ".."){
+				if(substr(strtolower($file),0,$queryLen)==$query){
+					$item=$base.$file;
+					if((!$dirOnly or OC_Filesystem::is_dir($item))){
+						$files[]=(object)array('id'=>$item,'label'=>$item,'name'=>$item);
+					}
+				}
+			}
+		}
+	}
+}
+echo json_encode($files);
+
+?>
diff --git a/files/ajax/delete.php b/files/ajax/delete.php
new file mode 100644
index 0000000000000000000000000000000000000000..782db215dfc2ef74eefd0ec32203efacbb357c4c
--- /dev/null
+++ b/files/ajax/delete.php
@@ -0,0 +1,36 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Get data
+$dir = $_GET["dir"];
+$files = isset($_GET["file"]) ? $_GET["file"] : $_GET["files"];
+
+$files = explode(';', $files);
+$filesWithError = '';
+$status = 'success';
+//Now delete
+foreach($files as $file) {
+    if( !OC_Files::delete( $dir, $file )){
+		$filesWithError .= $file . "\n";
+		$status = 'error';
+	}
+}
+
+if($status == 'success') {
+	echo json_encode( array( "status" => $status, "data" => array( "dir" => $dir, "files" => $files )));
+} else {
+	echo json_encode( array( "status" => $status, "data" => array( "message" => "Could not delete:\n" . $filesWithError )));
+}
+
+?>
diff --git a/files/ajax/download.php b/files/ajax/download.php
new file mode 100644
index 0000000000000000000000000000000000000000..4c756f9b1905570f7900cea78a3a9f15a59beff1
--- /dev/null
+++ b/files/ajax/download.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+	exit();
+}
+
+$files = $_GET["files"];
+$dir = $_GET["dir"];
+
+OC_Files::get($dir,$files);
+?>
diff --git a/files/ajax/list.php b/files/ajax/list.php
new file mode 100644
index 0000000000000000000000000000000000000000..547bc91fb052ab1f1ca8e5bdc99c6ef36baa9bf6
--- /dev/null
+++ b/files/ajax/list.php
@@ -0,0 +1,50 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Load the files
+$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+$doBreadcrumb = isset( $_GET['breadcrumb'] ) ? true : false;
+$data = array();
+
+// Make breadcrumb
+if($doBreadcrumb){
+	$breadcrumb = array();
+	$pathtohere = "/";
+	foreach( explode( "/", $dir ) as $i ){
+		if( $i != "" ){
+			$pathtohere .= "$i/";
+			$breadcrumb[] = array( "dir" => $pathtohere, "name" => $i );
+		}
+	}
+	
+	$breadcrumbNav = new OC_Template( "files", "part.breadcrumb", "" );
+	$breadcrumbNav->assign( "breadcrumb", $breadcrumb );
+	
+	$data['breadcrumb'] = $breadcrumbNav->fetchPage();
+}
+
+// make filelist
+$files = array();
+foreach( OC_Files::getdirectorycontent( $dir ) as $i ){
+	$i["date"] = OC_Util::formatDate($i["mtime"] );
+	$files[] = $i;
+}
+
+$list = new OC_Template( "files", "part.list", "" );
+$list->assign( "files", $files );
+$data = array('files' => $list->fetchPage());
+
+echo json_encode( array( "status" => "success", "data" => $data));
+
+?>
diff --git a/files/ajax/move.php b/files/ajax/move.php
new file mode 100644
index 0000000000000000000000000000000000000000..4224cbce6d0b4f9d282b98cd9d8ae0de99581ed0
--- /dev/null
+++ b/files/ajax/move.php
@@ -0,0 +1,27 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Get data
+$dir = $_GET["dir"];
+$file = $_GET["file"];
+$target = $_GET["target"];
+
+
+if(OC_Files::move($dir,$file,$target,$file)){
+	echo json_encode( array( "status" => 'success', "data" => array( "dir" => $dir, "files" => $file )));
+}else{
+	echo json_encode( array( "status" => 'error', "data" => array( "message" => "Could move $file" )));
+}
+
+?>
\ No newline at end of file
diff --git a/files/ajax/newfolder.php b/files/ajax/newfolder.php
new file mode 100644
index 0000000000000000000000000000000000000000..610418583bd680bfb334047ef59f506fb4f4a7dd
--- /dev/null
+++ b/files/ajax/newfolder.php
@@ -0,0 +1,29 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Get the params
+$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+$foldername = isset( $_GET['foldername'] ) ? $_GET['foldername'] : '';
+
+if($foldername == '') {
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Empty Foldername" )));
+	exit();
+}
+error_log('try to create ' . $foldername . ' in ' . $dir);
+if(OC_Files::newFile($dir, $foldername, 'dir')) {
+	echo json_encode( array( "status" => "success", "data" => array()));
+	exit();
+}
+
+echo json_encode( array( "status" => "error", "data" => array( "message" => "Error when creating the folder" )));
\ No newline at end of file
diff --git a/files/ajax/rename.php b/files/ajax/rename.php
new file mode 100644
index 0000000000000000000000000000000000000000..516077f6fda08b3d6799b9cead6e07515316b5ff
--- /dev/null
+++ b/files/ajax/rename.php
@@ -0,0 +1,28 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Get data
+$dir = $_GET["dir"];
+$file = $_GET["file"];
+$newname = $_GET["newname"];
+
+// Delete
+if( OC_Files::move( $dir, $file, $dir, $newname )) {
+	echo json_encode( array( "status" => "success", "data" => array( "dir" => $dir, "file" => $file, "newname" => $newname )));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to rename file" )));
+}
+
+?>
diff --git a/files/ajax/timezone.php b/files/ajax/timezone.php
new file mode 100644
index 0000000000000000000000000000000000000000..93d06611a0d977fb830ad576feca1a0faaf632a7
--- /dev/null
+++ b/files/ajax/timezone.php
@@ -0,0 +1,4 @@
+<?php
+	session_start();
+	$_SESSION['timezone'] = $_GET['time'];
+?>
\ No newline at end of file
diff --git a/files/ajax/upload.php b/files/ajax/upload.php
new file mode 100644
index 0000000000000000000000000000000000000000..c642b0ded1cf8b2910d4a880c93a607583b6fe5d
--- /dev/null
+++ b/files/ajax/upload.php
@@ -0,0 +1,49 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+// header( "Content-Type: application/json" );
+// Firefox and Konqueror tries to download application/json for me.  --Arthur
+header( "Content-Type: text/plain" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$files=$_FILES['files'];
+
+$dir = $_POST['dir'];
+$dir .= '/';
+$error='';
+
+$totalSize=0;
+foreach($files['size'] as $size){
+	$totalSize+=$size;
+}
+if($totalSize>OC_Filesystem::free_space('/')){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Not enough space available" )));
+	exit();
+}
+
+$result=array();
+if(strpos($dir,'..') === false){
+	$fileCount=count($files['name']);
+	for($i=0;$i<$fileCount;$i++){
+		$target=stripslashes($dir) . $files['name'][$i];
+		if(OC_Filesystem::fromUploadedFile($files['tmp_name'][$i],$target)){
+			$result[]=array( "status" => "success", 'mime'=>OC_Filesystem::getMimeType($target),'size'=>OC_Filesystem::filesize($target),'name'=>$files['name'][$i]);
+		}
+	}
+	echo json_encode($result);
+	exit();
+}else{
+	$error='invalid dir';
+}
+
+echo json_encode(array( 'status' => 'error', 'data' => array('error' => $error, "file" => $fileName)));
+
+?>
diff --git a/files/appinfo/app.php b/files/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..e8b2f0c31694a920e6973e7c3c530ebf9533281b
--- /dev/null
+++ b/files/appinfo/app.php
@@ -0,0 +1,9 @@
+<?php
+
+$l=new OC_L10N('files');
+
+OC_App::register( array( "order" => 2, "id" => "files", "name" => "Files" ));
+
+OC_App::addNavigationEntry( array( "id" => "files_index", "order" => 1, "href" => OC_Helper::linkTo( "files", "index.php" ), "icon" => OC_Helper::imagePath( "core", "places/home.svg" ), "name" => $l->t("Files") ));
+
+?>
diff --git a/files/css/files.css b/files/css/files.css
new file mode 100644
index 0000000000000000000000000000000000000000..27ead667dc4423344b7470778655d5248bd25feb
--- /dev/null
+++ b/files/css/files.css
@@ -0,0 +1,66 @@
+/* Copyright (c) 2011, Jan-Christoph Borchardt
+ This file is licensed under the Affero General Public License version 3 or later.
+ See the COPYING-README file. */
+
+/* FILE MENU */
+.actions { padding:.3em; float:left; }
+.actions input { margin:0; }
+#file_menu { right:0; position:absolute; top:0; }
+#file_menu a { display:block; float:left; background-image:none; text-decoration:none; }
+.file_upload_form, #file_newfolder_form { display:inline; float: left;}
+#fileSelector, #file_upload_submit, #file_newfolder_submit { display:none; }
+.file_upload_wrapper, #file_newfolder_name { background-repeat:no-repeat; background-position:.5em .5em; padding-left:2em; }
+.file_upload_wrapper { font-weight:bold; display:-moz-inline-box; /* fallback for older firefox versions*/ display:inline-block; padding-left:0; overflow:hidden; position:relative; margin:.1em 1em;}
+.file_upload_wrapper .file_upload_button_wrapper { position:absolute; top:0; left:0; width:100%; height:100%; cursor:pointer; z-index:1000; }
+
+#file_newfolder_name { background-image:url('../../core/img/places/folder.svg'); font-weight:normal; width:7em; }
+.file_upload_start, .file_upload_filename { font-size:1em; }
+#file_newfolder_submit, #file_upload_submit { width:3em; }
+.file_upload_target { display:none; }
+
+.file_upload_start { opacity:0; filter:alpha(opacity=0); z-index:1; position:absolute; left:0; top:0; width:100%; cursor:pointer;}
+.file_upload_filename { z-index:100; cursor:pointer;}
+
+.file_upload_form, .file_upload_wrapper, .file_upload_start, .file_upload_filename, #file_upload_submit { cursor:pointer; }
+
+/* FILE TABLE */
+#emptyfolder { position:absolute; margin:10em 0 0 10em; font-size:1.5em; font-weight:bold; color:#888; text-shadow:#fff 0 1px 0; }
+table { position:relative; top:37px; width:100%; }
+tbody tr:hover, tbody tr:active, tbody tr.selected { background-color:#f8f8f8; height:1em; }
+tbody tr.selected { background-color:#eee; }
+tbody a { color:#000; }
+span.extention, td.date { color:#999; }
+span.extention { opacity:0; -webkit-transition:opacity 500ms; -moz-transition:opacity 500ms; -o-transition:opacity 500ms; transition:opacity 500ms; }
+tr:hover span.extention { opacity:1; }
+div.crumb { float:left; display:block; background:no-repeat right 0; padding:.75em 1.5em 0 1em; height:2.9em; }
+div.crumb:first-child { padding-left:1.5em; }
+div.crumb:last-child { font-weight:bold; }
+table tr.mouseOver td { background-color:#eee; }
+table th { height:2em; padding:0 .5em; color:#999; }
+table th .name { float:left; margin-left:.5em; }
+table th.multiselect { background:#ddd; color:#000; font-weight:bold; }
+table th, table td { border-bottom:1px solid #ddd; text-align:left; font-weight:normal; }
+table td { border-bottom:1px solid #eee; font-style:normal; background-position:1em .5em; background-repeat:no-repeat; }
+table th#headerSize, table td.filesize { width:3em; padding:0 1em; text-align:right; }
+table th#headerDate, table td.date { width:11em; padding:0 .1em 0 1em; text-align:left; }
+table td.selection, table th.selection, table td.fileaction { width:2em; text-align:center; }
+table td.filename a.name { display:block; height:1.5em; vertical-align:middle; margin-left:3em; }
+table tr[data-type="dir"] td.filename a.name {font-weight:bold; }
+table td.filename a.name input, table td.filename a.name form { width:100%; cursor:text; }
+table td.filename a, table td.login, table td.logout, table td.download, table td.upload, table td.create, table td.delete { padding:.2em .5em .5em 0; }
+table td.filename .nametext, .modified { float:left; padding:.3em 0; }
+table td.filename .nametext { width:60%; }
+table td.filename form { float:left; font-size:.85em; }
+table thead.fixed tr{ position:fixed; top:6.5em; z-index:49; -moz-box-shadow:0 -3px 7px #ddd; -webkit-box-shadow:0 -3px 7px #ddd; box-shadow:0 -3px 7px #ddd; }
+table thead.fixed { height:2em; }
+#fileList tr td.filename>input[type=checkbox]:first-child { opacity:0; float:left; margin:.7em 0 0 1em; /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ -webkit-transition:opacity 500ms; -moz-transition:opacity 500ms; -o-transition:opacity 500ms; transition:opacity 500ms; }
+#fileList tr:hover td.filename>input[type="checkbox"]:first-child { opacity:.8; }
+#fileList tr td.filename>input[type="checkbox"]:checked:first-child { opacity:1; }
+#fileList tr td.filename { -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms; }
+#select_all { float:left; margin:.3em 0.6em 0 .5em; }
+#uploadsize-message,#delete-confirm { display:none; }
+.selectedActions a, a.action { float:right; display:inline; margin:0 .5em; padding:.3em .3em 0 .3em !important; }
+.selectedActions { display:none; }
+
+/* add breadcrumb divider to the File item in navigation panel */
+#navigation>ul>li:first-child { background:url('../../core/img/breadcrumb-start.svg') no-repeat 12.5em 0px; width:12.5em; padding-right:1em; }
diff --git a/files/download.php b/files/download.php
new file mode 100644
index 0000000000000000000000000000000000000000..ccd3eb43d8e7bac556c2d274823ab5f624f671f5
--- /dev/null
+++ b/files/download.php
@@ -0,0 +1,54 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+// Init owncloud
+require_once('../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+	exit();
+}
+
+$filename = $_GET["file"];
+
+if(!OC_Filesystem::file_exists($filename)){
+	header("HTTP/1.0 404 Not Found");
+	$tmpl = new OC_Template( '', '404', 'guest' );
+	$tmpl->assign('file',$filename);
+	$tmpl->printPage();
+	exit;
+}
+
+$ftype=OC_Filesystem::getMimeType( $filename );
+
+header('Content-Type:'.$ftype);
+header('Content-Disposition: attachment; filename="'.basename($filename).'"');
+header('Expires: 0');
+header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+header('Pragma: public');
+header('Content-Length: '.OC_Filesystem::filesize($filename));
+
+ob_end_clean();
+OC_Filesystem::readfile( $filename );
+?>
diff --git a/files/index.php b/files/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..3d62c3385faf60c64ea8e20dde8be3f82cd41544
--- /dev/null
+++ b/files/index.php
@@ -0,0 +1,95 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+
+// Init owncloud
+require_once('../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+// Load the files we need
+OC_Util::addStyle( "files", "files" );
+OC_Util::addScript( "files", "files" );
+OC_Util::addScript( 'files', 'filelist' );
+OC_Util::addScript( 'files', 'fileactions' );
+if(!isset($_SESSION['timezone'])){
+	OC_Util::addScript( 'files', 'timezone' );
+}
+OC_App::setActiveNavigationEntry( "files_index" );
+// Load the files
+$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+
+$files = array();
+foreach( OC_Files::getdirectorycontent( $dir ) as $i ){
+	$i["date"] = OC_Util::formatDate($i["mtime"] );
+	if($i['type']=='file'){
+		$fileinfo=pathinfo($i['name']);
+		$i['basename']=$fileinfo['filename'];
+		if (!empty($fileinfo['extension'])) {
+			$i['extention']='.' . $fileinfo['extension'];
+		}
+		else {
+			$i['extention']='';
+		}
+	}
+	if($i['directory']=='/'){
+		$i['directory']='';
+	}
+	$files[] = $i;
+}
+
+// Make breadcrumb
+$breadcrumb = array();
+$pathtohere = "";
+foreach( explode( "/", $dir ) as $i ){
+	if( $i != "" ){
+		$pathtohere .= "/$i";
+		$breadcrumb[] = array( "dir" => $pathtohere, "name" => $i );
+	}
+}
+
+// make breadcrumb und filelist markup
+$list = new OC_Template( "files", "part.list", "" );
+$list->assign( "files", $files );
+$list->assign( "baseURL", OC_Helper::linkTo("files", "index.php?dir="));
+$list->assign( "downloadURL", OC_Helper::linkTo("files", "download.php?file="));
+$breadcrumbNav = new OC_Template( "files", "part.breadcrumb", "" );
+$breadcrumbNav->assign( "breadcrumb", $breadcrumb );
+$breadcrumbNav->assign( "baseURL", OC_Helper::linkTo("files", "index.php?dir="));
+
+$maxUploadFilesize = OC_Helper::computerFileSize(ini_get('upload_max_filesize'));
+
+$tmpl = new OC_Template( "files", "index", "user" );
+$tmpl->assign( "fileList", $list->fetchPage() );
+$tmpl->assign( "breadcrumb", $breadcrumbNav->fetchPage() );
+$tmpl->assign( 'dir', $dir);
+$tmpl->assign( "files", $files );
+$tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize);
+$tmpl->assign( 'uploadMaxHumanFilesize', OC_Helper::humanFileSize($maxUploadFilesize));
+$tmpl->printPage();
+
+?>
diff --git a/files/js/admin.js b/files/js/admin.js
new file mode 100644
index 0000000000000000000000000000000000000000..5cbb2b9f5ac5aa163ff3bad64eb3629ecd177574
--- /dev/null
+++ b/files/js/admin.js
@@ -0,0 +1,15 @@
+function switchPublicFolder()  
+{
+        var publicEnable = $('#publicEnable').is(':checked');
+        var sharingaimGroup = $('input:radio[name=sharingaim]'); //find all radiobuttons of that group
+        $.each(sharingaimGroup, function(index, sharingaimItem) {
+                sharingaimItem.disabled = !publicEnable;	 //set all buttons to the correct state
+        });
+}
+
+$(document).ready(function(){
+       switchPublicFolder(); // Execute the function after loading DOM tree
+       $('#publicEnable').click(function(){
+               switchPublicFolder(); // To get rid of onClick()
+       });
+});
diff --git a/files/js/fileactions.js b/files/js/fileactions.js
new file mode 100644
index 0000000000000000000000000000000000000000..4ff8562fef3bcbf3af21f0b6bced2d8bf8042ee5
--- /dev/null
+++ b/files/js/fileactions.js
@@ -0,0 +1,145 @@
+FileActions={
+	actions:{},
+	defaults:{},
+	icons:{},
+	currentFile:null,
+	register:function(mime,name,icon,action){
+		if(!FileActions.actions[mime]){
+			FileActions.actions[mime]={};
+		}
+		FileActions.actions[mime][name]=action;
+		FileActions.icons[name]=icon;
+	},
+	setDefault:function(mime,name){
+		FileActions.defaults[mime]=name;
+	},
+	get:function(mime,type){
+		var actions={};
+		if(FileActions.actions.all){
+			actions=$.extend( actions, FileActions.actions.all )
+		}
+		if(mime){
+			if(FileActions.actions[mime]){
+				actions=$.extend( actions, FileActions.actions[mime] )
+			}
+			var mimePart=mime.substr(0,mime.indexOf('/'));
+			if(FileActions.actions[mimePart]){
+				actions=$.extend( actions, FileActions.actions[mimePart] )
+			}
+		}
+		if(type){//type is 'dir' or 'file'
+			if(FileActions.actions[type]){
+				actions=$.extend( actions, FileActions.actions[type] )
+			}
+		}
+		return actions;
+	},
+	getDefault:function(mime,type){
+		if(mime){
+			var mimePart=mime.substr(0,mime.indexOf('/'));
+		}
+		var name=false;
+		if(mime && FileActions.defaults[mime]){
+			name=FileActions.defaults[mime];
+		}else if(mime && FileActions.defaults[mimePart]){
+			name=FileActions.defaults[mimePart];
+		}else if(type && FileActions.defaults[type]){
+			name=FileActions.defaults[type];
+		}else{
+			name=FileActions.defaults.all;
+		}
+		var actions=this.get(mime,type);
+		return actions[name];
+	},
+	display:function(parent){
+		FileActions.currentFile=parent;
+		$('.action').remove();
+		var actions=FileActions.get(FileActions.getCurrentMimeType(),FileActions.getCurrentType());
+		var file=FileActions.getCurrentFile();
+		if($('tr[data-file="'+file+'"]').data('renaming')){
+			return;
+		}
+		var defaultAction=FileActions.getDefault(FileActions.getCurrentMimeType(),FileActions.getCurrentType());
+		for(name in actions){
+			if((name=='Download' || actions[name]!=defaultAction) && name!='Delete'){
+				var img=FileActions.icons[name];
+				if(img.call){
+					img=img(file);
+				}
+				var html='<a href="#" title="'+name+'" class="action" />';
+				var element=$(html);
+				if(img){
+					element.append($('<img src="'+img+'"/>'));
+				}
+				element.data('action',name);
+				element.click(function(event){
+					event.stopPropagation();
+					event.preventDefault();
+					var action=actions[$(this).data('action')];
+					var currentFile=FileActions.getCurrentFile();
+					FileActions.hide();
+					action(currentFile);
+				});
+				parent.children('a.name').append(element);
+			}
+		}
+		if(actions['Delete']){
+			var img=FileActions.icons['Delete'];
+			if(img.call){
+				img=img(file);
+			}
+			var html='<a href="#" title="Delete" class="action" />';
+			var element=$(html);
+			if(img){
+				element.append($('<img src="'+img+'"/>'));
+			}
+			element.data('action','Delete');
+			element.click(function(event){
+				event.stopPropagation();
+				event.preventDefault();
+				var action=actions[$(this).data('action')];
+				var currentFile=FileActions.getCurrentFile();
+				FileActions.hide();
+				action(currentFile);
+			});
+			parent.parent().children().last().append(element);
+		}
+		$('.action').hide();
+		$('.action').fadeIn(200);
+		return false;
+	},
+	hide:function(){
+		$('.action').fadeOut(200,function(){
+			$(this).remove();
+		});
+	},
+	getCurrentFile:function(){
+		return FileActions.currentFile.parent().attr('data-file');
+	},
+	getCurrentMimeType:function(){
+		return FileActions.currentFile.parent().attr('data-mime');
+	},
+	getCurrentType:function(){
+		return FileActions.currentFile.parent().attr('data-type');
+	}
+}
+
+FileActions.register('all','Download',function(){return OC.imagePath('core','actions/download')},function(filename){
+	window.location='ajax/download.php?files='+filename+'&dir='+$('#dir').val();
+});
+
+FileActions.register('all','Delete',function(){return OC.imagePath('core','actions/delete')},function(filename){
+	FileList.do_delete(filename);
+});
+
+FileActions.register('all','Rename',function(){return OC.imagePath('core','actions/rename')},function(filename){
+	FileList.rename(filename);
+});
+
+//FileActions.setDefault('all','Download');
+
+FileActions.register('dir','Open','',function(filename){
+	window.location='index.php?dir='+$('#dir').val()+'/'+filename;
+});
+
+FileActions.setDefault('dir','Open');
diff --git a/files/js/filelist.js b/files/js/filelist.js
new file mode 100644
index 0000000000000000000000000000000000000000..ae9e7977c95e156191730618a79126954e6532c6
--- /dev/null
+++ b/files/js/filelist.js
@@ -0,0 +1,214 @@
+FileList={
+	update:function(fileListHtml) {
+		$('#fileList').empty().html(fileListHtml);
+	},
+	addFile:function(name,size,lastModified,loading){
+		var img=(loading)?OC.imagePath('core', 'loading.gif'):OC.imagePath('core', 'filetypes/file');
+		var html='<tr data-file="'+name+'" data-type="file" data-size="'+size+'">';
+		if(name.indexOf('.')!=-1){
+			var basename=name.substr(0,name.lastIndexOf('.'));
+			var extention=name.substr(name.lastIndexOf('.'));
+		}else{
+			var basename=name;
+			var extention=false;
+		}
+		html+='<td class="filename" style="background-image:url('+img+')"><input type="checkbox" />';
+		html+='<a class="name" href="download.php?file='+$('#dir').val()+'/'+name+'"><span class="nametext">'+basename
+		if(extention){
+			html+='<span class="extention">'+extention+'</span>';
+		}
+		html+='</span></a></td>';
+		if(size!='Pending'){
+			simpleSize=simpleFileSize(size);
+		}else{
+			simpleSize='Pending';
+		}
+		sizeColor = Math.round(200-size/(1024*1024)*2);
+		lastModifiedTime=Math.round(lastModified.getTime() / 1000);
+		modifiedColor=Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*14);
+		html+='<td class="filesize" title="'+humanFileSize(size)+'" style="color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')">'+simpleSize+'</td>';
+		html+='<td class="date" title="'+formatDate(lastModified)+'" style="color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')">'+relative_modified_date(lastModified.getTime() / 1000)+'</td>';
+		html+='</tr>';
+		FileList.insertElement(name,'file',$(html));
+		if(loading){
+			$('tr[data-file="'+name+'"]').data('loading',true);
+		}else{
+			$('tr[data-file="'+name+'"] td.filename').draggable(dragOptions);
+		}
+	},
+	addDir:function(name,size,lastModified){
+		var html='<tr data-file="'+name+'" data-type="dir" data-size="'+size+'">';
+		html+='<td class="filename" style="background-image:url('+OC.imagePath('core', 'places/folder')+')"><input type="checkbox" /><a class="name" href="index.php?dir='+$('#dir').val()+'/'+name+'">'+name+'</a></td>';
+		if(size!='Pending'){
+			simpleSize=simpleFileSize(size);
+		}else{
+			simpleSize='Pending';
+		}
+		sizeColor = Math.round(200-Math.pow((size/(1024*1024)),2));
+		lastModifiedTime=Math.round(lastModified.getTime() / 1000);
+		modifiedColor=Math.round((Math.round((new Date()).getTime() / 1000)-lastModifiedTime)/60/60/24*5);
+		html+='<td class="filesize" title="'+humanFileSize(size)+'" style="color:rgb('+sizeColor+','+sizeColor+','+sizeColor+')">'+simpleSize+'</td>';
+		html+='<td class="date" title="'+formatDate(lastModified)+'" style="color:rgb('+modifiedColor+','+modifiedColor+','+modifiedColor+')">'+relative_modified_date(lastModified.getTime() / 1000)+'</td>';
+		html+='</tr>';
+		
+		FileList.insertElement(name,'dir',$(html));
+		$('tr[data-file="'+name+'"] td.filename').draggable(dragOptions);
+		$('tr[data-file="'+name+'"] td.filename').droppable(folderDropOptions);
+	},
+	refresh:function(data) {
+		result = jQuery.parseJSON(data.responseText);
+		if(typeof(result.data.breadcrumb) != 'undefined'){
+			updateBreadcrumb(result.data.breadcrumb);
+		}
+		FileList.update(result.data.files);
+		resetFileActionPanel();
+	},
+	remove:function(name){
+		$('tr[data-file="'+name+'"] td.filename').draggable('destroy');
+		$('tr[data-file="'+name+'"]').remove();
+		if($('tr[data-file]').length==0){
+			$('#emptyfolder').show();
+			$('.file_upload_filename').addClass('highlight');
+		}
+	},
+	insertElement:function(name,type,element){
+		//find the correct spot to insert the file or folder
+		var fileElements=$('tr[data-file][data-type="'+type+'"]');
+		var pos;
+		if(name.localeCompare($(fileElements[0]).attr('data-file'))<0){
+			pos=-1;
+		}else if(name.localeCompare($(fileElements[fileElements.length-1]).attr('data-file'))>0){
+			pos=fileElements.length-1;
+		}else{
+			for(var pos=0;pos<fileElements.length-1;pos++){
+				if(name.localeCompare($(fileElements[pos]).attr('data-file'))>0 && name.localeCompare($(fileElements[pos+1]).attr('data-file'))<0){
+					break;
+				}
+			}
+		}
+		if(fileElements.length){
+			if(pos==-1){
+				$(fileElements[0]).before(element);
+			}else{
+				$(fileElements[pos]).after(element);
+			}
+		}else if(type=='dir' && $('tr[data-file]').length>0){
+			$('tr[data-file]').first().before(element);
+		}else{
+			$('#fileList').append(element);
+		}
+		$('#emptyfolder').hide();
+		$('.file_upload_filename').removeClass('highlight');
+	},
+	loadingDone:function(name){
+		$('tr[data-file="'+name+'"]').data('loading',false);
+		var mime=$('tr[data-file="'+name+'"]').data('mime');
+		$('tr[data-file="'+name+'"] td.filename').attr('style','background-image:url('+getMimeIcon(mime)+')');
+		$('tr[data-file="'+name+'"] td.filename').draggable(dragOptions);
+	},
+	isLoading:function(name){
+		return $('tr[data-file="'+name+'"]').data('loading');
+	},
+	rename:function(name){
+		var tr=$('tr[data-file="'+name+'"]');
+		tr.data('renaming',true);
+		var td=tr.children('td.filename');
+		var input=$('<input value="'+name+'" class="filename"></input>');
+		var form=$('<form action="#"></form>')
+		form.append(input);
+		td.children('a.name').text('');
+		td.children('a.name').append(form)
+		input.focus();
+		form.submit(function(event){
+			event.stopPropagation();
+			event.preventDefault();
+			var newname=input.val();
+			tr.data('renaming',false);
+			tr.attr('data-file',newname);
+			td.children('a.name').empty();
+			if(newname.indexOf('.')>0){
+				basename=newname.substr(0,newname.lastIndexOf('.'));
+			}else{
+				basename=newname;
+			}
+			var span=$('<span class="nametext"></span>');
+			span.text(basename);
+			td.children('a.name').append(span);
+			if(newname.indexOf('.')>0){
+				span.append($('<span class="extention">'+newname.substr(newname.lastIndexOf('.'))+'</span>'));
+			}
+			$.ajax({
+				url: 'ajax/rename.php',
+				data: "dir="+$('#dir').val()+"&newname="+encodeURIComponent(newname)+"&file="+encodeURIComponent(name)
+			});
+		});
+		form.click(function(event){
+			event.stopPropagation();
+			event.preventDefault();
+		});
+		input.blur(function(){
+			form.trigger('submit');
+		});
+	},
+	do_delete:function(files){
+		if(FileList.deleteFiles){//finish any ongoing deletes first
+			FileList.finishDelete(function(){
+				FileList.do_delete(files);
+			});
+			return;
+		}
+		if(files.substr){
+			files=[files];
+		}
+		$.each(files,function(index,file){
+			$('tr[data-file="'+file+'"]').hide();
+			$('tr[data-file="'+file+'"]').find('input[type="checkbox"]').removeAttr('checked');
+			$('tr[data-file="'+file+'"]').removeClass('selected');
+		});
+		procesSelection();
+		FileList.deleteCanceled=false;
+		FileList.deleteFiles=files;
+		$('#notification').text(t('files','undo deletion'));
+		$('#notification').fadeIn();
+	},
+	finishDelete:function(ready,sync){
+		if(!FileList.deleteCanceled && FileList.deleteFiles){
+			var fileNames=FileList.deleteFiles.join(';');
+			$.ajax({
+				url: 'ajax/delete.php',
+				async:!sync,
+				data: "dir="+$('#dir').val()+"&files="+encodeURIComponent(fileNames),
+				complete: function(data){
+					boolOperationFinished(data, function(){
+						$('#notification').fadeOut();
+						$.each(FileList.deleteFiles,function(index,file){
+// 							alert(file);
+							FileList.remove(file);
+						});
+						FileList.deleteCanceled=true;
+						FileList.deleteFiles=null;
+						if(ready){
+							ready();
+						}
+					});
+				}
+			});
+		}
+	}
+}
+
+$(document).ready(function(){
+	$('#notification').hide();
+	$('#notification').click(function(){
+		FileList.deleteCanceled=true;
+		$('#notification').fadeOut();
+		$.each(FileList.deleteFiles,function(index,file){
+			$('tr[data-file="'+file+'"]').show();
+// 			alert(file);
+		});
+		FileList.deleteFiles=null;
+	});
+	$(window).bind('beforeunload', function (){
+		FileList.finishDelete(null,true);
+	});
+});
diff --git a/files/js/files.js b/files/js/files.js
new file mode 100644
index 0000000000000000000000000000000000000000..7b37837d9ccd69ef81b4cf834b5ed969568be871
--- /dev/null
+++ b/files/js/files.js
@@ -0,0 +1,476 @@
+$(document).ready(function() {
+	if($('tr[data-file]').length==0){
+		$('.file_upload_filename').addClass('highlight');
+	}
+	
+	$('#file_action_panel').attr('activeAction', false);
+
+	//drag/drop of files
+	$('#fileList tr td.filename').draggable(dragOptions);
+	$('#fileList tr[data-type="dir"] td.filename').droppable(folderDropOptions);
+	$('div.crumb').droppable(crumbDropOptions);
+	$('#plugins>ul>li:first-child').data('dir','');
+	$('#plugins>ul>li:first-child').droppable(crumbDropOptions);
+	
+	// Triggers invisible file input
+	$('.file_upload_button_wrapper').live('click', function() {
+		$(this).parent().children('.file_upload_start').trigger('click');
+		return false;
+	});
+
+	// Sets the file-action buttons behaviour :
+	$('tr').live('mouseenter',function(event) {
+		FileActions.display($(this).children('td.filename'));
+	});
+	$('tr').live('mouseleave',function(event) {
+		FileActions.hide();
+	});
+
+	var lastChecked;
+
+	// Sets the file link behaviour :
+	$('td.filename a').live('click',function(event) {
+		event.preventDefault();
+		if (event.ctrlKey || event.shiftKey) {
+			if (event.shiftKey) {
+				var last = $(lastChecked).parent().parent().prevAll().length;
+				var first = $(this).parent().parent().prevAll().length;
+				var start = Math.min(first, last);
+				var end = Math.max(first, last);
+				var rows = $(this).parent().parent().parent().children('tr');
+				for (var i = start; i < end; i++) {
+					$(rows).each(function(index) {
+						if (index == i) {
+							var checkbox = $(this).children().children('input:checkbox');
+							$(checkbox).attr('checked', 'checked');
+							$(checkbox).parent().parent().addClass('selected');
+						}
+					});
+				}
+			}
+			var checkbox = $(this).parent().children('input:checkbox');
+			lastChecked = checkbox;
+			if ($(checkbox).attr('checked')) {
+				$(checkbox).removeAttr('checked');
+				$(checkbox).parent().parent().removeClass('selected');
+				$('#select_all').removeAttr('checked');
+			} else {
+				$(checkbox).attr('checked', 'checked');
+				$(checkbox).parent().parent().toggleClass('selected');
+				var selectedCount=$('td.filename input:checkbox:checked').length;
+				if (selectedCount == $('td.filename input:checkbox').length) {
+					$('#select_all').attr('checked', 'checked');
+				}
+			}
+			procesSelection();
+		} else {
+			var filename=$(this).parent().parent().data('file');
+			if(!FileList.isLoading(filename)){
+				var mime=$(this).parent().parent().data('mime');
+				var type=$(this).parent().parent().data('type');
+				var action=FileActions.getDefault(mime,type);
+				if(action){
+					action(filename);
+				}
+			}
+		}
+		
+	});
+	
+	// Sets the select_all checkbox behaviour :
+	$('#select_all').click(function() {
+		if($(this).attr('checked')){
+			// Check all
+			$('td.filename input:checkbox').attr('checked', true);
+			$('td.filename input:checkbox').parent().parent().addClass('selected');
+		}else{
+			// Uncheck all
+			$('td.filename input:checkbox').attr('checked', false);
+			$('td.filename input:checkbox').parent().parent().removeClass('selected');
+		}
+		procesSelection();
+	});
+	
+	$('td.filename input:checkbox').live('click',function(event) {
+		if (event.shiftKey) {
+			var last = $(lastChecked).parent().parent().prevAll().length;
+			var first = $(this).parent().parent().prevAll().length;
+			var start = Math.min(first, last);
+			var end = Math.max(first, last);
+			var rows = $(this).parent().parent().parent().children('tr');
+			for (var i = start; i < end; i++) {
+				$(rows).each(function(index) {
+					if (index == i) {
+						var checkbox = $(this).children().children('input:checkbox');
+						$(checkbox).attr('checked', 'checked');
+						$(checkbox).parent().parent().addClass('selected');
+					}
+				});
+			}
+		}
+		var selectedCount=$('td.filename input:checkbox:checked').length;
+		$(this).parent().parent().toggleClass('selected');
+		if(!$(this).attr('checked')){
+			$('#select_all').attr('checked',false);
+		}else{
+			if(selectedCount==$('td.filename input:checkbox').length){
+				$('#select_all').attr('checked',true);
+			}
+		}
+		procesSelection();
+	});
+	
+	$('#file_newfolder_form').submit(function(event) {
+		event.preventDefault();
+		$.ajax({
+			url: 'ajax/newfolder.php',
+			data: "dir="+$('#dir').val()+"&foldername="+$('#file_newfolder_name').val(),
+			complete: function(data){boolOperationFinished(data, function(){
+				var date=new Date();
+				FileList.addDir($('#file_newfolder_name').val(),0,date);
+				$('#file_newfolder_name').val('New Folder');
+				$('#file_newfolder_name').blur();
+			});}
+		});
+	});
+	
+	$('#file_newfolder_name').click(function(){
+		if($('#file_newfolder_name').val() == 'New Folder'){
+			$('#file_newfolder_name').val('');
+		}
+	});
+	
+	$('.download').click('click',function(event) {
+		var files=getSelectedFiles('name').join(';');
+		
+		//send the browser to the download location
+		var dir=$('#dir').val()||'/';
+// 		alert(files);
+		window.location='ajax/download.php?files='+encodeURIComponent(files)+'&dir='+encodeURIComponent(dir);
+		return false;
+	});
+	
+	$('.delete').click(function(event) {
+		var files=getSelectedFiles('name');
+		event.preventDefault();
+		FileList.do_delete(files);
+		return false;
+	});
+
+	$('.file_upload_start').live('change',function(){
+		var form=$(this).parent().parent();
+		var uploadId=form.attr('data-upload-id');
+		var files=this.files;
+		var target=form.children('iframe');
+		var totalSize=0;
+		for(var i=0;i<files.length;i++){
+			totalSize+=files[i].size;
+		}
+		if(totalSize>$('#max_upload').val()){
+			$( "#uploadsize-message" ).dialog({
+				modal: true,
+				buttons: {
+					Close: function() {
+						$( this ).dialog( "close" );
+					}
+				}
+			});
+		}else{
+			target.load(function(){
+				var response=jQuery.parseJSON(target.contents().find('body').text());
+				//set mimetype and if needed filesize
+				if(response){
+					for(var i=0;i<response.length;i++){
+						var file=response[i];
+						$('tr[data-file="'+file.name+'"]').data('mime',file.mime);
+						if(size=='Pending'){
+							$('tr[data-file='+file.name+'] td.filesize').text(file.size);
+						}
+						FileList.loadingDone(file.name);
+					}
+				}
+			});
+			form.submit();
+			var date=new Date();
+			for(var i=0;i<files.length;i++){
+				if(files[i].size>0){
+					var size=files[i].size;
+				}else{
+					var size=t('files','Pending');
+				}
+				FileList.addFile(files[i].name,size,date,true);
+			}
+
+			//clone the upload form and hide the new one to allow users to start a new upload while the old one is still uploading
+			var clone=form.clone();
+			uploadId++;
+			clone.attr('data-upload-id',uploadId);
+			clone.attr('target','file_upload_target_'+uploadId);
+			clone.children('iframe').attr('name','file_upload_target_'+uploadId)
+			clone.insertBefore(form);
+			form.hide();
+		}
+	});
+	
+	//add multiply file upload attribute to all browsers except konqueror (which crashes when it's used)
+	if(navigator.userAgent.search(/konqueror/i)==-1){
+		$('.file_upload_start').attr('multiple','multiple')
+	}
+
+	//if the breadcrumb is to long, start by replacing foldernames with '...' except for the current folder
+	var crumb=$('div.crumb').first();
+	while($('div.controls').height()>40 && crumb.next('div.crumb').length>0){
+		crumb.children('a').text('...');
+		crumb=crumb.next('div.crumb');
+	}
+	//if that isn't enough, start removing items from the breacrumb except for the current folder and it's parent
+	var crumb=$('div.crumb').first();
+	var next=crumb.next('div.crumb');
+	while($('div.controls').height()>40 && next.next('div.crumb').length>0){
+		crumb.remove();
+		crumb=next;
+		next=crumb.next('div.crumb');
+	}
+	//still not enough, start shorting down the current folder name
+	var crumb=$('div.crumb>a').last();
+	while($('div.controls').height()>40 && crumb.text().length>6){
+		var text=crumb.text()
+		text=text.substr(0,text.length-6)+'...';
+		crumb.text(text);
+	}
+});
+
+var adjustNewFolderSize = function() {
+	if($('#file_newfolder_name').val() != '') {
+		splitSize($('#file_newfolder_name'),$('#file_newfolder_submit'));
+		$('#file_newfolder_name').unbind('keyup', adjustNewFolderSize);
+	};
+}
+
+function splitSize(existingEl, appearingEl) {
+	nw = parseInt($(existingEl).css('width')) - parseInt($(appearingEl).css('width'));
+	$(existingEl).css('width', nw + 'px');
+	$(appearingEl).fadeIn(250);
+}
+
+function unsplitSize(stayingEl, vanishingEl) {
+	nw = parseInt($(stayingEl).css('width')) + parseInt($(vanishingEl).css('width'));
+	$(stayingEl).css('width', nw + 'px');
+	$(vanishingEl).fadeOut(250);
+}
+
+function resetFileActionPanel() {
+	$('#file_action_panel form').css({"display":"none"});
+	$('#file_action_panel').attr('activeAction', false);
+}
+
+function boolOperationFinished(data, callback) {
+	result = jQuery.parseJSON(data.responseText);
+	if(result.status == 'success'){
+		callback.call();
+	} else {
+		alert(result.data.message);
+	}
+}
+
+function updateBreadcrumb(breadcrumbHtml) {
+	$('p.nav').empty().html(breadcrumbHtml);
+}
+
+function humanFileSize(bytes){
+	if( bytes < 1024 ){
+		return bytes+' B';
+	}
+	bytes = Math.round(bytes / 1024, 1 );
+	if( bytes < 1024 ){
+		return bytes+' kB';
+	}
+	bytes = Math.round( bytes / 1024, 1 );
+	if( bytes < 1024 ){
+		return bytes+' MB';
+	}
+	
+	// Wow, heavy duty for owncloud
+	bytes = Math.round( bytes / 1024, 1 );
+	return bytes+' GB';
+}
+
+function simpleFileSize(bytes) {
+	mbytes = Math.round(bytes/(1024*1024/10))/10;
+	if(bytes == 0) { return '0'; }
+	else if(mbytes < 0.1) { return '< 0.1'; }
+	else if(mbytes > 1000) { return '> 1000'; }
+	else { return mbytes.toFixed(1); }
+}
+
+function formatDate(date){
+	var monthNames = [ t('files','January'), t('files','February'), t('files','March'), t('files','April'), t('files','May'), t('files','June'),
+	t('files','July'), t('files','August'), t('files','September'), t('files','October'), t('files','November'), t('files','December') ];
+	return monthNames[date.getMonth()]+' '+date.getDate()+', '+date.getFullYear()+', '+((date.getHours()<10)?'0':'')+date.getHours()+':'+date.getMinutes();
+}
+
+
+//options for file drag/dropp
+var dragOptions={
+	distance: 20, revert: 'invalid', opacity: 0.7,
+	stop: function(event, ui) {
+		$('#fileList tr td.filename').addClass('ui-draggable');
+	}
+};
+var folderDropOptions={
+	drop: function( event, ui ) {
+		var file=ui.draggable.text().trim();
+		var target=$(this).text().trim();
+		var dir=$('#dir').val();
+		$.ajax({
+			url: 'ajax/move.php',
+		data: "dir="+dir+"&file="+file+'&target='+dir+'/'+target,
+		complete: function(data){boolOperationFinished(data, function(){
+			var el=$('#fileList tr[data-file="'+file+'"] td.filename');
+			el.draggable('destroy');
+			FileList.remove(file);
+		});}
+		});
+	}
+}
+var crumbDropOptions={
+	drop: function( event, ui ) {
+		var file=ui.draggable.text().trim();
+		var target=$(this).data('dir');
+		var dir=$('#dir').val();
+		while(dir.substr(0,1)=='/'){//remove extra leading /'s
+				dir=dir.substr(1);
+		}
+		dir='/'+dir;
+		if(dir.substr(-1,1)!='/'){
+			dir=dir+'/';
+		}
+		if(target==dir){
+			return;
+		}
+		$.ajax({
+			url: 'ajax/move.php',
+		 data: "dir="+dir+"&file="+file+'&target='+target,
+		 complete: function(data){boolOperationFinished(data, function(){
+			 FileList.remove(file);
+		 });}
+		});
+	},
+	tolerance: 'pointer'
+}
+
+function procesSelection(){
+	var selected=getSelectedFiles();
+	var selectedFiles=selected.filter(function(el){return el.type=='file'});
+	var selectedFolders=selected.filter(function(el){return el.type=='dir'});
+	if(selectedFiles.length==0 && selectedFolders.length==0){
+		$('#headerName>span.name').text(t('files','Name'));
+		$('#headerSize').text(t('files','Size'));
+		$('#modified').text(t('files','Modified'));
+		$('th').removeClass('multiselect');
+		$('.selectedActions').hide();
+		$('thead').removeClass('fixed');
+		$('#headerName').css('width','auto');
+		$('#headerSize').css('width','auto');
+		$('#headerDate').css('width','auto');
+		$('table').css('padding-top','0');
+	}else{
+		var width={name:$('#headerName').css('width'),size:$('#headerSize').css('width'),date:$('#headerDate').css('width')};
+		$('thead').addClass('fixed');
+		$('#headerName').css('width',width.name);
+		$('#headerSize').css('width',width.size);
+		$('#headerDate').css('width',width.date);
+		$('table').css('padding-top','2.1em');
+		$('.selectedActions').show();
+		var totalSize=0;
+		for(var i=0;i<selectedFiles.length;i++){
+			totalSize+=selectedFiles[i].size;
+		};
+		for(var i=0;i<selectedFolders.length;i++){
+			totalSize+=selectedFolders[i].size;
+		};
+		simpleSize=simpleFileSize(totalSize);
+		$('#headerSize').text(simpleSize);
+		$('#headerSize').attr('title',humanFileSize(totalSize));
+		var selection='';
+		if(selectedFolders.length>0){
+			if(selectedFolders.length==1){
+				selection+='1 '+t('files','folder');
+			}else{
+				selection+=selectedFolders.length+' '+t('files','folders');
+			}
+			if(selectedFiles.length>0){
+				selection+=' & ';
+			}
+		}
+		if(selectedFiles.length>0){
+			if(selectedFiles.length==1){
+				selection+='1 '+t('files','file');
+			}else{
+				selection+=selectedFiles.length+' '+t('files','files');
+			}
+		}
+		$('#headerName>span.name').text(selection);
+		$('#modified').text('');
+		$('th').addClass('multiselect');
+	}
+}
+
+/**
+ * @brief get a list of selected files
+ * @param string property (option) the property of the file requested
+ * @return array
+ *
+ * possible values for property: name, mime, size and type
+ * if property is set, an array with that property for each file is returnd
+ * if it's ommited an array of objects with all properties is returned
+ */
+function getSelectedFiles(property){
+	var elements=$('td.filename input:checkbox:checked').parent().parent();
+	var files=[];
+	elements.each(function(i,element){
+		var file={
+			name:$(element).data('file'),
+			mime:$(element).data('mime'),
+			type:$(element).data('type'),
+			size:$(element).data('size'),
+		};
+		if(property){
+			files.push(file[property]);
+		}else{
+			files.push(file);
+		}
+	});
+	return files;
+}
+
+function relative_modified_date(timestamp) {
+	var timediff = Math.round((new Date()).getTime() / 1000) - timestamp;
+	var diffminutes = Math.round(timediff/60);
+	var diffhours = Math.round(diffminutes/60);
+	var diffdays = Math.round(diffhours/24);
+	var diffmonths = Math.round(diffdays/31);
+	var diffyears = Math.round(diffdays/365);
+	if(timediff < 60) { return t('files','seconds ago'); }
+	else if(timediff < 120) { return '1 '+t('files','minute ago'); }
+	else if(timediff < 3600) { return diffminutes+' '+t('files','minutes ago'); }
+	//else if($timediff < 7200) { return '1 hour ago'; }
+	//else if($timediff < 86400) { return $diffhours.' hours ago'; }
+	else if(timediff < 86400) { return t('files','today'); }
+	else if(timediff < 172800) { return t('files','yesterday'); }
+	else if(timediff < 2678400) { return diffdays+' '+t('files','days ago'); }
+	else if(timediff < 5184000) { return t('files','last month'); }
+	//else if($timediff < 31556926) { return $diffmonths.' months ago'; }
+	else if(timediff < 31556926) { return t('files','months ago'); }
+	else if(timediff < 63113852) { return t('files','last year'); }
+	else { return diffyears+' '+t('files','years ago'); }
+}
+
+function getMimeIcon(mime){
+	mime=mime.substr(0,mime.indexOf('/'));
+	var knownMimes=['image','audio'];
+	if(knownMimes.indexOf(mime)==-1){
+		mime='file';
+	}
+	return OC.imagePath('core','filetypes/'+mime);
+}
diff --git a/files/js/timezone.js b/files/js/timezone.js
new file mode 100644
index 0000000000000000000000000000000000000000..d569683f2109491fa5c16a0f124aeac26014d793
--- /dev/null
+++ b/files/js/timezone.js
@@ -0,0 +1,12 @@
+//send the clients time zone to the server
+$(document).ready(function() {
+	var visitortimezone = (-new Date().getTimezoneOffset()/60);
+	$.ajax({
+		type: "GET",
+		url: "ajax/timezone.php",
+		data: 'time='+ visitortimezone,
+		success: function(){
+			location.reload();
+		}
+	});
+});
\ No newline at end of file
diff --git a/files/l10n/ca.php b/files/l10n/ca.php
new file mode 100644
index 0000000000000000000000000000000000000000..14ab8b70a8d3de51d67666bf375c17ceb1f81e12
--- /dev/null
+++ b/files/l10n/ca.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Fitxers",
+"Maximum upload size" => "Mida màxima de pujada",
+"Nothing in here. Upload something!" => "Res per aquí. Pugeu alguna cosa!",
+"Upload" => "Puja",
+"New Folder" => "Carpeta nova",
+"Name" => "Nom",
+"Download" => "Descarrega",
+"Size" => "Mida",
+"Modified" => "Modificat",
+"Delete" => "Esborra",
+"Upload too large" => "La pujada és massa gran",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Els fitxers que esteu intentant pujar excedeixen la mida màxima de pujada d'aquest servidor"
+);
diff --git a/files/l10n/da.php b/files/l10n/da.php
new file mode 100644
index 0000000000000000000000000000000000000000..7bbcc6f8c23cc66b664b05f2868542dfd751f7d7
--- /dev/null
+++ b/files/l10n/da.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Filer",
+"Maximum upload size" => "Maksimal upload-størrelse",
+"Nothing in here. Upload something!" => "Her er tomt. Upload noget!",
+"Upload" => "Upload",
+"New Folder" => "Ny Mappe",
+"Name" => "Navn",
+"Download" => "Download",
+"Size" => "Størrelse",
+"Modified" => "Ændret",
+"Delete" => "Slet",
+"Upload too large" => "Upload for stor",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerne du prøver at uploade er større end den maksimale størrelse for fil-upload på denne server."
+);
diff --git a/files/l10n/de.php b/files/l10n/de.php
new file mode 100644
index 0000000000000000000000000000000000000000..ade5a0495039d4a55c178453fb1ba185591b3e48
--- /dev/null
+++ b/files/l10n/de.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Dateien",
+"Maximum upload size" => "Maximale Größe",
+"Nothing in here. Upload something!" => "Alles leer. Lad’ was hoch!",
+"Upload" => "Hochladen",
+"New Folder" => "Neuer Ordner",
+"Name" => "Name",
+"Download" => "Herunterladen",
+"Size" => "Größe",
+"Modified" => "Bearbeitet",
+"Delete" => "Löschen",
+"Upload too large" => "Upload zu groß",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server."
+);
diff --git a/files/l10n/el.php b/files/l10n/el.php
new file mode 100644
index 0000000000000000000000000000000000000000..3537419e24d80d0b9ddcf92c87a20b5322941b1f
--- /dev/null
+++ b/files/l10n/el.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Αρχεία",
+"Maximum upload size" => "Μέγιστο μέγεθος μεταφόρτωσης",
+"Nothing in here. Upload something!" => "Δεν υπάρχει τίποτα εδώ. Ανέβασε κάτι!",
+"Upload" => "Μεταφόρτωση",
+"New Folder" => "Νέος φάκελος",
+"Name" => "Όνομα",
+"Download" => "Λήψη",
+"Size" => "Μέγεθος",
+"Modified" => "Τροποποιήθηκε",
+"Delete" => "Διαγραφή",
+"Upload too large" => "Πολύ μεγάλο το αρχείο προς μεταφόρτωση",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Τα αρχεία που προσπαθείτε να ανεβάσετε υπερβαίνουν το μέγιστο μέγεθος μεταφόρτωσης αρχείων σε αυτόν το διακομιστή."
+);
diff --git a/files/l10n/es.php b/files/l10n/es.php
new file mode 100644
index 0000000000000000000000000000000000000000..acb9d8c7083716f2f5d8840b4b9ddc0769774433
--- /dev/null
+++ b/files/l10n/es.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Archivos",
+"Maximum upload size" => "Tamaño máximo de subida",
+"Nothing in here. Upload something!" => "Aquí no hay nada. ¡Sube algo!",
+"Upload" => "Subir",
+"New Folder" => "Crear Carpeta",
+"Name" => "Nombre",
+"Download" => "Descargar",
+"Size" => "Tamaño",
+"Modified" => "Modificado",
+"Delete" => "Eliminado",
+"Upload too large" => "El archivo es demasiado grande",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor."
+);
diff --git a/files/l10n/fr.php b/files/l10n/fr.php
new file mode 100644
index 0000000000000000000000000000000000000000..ccaf9a3867db30cc2528e5b562a0f0c84c42b424
--- /dev/null
+++ b/files/l10n/fr.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Fichiers",
+"Maximum upload size" => "Taille max. d'envoi",
+"Nothing in here. Upload something!" => "Il n'y a rien ici ! Envoyez donc quelque chose :)",
+"Upload" => "Envoyer",
+"New Folder" => "Nouveau dossier",
+"Name" => "Nom",
+"Download" => "Téléchargement",
+"Size" => "Taille",
+"Modified" => "Modifié",
+"Delete" => "Supprimer",
+"Upload too large" => "Fichier trop volumineux",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise par ce serveur."
+);
diff --git a/files/l10n/id.php b/files/l10n/id.php
new file mode 100644
index 0000000000000000000000000000000000000000..28b298f289a8b167520afb2cbdec8d53f759b6d0
--- /dev/null
+++ b/files/l10n/id.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Berkas",
+"Maximum upload size" => "Ukuran unggah maksimum",
+"Nothing in here. Upload something!" => "Tidak ada apa-apa di sini. Unggah sesuatu!",
+"Upload" => "Unggah",
+"New Folder" => "Folder Baru",
+"Name" => "Nama",
+"Download" => "Unduh",
+"Size" => "Ukuran",
+"Modified" => "Dimodifikasi",
+"Delete" => "Hapus",
+"Upload too large" => "Unggahan terlalu besar",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Berkas yang anda coba unggah melebihi ukuran maksimum untuk pengunggahan berkas di server ini."
+);
diff --git a/files/l10n/it.php b/files/l10n/it.php
new file mode 100644
index 0000000000000000000000000000000000000000..c83e223eb9d84ad98f16a5ff6cc14f9962309886
--- /dev/null
+++ b/files/l10n/it.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "File",
+"Maximum upload size" => "Dimensione massima upload",
+"Nothing in here. Upload something!" => "Non c'è niente qui. Carica qualcosa!",
+"Upload" => "Carica",
+"New Folder" => "Nuova Cartella",
+"Name" => "Nome",
+"Download" => "Scarica",
+"Size" => "Dimensione",
+"Modified" => "Modificato",
+"Delete" => "Cancella",
+"Upload too large" => "Il file caricato è troppo grande",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server."
+);
diff --git a/files/l10n/nl.php b/files/l10n/nl.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ad054877126282833cbb5571af58a1af4526bdb
--- /dev/null
+++ b/files/l10n/nl.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Bestanden",
+"Maximum upload size" => "Maximaale bestands groote voor uploads",
+"Nothing in here. Upload something!" => "Er bevind zich hier niks, upload een bestand.",
+"Upload" => "Uploaden",
+"New Folder" => "Nieuwe Map",
+"Name" => "Naam",
+"Download" => "Download",
+"Size" => "Bestandsgroote",
+"Modified" => "Laatst aangepast",
+"Delete" => "Verwijderen",
+"Upload too large" => "Bestanden te groot",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "De bestanden die U probeert up te loaden zijn grooter dan de maximaal toegstane  groote voor deze server."
+);
diff --git a/files/l10n/pt_BR.php b/files/l10n/pt_BR.php
new file mode 100644
index 0000000000000000000000000000000000000000..dbafd695643f8e75fcbce4e37d573c5ebdfa7f36
--- /dev/null
+++ b/files/l10n/pt_BR.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Arquivos",
+"Maximum upload size" => "Tamanho máximo para carregar",
+"Nothing in here. Upload something!" => "Nada aqui.Carregar alguma coisa!",
+"Upload" => "Carregar",
+"New Folder" => "Nova Pasta",
+"Name" => "Nome",
+"Download" => "Baixar",
+"Size" => "Tamanho",
+"Modified" => "Modificado",
+"Delete" => "Excluir",
+"Upload too large" => "Arquivo muito grande",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Os arquivos que você está tentando carregar excedeu o tamanho máximo para arquivos no servidor."
+);
diff --git a/files/l10n/sv.php b/files/l10n/sv.php
new file mode 100644
index 0000000000000000000000000000000000000000..122905c950104d9b6c7a6593fa900d1b9508f03f
--- /dev/null
+++ b/files/l10n/sv.php
@@ -0,0 +1,14 @@
+<?php $TRANSLATIONS = array(
+"Files" => "Filer",
+"Maximum upload size" => "Maximal storlek att lägga upp",
+"Nothing in here. Upload something!" => "Ingenting här. Lägg upp något!",
+"Upload" => "Lägg upp",
+"New Folder" => "Ny katalog",
+"Name" => "Namn",
+"Download" => "Ladda ned",
+"Size" => "Storlek",
+"Modified" => "Ändrad",
+"Delete" => "Ta bort",
+"Upload too large" => "För stor överföring",
+"The files you are trying to upload exceed the maximum size for file uploads on this server." => "Filerna du försöker lägga upp överstiger den maximala storleken för filöverföringar på servern."
+);
diff --git a/files/l10n/xgettextfiles b/files/l10n/xgettextfiles
new file mode 100644
index 0000000000000000000000000000000000000000..9e22680e4556aaf686cc29c1bca32c1991857e64
--- /dev/null
+++ b/files/l10n/xgettextfiles
@@ -0,0 +1,5 @@
+../appinfo/app.php
+../templates/index.php
+../templates/part.list.php
+../js/filelist.js
+../js/files.js
diff --git a/files/settings.php b/files/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..2bbcb2acd3aec2e178fa165339e4845d449c11d6
--- /dev/null
+++ b/files/settings.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+
+// Init owncloud
+require_once('../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+	exit();
+}
+
+// Load the files we need
+OC_Util::addStyle( "files", "files" );
+OC_Util::addScript( "files", "files" );
+
+// Load the files
+$dir = isset( $_GET['dir'] ) ? $_GET['dir'] : '';
+
+$files = array();
+foreach( OC_Files::getdirectorycontent( $dir ) as $i ){
+	$i["date"] = date( $CONFIG_DATEFORMAT, $i["mtime"] );
+	$files[] = $i;
+}
+
+// Make breadcrumb
+$breadcrumb = array();
+$pathtohere = "/";
+foreach( explode( "/", $dir ) as $i ){
+	if( $i != "" ){
+		$pathtohere .= "$i/";
+		$breadcrumb[] = array( "dir" => $pathtohere, "name" => $i );
+	}
+}
+
+// return template
+$tmpl = new OC_Template( "files", "index", "user" );
+$tmpl->assign( 'files', $files );
+$tmpl->assign( "breadcrumb", $breadcrumb );
+$tmpl->printPage();
+
+?>
diff --git a/files/templates/admin.php b/files/templates/admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..0122865ee72c5a04b97b3b23ea220e57f25c34c8
--- /dev/null
+++ b/files/templates/admin.php
@@ -0,0 +1,10 @@
+<?php OC_Util::addScript('files','admin'); ?>
+
+<form name="filesForm" action='#' method='post'>
+	<?php if($_['htaccessWorking']):?>
+		<label for="maxUploadSize"><?php echo $l->t( 'Maximum upload size' ); ?> </label><input name='maxUploadSize' id="maxUploadSize" value='<?php echo $_['uploadMaxFilesize'] ?>'/><br/>
+		<input type='submit' value='Save'/>
+	<?php else:?>
+		No settings currently available.
+	<?php endif;?>
+</form>
diff --git a/files/templates/index.php b/files/templates/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e105811f051a7d41c9a2fd9e5102847f2f5378f
--- /dev/null
+++ b/files/templates/index.php
@@ -0,0 +1,50 @@
+<div id="controls">
+	<?php echo($_['breadcrumb']); ?>
+	<div class="actions">
+		<form data-upload-id='1' class="file_upload_form" action="ajax/upload.php" method="post" enctype="multipart/form-data" target="file_upload_target_1">
+			<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo $_['uploadMaxFilesize'] ?>" id="max_upload">
+			<input type="hidden" class="max_human_file_size" value="(max <?php echo $_['uploadMaxHumanFilesize']; ?>)">
+			<input type="hidden" name="dir" value="<?php echo $_['dir'] ?>" id="dir">
+			<div class="file_upload_wrapper svg">
+				<input type="submit" class="file_upload_filename" value="<?php echo $l->t('Upload'); ?>"/>
+				<input class="file_upload_start" type="file" name='files[]'/>
+				<a href="#" class="file_upload_button_wrapper" onclick="return false;" title="<?php echo  'max. '.$_['uploadMaxHumanFilesize'] ?>"></a>
+			</div>
+			<iframe name="file_upload_target_1" class='file_upload_target' src=""></iframe>
+		</form>
+		<form id="file_newfolder_form">
+			<input class="svg" type="text" name="file_newfolder_name" id="file_newfolder_name" value="" placeholder="<?php echo $l->t('New Folder')?>" />
+		</form>
+	</div>
+	<div id="file_action_panel">
+	</div>
+</div>
+<div id='notification'></div>
+
+<div id="emptyfolder" <?php if(count($_['files'])) echo 'style="display:none;"';?>><?php echo $l->t('Nothing in here. Upload something!')?></div>
+
+<table>
+	<thead>
+		<tr>
+			<th id='headerName'>
+				<input type="checkbox" id="select_all" />
+				<span class='name'><?php echo $l->t( 'Name' ); ?></span>
+				<span class='selectedActions'>
+					<a href="" title="<?php echo $l->t('Download')?>" class="download"><img class='svg' alt="Download" src="../core/img/actions/download.svg" /></a>
+					<a href="" title="Share" class="share"><img class='svg' alt="Share" src="../core/img/actions/share.svg" /></a>
+				</span>
+			</th>
+			<th id="headerSize"><?php echo $l->t( 'Size' ); ?></th>
+			<th id="headerDate"><span id="modified"><?php echo $l->t( 'Modified' ); ?></span><span class="selectedActions"><a href="" title="Delete" class="delete"><img class="svg" alt="<?php echo $l->t('Delete')?>" src="../core/img/actions/delete.svg" /></a></span></th>
+		</tr>
+	</thead>
+	<tbody id="fileList">
+		<?php echo($_['fileList']); ?>
+	</tbody>
+</table>
+
+<div id="uploadsize-message" title="<?php echo $l->t('Upload too large')?>">
+	<p>
+		<?php echo $l->t('The files you are trying to upload exceed the maximum size for file uploads on this server.');?>
+	</p>
+</div>
diff --git a/files/templates/part.breadcrumb.php b/files/templates/part.breadcrumb.php
new file mode 100644
index 0000000000000000000000000000000000000000..9a265a9c1eaf7cf3744a1e2439fd0102d334ae5e
--- /dev/null
+++ b/files/templates/part.breadcrumb.php
@@ -0,0 +1,5 @@
+	<?php foreach($_["breadcrumb"] as $crumb): ?>
+		<div class="crumb svg" data-dir='<?php echo $crumb["dir"];?>' style='background-image:url("<?php echo image_path('core','breadcrumb.png');?>")'>
+			<a href="<?php echo $_['baseURL'].$crumb["dir"]; ?>"><?php echo htmlspecialchars($crumb["name"]); ?></a>
+		</div>
+	<?php endforeach; ?>
diff --git a/files/templates/part.list.php b/files/templates/part.list.php
new file mode 100644
index 0000000000000000000000000000000000000000..5995976f73a8f541a6a63d9f75c33a1fe7b1b473
--- /dev/null
+++ b/files/templates/part.list.php
@@ -0,0 +1,24 @@
+		<?php foreach($_['files'] as $file):
+			$simple_file_size = simple_file_size($file['size']);
+			$simple_size_color = intval(200-$file['size']/(1024*1024)*2); // the bigger the file, the darker the shade of grey; megabytes*2
+			if($simple_size_color<0) $simple_size_color = 0;
+			$relative_modified_date = relative_modified_date($file['mtime']);
+			$relative_date_color = round((time()-$file['mtime'])/60/60/24*14); // the older the file, the brighter the shade of grey; days*14
+			if($relative_date_color>200) $relative_date_color = 200; ?>
+			<tr data-file="<?php echo $file['name'];?>" data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>" data-mime="<?php echo $file['mime']?>" data-size='<?php echo $file['size'];?>'>
+				<td class="filename svg" style="background-image:url(<?php if($file['type'] == 'dir') echo mimetype_icon('dir'); else echo mimetype_icon($file['mime']); ?>)">
+					<input type="checkbox" />
+					<a class="name" href="<?php if($file['type'] == 'dir') echo $_['baseURL'].$file['directory'].'/'.$file['name']; else echo $_['downloadURL'].$file['directory'].'/'.$file['name']; ?>" title="">
+					<span class="nametext">
+						<?php if($file['type'] == 'dir'):?>
+							<?php echo htmlspecialchars($file['name']);?>
+						<?php else:?>
+							<?php echo htmlspecialchars($file['basename']);?><span class='extention'><?php echo $file['extention'];?></span>
+						<?php endif;?>
+					</span>
+					</a>
+				</td>
+				<td class="filesize" title="<?php echo human_file_size($file['size']); ?>" style="color:rgb(<?php echo $simple_size_color.','.$simple_size_color.','.$simple_size_color ?>)"><?php echo $simple_file_size; ?></td>
+				<td class="date"><span class="modified" title="<?php echo $file['date']; ?>" style="color:rgb(<?php echo $relative_date_color.','.$relative_date_color.','.$relative_date_color ?>)"><?php echo $relative_modified_date; ?></span></td>
+			</tr>
+		<?php endforeach; ?>
diff --git a/files/webdav.php b/files/webdav.php
new file mode 100644
index 0000000000000000000000000000000000000000..b1d242b2cc9017f5309cef378e7e4b1e0d2d8d78
--- /dev/null
+++ b/files/webdav.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @author Jakob Sack
+ * @copyright 2010 Frank Karlitschek karlitschek@kde.org
+ * @copyright 2011 Jakob Sack kde@jakobsack.de
+ *
+ * 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/>.
+ *
+ */
+
+// Do not load FS ...
+$RUNTIME_NOSETUPFS = true;
+
+require_once('../lib/base.php');
+
+// Backends
+$authBackend = new OC_Connector_Sabre_Auth();
+$lockBackend = new OC_Connector_Sabre_Locks();
+
+// Create ownCloud Dir
+$publicDir = new OC_Connector_Sabre_Directory('');
+
+// Fire up server
+$server = new Sabre_DAV_Server($publicDir);
+$server->setBaseUri($WEBROOT.'/files/webdav.php');
+
+// Load plugins
+$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
+$server->addPlugin(new Sabre_DAV_Locks_Plugin($lockBackend));
+
+// And off we go!
+$server->exec();
diff --git a/index.php b/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..52a00465f225623c0b1fd8665dd2ccb6990e225f
--- /dev/null
+++ b/index.php
@@ -0,0 +1,126 @@
+<?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/>.
+*
+*/
+
+$RUNTIME_NOAPPS = TRUE; //no apps, yet
+
+require_once('lib/base.php');
+
+OC_Util::addScript('setup');
+
+$not_installed = !OC_Config::getValue('installed', false);
+$install_called = (isset($_POST['install']) AND $_POST['install']=='true');
+// First step : check if the server is correctly configured for ownCloud :
+$errors = OC_Util::checkServer();
+if(count($errors) > 0) {
+	OC_Template::printGuestPage("", "error", array("errors" => $errors));
+}
+
+// Setup required :
+elseif($not_installed OR $install_called) {
+	require_once('setup.php');
+	exit();
+}
+
+if($_SERVER['REQUEST_METHOD']=='PROPFIND'){//handle webdav
+	header('location: '.OC_Helper::linkTo('files','webdav.php'));
+	exit();
+}
+
+// Someone is logged in :
+elseif(OC_User::isLoggedIn()) {
+	if(isset($_GET["logout"]) and ($_GET["logout"])) {
+		OC_User::logout();
+		header("Location: ".$WEBROOT.'/');
+		exit();
+	}
+	else {
+		header("Location: ".$WEBROOT.'/'.OC_Appconfig::getValue("core", "defaultpage", "files/index.php"));
+		exit();
+	}
+}
+
+// Someone wants to log in :
+elseif(isset($_POST["user"]) && isset($_POST['password'])) {
+	OC_App::loadApps();
+	if(OC_User::login($_POST["user"], $_POST["password"])) {
+		header("Location: ".$WEBROOT.'/'.OC_Appconfig::getValue("core", "defaultpage", "files/index.php"));
+		if(!empty($_POST["remember_login"])){
+			OC_User::setUsernameInCookie($_POST["user"]);
+		}
+		else {
+			OC_User::unsetUsernameInCookie();
+		}
+		exit();
+	}
+	else {
+		if(isset($_COOKIE["username"])){
+			OC_Template::printGuestPage("", "login", array("error" => true, "username" => $_COOKIE["username"]));
+		}else{
+			OC_Template::printGuestPage("", "login", array("error" => true));
+		}
+	}
+}
+
+// Someone lost their password:
+elseif(isset($_GET['lostpassword'])) {
+	OC_App::loadApps();
+	if (isset($_POST['user'])) {
+		if (OC_User::userExists($_POST['user'])) {
+			$token = sha1($_POST['user']+uniqId());
+			OC_Preferences::setValue($_POST['user'], "owncloud", "lostpassword", $token);
+			// TODO send email with link+token
+			OC_Template::printGuestPage("", "lostpassword", array("error" => false, "requested" => true));
+		} else {
+			OC_Template::printGuestPage("", "lostpassword", array("error" => true, "requested" => false));
+		}
+	} else {
+		OC_Template::printGuestPage("", "lostpassword", array("error" => false, "requested" => false));
+	}
+}
+
+// Someone wants to reset their password:
+elseif(isset($_GET['resetpassword']) && isset($_GET['token']) && isset($_GET['user']) && OC_Preferences::getValue($_GET['user'], "owncloud", "lostpassword") === $_GET['token']) {
+	OC_App::loadApps();
+	if (isset($_POST['password'])) {
+		if (OC_User::setPassword($_GET['user'], $_POST['password'])) {
+			OC_Preferences::deleteKey($_GET['user'], "owncloud", "lostpassword");
+			OC_Template::printGuestPage("", "resetpassword", array("success" => true));
+		} else {
+			OC_Template::printGuestPage("", "resetpassword", array("success" => false));
+		}
+	} else {
+		OC_Template::printGuestPage("", "resetpassword", array("success" => false));
+	}
+}
+
+// For all others cases, we display the guest page :
+else {
+	OC_App::loadApps();
+	if(isset($_COOKIE["username"])){
+		OC_Template::printGuestPage("", "login", array("error" => false, "username" => $_COOKIE["username"]));
+	}else{
+		OC_Template::printGuestPage("", "login", array("error" => false));
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/l10n/bg_BG/core.po b/l10n/bg_BG/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..ba3c85033ebd44e4bdc7da4a2c9dc3c95e5b407b
--- /dev/null
+++ b/l10n/bg_BG/core.po
@@ -0,0 +1,130 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Stefan Ilivanov <ilivanov@gmail.com>, 2011.
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:17+0200\n"
+"PO-Revision-Date: 2011-08-23 09:17+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Bulgarian (Bulgaria) (http://www.transifex.net/projects/p/owncloud/team/bg_BG/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: bg_BG\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr ""
+
+#: strings.php:6
+msgid "Users"
+msgstr ""
+
+#: strings.php:7
+msgid "Apps"
+msgstr ""
+
+#: strings.php:8
+msgid "Admin"
+msgstr ""
+
+#: strings.php:9
+msgid "Help"
+msgstr ""
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Входа пропадна!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr ""
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr ""
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr ""
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr ""
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr ""
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr ""
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr ""
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr ""
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Име на базата"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Разширено"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "хост"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Префикс за таблиците"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Директория за данни"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Завършване на настройките"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "обклакът не намерен"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr ""
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "пред."
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "следващо"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Вие излязохте."
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr ""
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr ""
+
+
diff --git a/l10n/bg_BG/files.po b/l10n/bg_BG/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..dbdf61722ba3f69b80234e003fddf85c0f14012f
--- /dev/null
+++ b/l10n/bg_BG/files.po
@@ -0,0 +1,69 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-13 02:25+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Bulgarian (Bulgaria) (http://www.transifex.net/projects/p/owncloud/team/bg_BG/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: bg_BG\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr ""
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr ""
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr ""
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr ""
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr ""
+
+#: templates/index.php:29
+msgid "Name"
+msgstr ""
+
+#: templates/index.php:31
+msgid "Download"
+msgstr ""
+
+#: templates/index.php:35
+msgid "Size"
+msgstr ""
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr ""
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr ""
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr ""
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+
+
diff --git a/l10n/bg_BG/media.po b/l10n/bg_BG/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..3f35e23fb8b26dcd705282d38df9ee179b494abe
--- /dev/null
+++ b/l10n/bg_BG/media.po
@@ -0,0 +1,67 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Bulgarian (Bulgaria) (http://www.transifex.net/projects/p/owncloud/team/bg_BG/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: bg_BG\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr ""
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr ""
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr ""
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr ""
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr ""
+
+#: templates/music.php:38
+msgid "Album"
+msgstr ""
+
+#: templates/music.php:39
+msgid "Title"
+msgstr ""
+
+
diff --git a/l10n/bg_BG/settings.po b/l10n/bg_BG/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..7a36a21518aad71c05f3cab168d79ca1cf266e2b
--- /dev/null
+++ b/l10n/bg_BG/settings.po
@@ -0,0 +1,132 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Stefan Ilivanov <ilivanov@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: 2011-08-27 23:13+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Bulgarian (Bulgaria) (http://www.transifex.net/projects/p/owncloud/team/bg_BG/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: bg_BG\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Проблем с индентификацията"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr ""
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Невалидна заявка"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Езика е сменен"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr ""
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr ""
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr ""
+
+#: templates/apps.php:23
+msgid "by"
+msgstr ""
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr ""
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr ""
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr ""
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr ""
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr ""
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr ""
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Вашата парола е сменена"
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr ""
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Нова парола"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr ""
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr ""
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Език"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr ""
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+
+#: templates/users.php:16
+msgid "Name"
+msgstr ""
+
+#: templates/users.php:17
+msgid "Password"
+msgstr ""
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr ""
+
+#: templates/users.php:24
+msgid "Create"
+msgstr ""
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr ""
+
+
diff --git a/l10n/ca/core.po b/l10n/ca/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..b660d24b392150b7eb275aa8145c18425ce0a863
--- /dev/null
+++ b/l10n/ca/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rcalvoi@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-23 15:32+0000\n"
+"Last-Translator: rogerc <rcalvoi@yahoo.com>\n"
+"Language-Team: Catalan (http://www.transifex.net/projects/p/owncloud/team/ca/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ca\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Personal"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Usuaris"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Aplicacions"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Administrador"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Ajuda"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "No s'ha trobat el núvol"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Crea un <strong>compte d'administrador</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Nom d'usuari"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Contrasenya"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Configura la base de dades"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "s'usarà"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Usuari de la base de dades"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Contrasenya de la base de dades"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nom de la base de dades"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Avançat"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Prefix de les taules"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Carpeta de dades"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Acaba la configuració"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "us dóna la llibertat per controlar les vostres dades a internet"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Sortir"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Arranjament"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "L'inici de sessió ha fallat!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "recorda'm"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Heu tancat la sessió."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "anterior"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "següent"
+
+
diff --git a/l10n/ca/files.po b/l10n/ca/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..9551131e02a4d4f6fc6132c44ca1f41f58d75cd5
--- /dev/null
+++ b/l10n/ca/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rcalvoi@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-16 18:04+0000\n"
+"Last-Translator: rogerc <rcalvoi@yahoo.com>\n"
+"Language-Team: Catalan (http://www.transifex.net/projects/p/owncloud/team/ca/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ca\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Fitxers"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Mida màxima de pujada"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Res per aquí. Pugeu alguna cosa!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Puja"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Carpeta nova"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Nom"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Descarrega"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Mida"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Modificat"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Esborra"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "La pujada és massa gran"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Els fitxers que esteu intentant pujar excedeixen la mida màxima de pujada "
+"d'aquest servidor"
+
+
diff --git a/l10n/ca/media.po b/l10n/ca/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..b0754f01964d420ffefa90e78332f8fbe2a29c53
--- /dev/null
+++ b/l10n/ca/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rcalvoi@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:09+0200\n"
+"PO-Revision-Date: 2011-08-21 10:56+0000\n"
+"Last-Translator: rogerc <rcalvoi@yahoo.com>\n"
+"Language-Team: Catalan (http://www.transifex.net/projects/p/owncloud/team/ca/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ca\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Música"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr "Reprodueix"
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pausa"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr "Anterior"
+
+#: templates/music.php:6
+msgid "Next"
+msgstr "Següent"
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr "Mut"
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr "Activa el so"
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Cançons escanejades"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Escaneja de nou la col·lecció"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artista"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Àlbum"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Títol"
+
+
diff --git a/l10n/ca/settings.po b/l10n/ca/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..e976e434c85df9704eabbcecfd9dddf666ec7a20
--- /dev/null
+++ b/l10n/ca/settings.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rcalvoi@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-23 15:33+0000\n"
+"Last-Translator: rogerc <rcalvoi@yahoo.com>\n"
+"Language-Team: Catalan (http://www.transifex.net/projects/p/owncloud/team/ca/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: ca\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Error d'autenticació"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID ha canviat"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Sol.licitud no vàlida"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "S'ha canviat l'idioma"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Afegir una aplicació"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Sel·leccioneu una aplicació"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "- amb llicència"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "per"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Feu una pregunta"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problemes per connectar-se a la base de dades d'ajuda."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Vés-hi manualment."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Resposta"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Esteu usant"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "del disponible"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "La contrasenya ha canviat"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "contrasenya actual"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Contrasenya nova"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "mostra"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "canvia la contrasenya"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Idioma"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Ajudeu amb la traducció"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"useu aquesta adreça per connectar-vos a ownCloud des del gestor de fitxers"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nom"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Contrasenya"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Grups"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Crea"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Esborra"
+
+
diff --git a/l10n/da/core.po b/l10n/da/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..046bfaaefeadb64483a4feaf13e2d6316a6eeb38
--- /dev/null
+++ b/l10n/da/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Pascal d'Hermilly <pascal@dhermilly.dk>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-26 13:48+0000\n"
+"Last-Translator: pascal_a <pascal@dhermilly.dk>\n"
+"Language-Team: Danish (http://www.transifex.net/projects/p/owncloud/team/da/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: da\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Personlig"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Brugere"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Apps"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Admin"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Hjælp"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Sky ikke fundet"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Lav en <strong>administrator konto</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Brugernavn"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Kodeord"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Konfigurer databasen"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "vil blive brugt"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Database-bruger"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Database-kodeord"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Database-navn"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Avanceret"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Vært"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Tabel præfiks"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Data mappe"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Afslut installation"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "giver dig friheden til at kontrollere dine egne data på internettet"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Log ud"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Indstillinger"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Login mislykkedes!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "husk"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Du er nu logget ud"
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "forrige"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "næste"
+
+
diff --git a/l10n/da/files.po b/l10n/da/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..e011eb0eb2a8a1d2c076cacc2bb3350b4a0acf95
--- /dev/null
+++ b/l10n/da/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Pascal d'Hermilly <pascal@dhermilly.dk>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-17 17:24+0000\n"
+"Last-Translator: pascal_a <pascal@dhermilly.dk>\n"
+"Language-Team: Danish (http://www.transifex.net/projects/p/owncloud/team/da/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: da\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Filer"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Maksimal upload-størrelse"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Her er tomt. Upload noget!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Upload"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Ny Mappe"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Navn"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Download"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Størrelse"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Ændret"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Slet"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Upload for stor"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Filerne du prøver at uploade er større end den maksimale størrelse for fil-"
+"upload på denne server."
+
+
diff --git a/l10n/da/media.po b/l10n/da/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..7c12df9653adcfa39a0b0cd4e3de7ad6ac803ad9
--- /dev/null
+++ b/l10n/da/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Pascal d'Hermilly <pascal@dhermilly.dk>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Danish (http://www.transifex.net/projects/p/owncloud/team/da/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: da\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Musik"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pause"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Sange skannet"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Genskan Samling"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Kunstner"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Titel"
+
+
diff --git a/l10n/da/settings.po b/l10n/da/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..7921a74708a18646442586c4f97a9e08948c5abd
--- /dev/null
+++ b/l10n/da/settings.po
@@ -0,0 +1,128 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Pascal d'Hermilly <pascal@dhermilly.dk>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-26 13:49+0000\n"
+"Last-Translator: pascal_a <pascal@dhermilly.dk>\n"
+"Language-Team: Danish (http://www.transifex.net/projects/p/owncloud/team/da/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: da\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Godkendelsesfejl"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID ændret"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Ugyldig forespørgsel"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Sprog ændret"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Tilføj dit program"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Vælg en App"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-licenseret"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "af"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Stil et spørgsmål"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problemer med at forbinde til hjælpe-databasen"
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "GÃ¥ derhen manuelt."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Svar"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Du benytter"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "af det tilgængelige"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Din adgangskode er blevet ændret"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Nuværende adgangskode"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Ny adgangskode"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "vis"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Skift password"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Sprog"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Hjælp med at oversætte"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr "benyt denne adresse til at forbinde til din ownCloud i din filbrowser"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Navn"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Kodeord"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Grupper"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Ny"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Slet"
+
+
diff --git a/l10n/de/core.po b/l10n/de/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..5db71c84d964940a1735de6b0266e0bfcdd3d101
--- /dev/null
+++ b/l10n/de/core.po
@@ -0,0 +1,131 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <georg.stefan.germany@googlemail.com>, 2011.
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:18+0200\n"
+"PO-Revision-Date: 2011-08-23 09:18+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: German (http://www.transifex.net/projects/p/owncloud/team/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Persönlich"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Nutzer"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Anwendungen"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Verwaltung"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Hilfe"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Anmeldung fehlgeschlagen!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "merken"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "<strong>Admin-Konto</strong> anlegen"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Nutzername"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Passwort"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Datenbank einrichten"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "wird genutzt"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Datenbanknutzer"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Datenbankpasswort"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Datenbankname"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Erweitert"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Tabellenpräfix"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Datenverzeichnis"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Installation abschließen"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Cloud nicht verfügbar"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr ""
+"gibt dir die Freiheit, deine eigenen Daten im Internet zu kontrollieren."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "Zurück"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "Weiter"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Erfolgreich abgemeldet."
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Abmelden"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Einstellungen"
+
+
diff --git a/l10n/de/files.po b/l10n/de/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..870b38d767a72aab4cfbfd8f46f418bbe083003f
--- /dev/null
+++ b/l10n/de/files.po
@@ -0,0 +1,71 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 05:10+0200\n"
+"PO-Revision-Date: 2011-08-13 02:38+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: German (http://www.transifex.net/projects/p/owncloud/team/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Dateien"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Maximale Größe"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Alles leer. Lad’ was hoch!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Hochladen"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Neuer Ordner"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Name"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Herunterladen"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Größe"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Bearbeitet"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Löschen"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Upload zu groß"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Die Datei überschreitet die Maximalgröße für Uploads auf diesem Server."
+
+
diff --git a/l10n/de/media.po b/l10n/de/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..437d50939d7f67f13a5718d7f21a33a441daf7c0
--- /dev/null
+++ b/l10n/de/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: German (http://www.transifex.net/projects/p/owncloud/team/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Musik"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pause"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Lieder gescannt"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Sammlung scannen"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Künstler"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Titel"
+
+
diff --git a/l10n/de/settings.po b/l10n/de/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..2998d93e0f78d6d980c026ef627dd73644e331c5
--- /dev/null
+++ b/l10n/de/settings.po
@@ -0,0 +1,134 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: 2011-08-27 23:13+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: German (http://www.transifex.net/projects/p/owncloud/team/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Berechtigungsfehler"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID geändert"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Ungültige Anfrage"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Sprache geändert"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Eigene Anwendung hinzufügen"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Wähle eine Anwendung aus"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-lizenziert"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "von"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Stell eine Frage"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Probleme bei der Verbindung zur Hilfe-Datenbank."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Datenbank direkt besuchen."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Antwort"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Du nutzt"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "der verfügbaren"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Dein Passwort wurde geändert"
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Aktuelles Passwort"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Neues Passwort"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "zeigen"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Passwort ändern"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Sprache"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Hilf bei der Ãœbersetzung"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"benutze diese Adresse, um deine ownCloud mit deinem Dateiverwalter zu "
+"verbinden"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Name"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Passwort"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Gruppen"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Anlegen"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Löschen"
+
+
diff --git a/l10n/el/core.po b/l10n/el/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..97bd6dcad2f69aee24294d2f45786dd05a627241
--- /dev/null
+++ b/l10n/el/core.po
@@ -0,0 +1,131 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <petros.kyladitis@gmail.com>, 2011.
+# Petros Kyladitis <petros.kyladitis@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:17+0200\n"
+"PO-Revision-Date: 2011-08-23 09:17+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Greek (http://www.transifex.net/projects/p/owncloud/team/el/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: el\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Προσωπικά"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Χρήστες"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Εφαρμογές"
+
+#: strings.php:8
+msgid "Admin"
+msgstr ""
+
+#: strings.php:9
+msgid "Help"
+msgstr "Βοήθεια"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Η σύνδεση απέτυχε!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "να με θυμάσαι"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Δημιουργήστε έναν <strong>λογαριασμό διαχειριστή</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Όνομα Χρήστη"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Κωδικός"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Διαμόρφωση της βάσης δεδομένων"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "θα χρησιμοποιηθούν"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Χρήστης της βάσης δεδομένων"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Κωδικός πρόσβασης βάσης δεδομένων"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Όνομα βάσης δεδομένων"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Για προχωρημένους"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Εξυπηρετητής"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Πρόθεμα πίνακα"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Φάκελος δεδομένων"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Ολοκλήρωση εγκατάστασης"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Δεν βρέθηκε σύννεφο"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr ""
+"σας δίνει την ελευθερία να ελέγχετε τα δικά σας δεδομένα στο διαδίκτυο"
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "προηγούμενο"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "επόμενο"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Έχετε αποσυνδεθεί."
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Αποσύνδεση"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Ρυθμίσεις"
+
+
diff --git a/l10n/el/files.po b/l10n/el/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..86f5a4e206c1dd0d3c12f0d3f361e717e954b0a6
--- /dev/null
+++ b/l10n/el/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Petros Kyladitis <petros.kyladitis@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-13 17:44+0000\n"
+"Last-Translator: multipetros <petros.kyladitis@gmail.com>\n"
+"Language-Team: Greek (http://www.transifex.net/projects/p/owncloud/team/el/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: el\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Αρχεία"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Μέγιστο μέγεθος μεταφόρτωσης"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Δεν υπάρχει τίποτα εδώ. Ανέβασε κάτι!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Μεταφόρτωση"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Νέος φάκελος"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Όνομα"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Λήψη"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Μέγεθος"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Τροποποιήθηκε"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Διαγραφή"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Πολύ μεγάλο το αρχείο προς μεταφόρτωση"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Τα αρχεία που προσπαθείτε να ανεβάσετε υπερβαίνουν το μέγιστο μέγεθος "
+"μεταφόρτωσης αρχείων σε αυτόν το διακομιστή."
+
+
diff --git a/l10n/el/media.po b/l10n/el/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..c5bd96407cb4ba4d5cb9dcc91f330ffd9b3658ce
--- /dev/null
+++ b/l10n/el/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Petros Kyladitis <petros.kyladitis@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:09+0200\n"
+"PO-Revision-Date: 2011-08-21 22:51+0000\n"
+"Last-Translator: multipetros <petros.kyladitis@gmail.com>\n"
+"Language-Team: Greek (http://www.transifex.net/projects/p/owncloud/team/el/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: el\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Μουσική"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr "Αναπαραγωγή"
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Παύση"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr "Προηγούμενο"
+
+#: templates/music.php:6
+msgid "Next"
+msgstr "Επόμενο"
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr "Σίγαση"
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr "Επαναφορά ήχου"
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Σαρωμένα τραγούγια"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Επανασάρωση συλλογής"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Καλλιτέχνης"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Άλμπουμ"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Τίτλος"
+
+
diff --git a/l10n/el/settings.po b/l10n/el/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..03240ac6c73034daf35560568787a196c56634d5
--- /dev/null
+++ b/l10n/el/settings.po
@@ -0,0 +1,135 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <petros.kyladitis@gmail.com>, 2011.
+# Petros Kyladitis <petros.kyladitis@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: 2011-08-27 23:13+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Greek (http://www.transifex.net/projects/p/owncloud/team/el/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: el\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Σφάλμα ταυτοποίησης"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "Το OpenID άλλαξε"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Άκυρα αίτημα"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Η γλώσσα άλλαξε"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr ""
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Επιλέξτε μια εφαρμογή"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-με άδεια"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "με"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Κάντε μια ερώτηση"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Προβλήματα κατά τη σύνδεση με τη βάση δεδομένων βοήθειας."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Χειροκίνητη μετάβαση."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Απάντηση"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Χρησιμοποιείτε"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "από τα διαθέσιμα"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Ο κωδικός πρόσβασής σας άλαλαξε"
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Τρέχοντα κωδικό πρόσβασης"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Νέος κωδικός"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "Εμφάνιση"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Αλλαγή κωδικού πρόσβασης"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Γλώσσα"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Βοηθήστε στη μετάφραση"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"χρησιμοποιήστε αυτή τη διεύθυνση για να συνδεθείτε στο ownCloud σας από το "
+"διαχειριστή αρχείων σας"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Όνομα"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Κωδικός"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Ομάδες"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Δημιουργία"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Διαγραφή"
+
+
diff --git a/l10n/es/core.po b/l10n/es/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..268c319c858f1ddcb2d996225dca01142bc3e84b
--- /dev/null
+++ b/l10n/es/core.po
@@ -0,0 +1,130 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <sergioballesterossolanas@gmail.com>, 2011.
+#   <rom1dep@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-24 23:20+0000\n"
+"Last-Translator: xsergiolpx <sergioballesterossolanas@gmail.com>\n"
+"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/owncloud/team/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Personal"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Usuarios"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Aplicaciones"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Administrador"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Ayuda"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "No se encontró la nube"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Crear una <strong>cuenta de administrador</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Nombre de usuario"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Contraseña"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Configurar la base de datos"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "serán utilizados"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Usuario de la base de datos"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Contraseña de la base de datos"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nombre de la base de datos"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Avanzado"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Prefijo de la tabla"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Directorio de almacenamiento"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Completar instalación"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "te da la libertad del control de tus propios datos en internet"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Salir"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Ajustes"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "¡No se pudo iniciar sesión!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "recuérdame"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Has cerrado sesión."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "anterior"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "siguiente"
+
+
diff --git a/l10n/es/files.po b/l10n/es/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..763fd3ae75fe655f12ab5816c7e8f545f2788ab3
--- /dev/null
+++ b/l10n/es/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <sergioballesterossolanas@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-17 07:24+0000\n"
+"Last-Translator: xsergiolpx <sergioballesterossolanas@gmail.com>\n"
+"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/owncloud/team/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Archivos"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Tamaño máximo de subida"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Aquí no hay nada. ¡Sube algo!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Subir"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Crear Carpeta"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Nombre"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Descargar"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Tamaño"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Modificado"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Eliminado"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "El archivo es demasiado grande"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Los archivos que estás intentando subir sobrepasan el tamaño máximo "
+"permitido por este servidor."
+
+
diff --git a/l10n/es/media.po b/l10n/es/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..3a159198a26b610af95e938f4bcfd6fbcc9434c9
--- /dev/null
+++ b/l10n/es/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <sergioballesterossolanas@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:09+0200\n"
+"PO-Revision-Date: 2011-08-21 22:27+0000\n"
+"Last-Translator: xsergiolpx <sergioballesterossolanas@gmail.com>\n"
+"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/owncloud/team/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Música"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr "Reproducir"
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pausa"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr "Anterior"
+
+#: templates/music.php:6
+msgid "Next"
+msgstr "Siguiente"
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr "Silenciar"
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr "Sonar"
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Canciones encontradas"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Buscar música nueva"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artista"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Álbum"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Título"
+
+
diff --git a/l10n/es/settings.po b/l10n/es/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..27e14d97249408eab7500c2f124ae5afa0463ea1
--- /dev/null
+++ b/l10n/es/settings.po
@@ -0,0 +1,130 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <sergioballesterossolanas@gmail.com>, 2011.
+#   <rom1dep@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-24 23:20+0000\n"
+"Last-Translator: xsergiolpx <sergioballesterossolanas@gmail.com>\n"
+"Language-Team: Spanish (Castilian) (http://www.transifex.net/projects/p/owncloud/team/es/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: es\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Error de autentificación"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID Cambiado"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Solicitud no válida"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Idioma cambiado"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Añadir tu aplicación"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Seleccionar una aplicación"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-autorizado"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "por"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Hacer una pregunta"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problemas al conectar con la base de datos de ayuda."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Ir manualmente"
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Respuesta"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Estás utilizando"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "del total disponible de"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Tu contraseña ha sido cambiada"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Contraseña actual"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nueva contraseña:"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "mostrar"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Cambiar contraseña"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Idioma"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Ayuda a traducir"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"usar esta dirección para conectar tu ownCloud en tu explorador de archivos"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nombre"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Contraseña"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Grupos"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Crear"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Eliminar"
+
+
diff --git a/l10n/fr/core.po b/l10n/fr/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..933eab057872b0a14e572d727e959f69187be91b
--- /dev/null
+++ b/l10n/fr/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rom1dep@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-26 15:33+0000\n"
+"Last-Translator: rom1dep <rom1dep@gmail.com>\n"
+"Language-Team: French (http://www.transifex.net/projects/p/owncloud/team/fr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Personnel"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Applications"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Administration"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Aide"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Introuvable"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Créer un <strong>compte administrateur</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Mot de passe"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Configurer la base de données"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "sera utilisé"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Utilisateur de la base de données"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Mot de passe de la base de données"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nom de la base de données"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Avancé"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Hôte"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Préfixe des tables"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Répertoire des données"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Terminer l'installation"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "vous rend libre de contrôler vos propres données sur internet"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Se déconnecter"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Paramètres"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Échec de la connexion !"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "se souvenir de moi"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Vous êtes désormais déconnecté."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "précédent"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "suivant"
+
+
diff --git a/l10n/fr/files.po b/l10n/fr/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..c75745d8bd0f2492d3228fd239bf25e961142c3a
--- /dev/null
+++ b/l10n/fr/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rom1dep@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 05:10+0200\n"
+"PO-Revision-Date: 2011-08-13 09:14+0000\n"
+"Last-Translator: rom1dep <rom1dep@gmail.com>\n"
+"Language-Team: French (http://www.transifex.net/projects/p/owncloud/team/fr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Fichiers"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Taille max. d'envoi"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Il n'y a rien ici ! Envoyez donc quelque chose :)"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Envoyer"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Nouveau dossier"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Nom"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Téléchargement"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Taille"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Modifié"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Supprimer"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Fichier trop volumineux"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Les fichiers que vous essayez d'envoyer dépassent la taille maximale permise"
+" par ce serveur."
+
+
diff --git a/l10n/fr/media.po b/l10n/fr/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..5cdf1cd9036f622275f6ad7b83f5ff1d68ed0bac
--- /dev/null
+++ b/l10n/fr/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rom1dep@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: French (http://www.transifex.net/projects/p/owncloud/team/fr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Musique"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pause"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Pistes scannées"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Réanalyser la Collection"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artiste"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Titre"
+
+
diff --git a/l10n/fr/settings.po b/l10n/fr/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..5bb5f1dd65de24839110b812eaae386f74204c4b
--- /dev/null
+++ b/l10n/fr/settings.po
@@ -0,0 +1,131 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <rom1dep@gmail.com>, 2011.
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-26 15:34+0000\n"
+"Last-Translator: rom1dep <rom1dep@gmail.com>\n"
+"Language-Team: French (http://www.transifex.net/projects/p/owncloud/team/fr/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Erreur d'authentification"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "Identifiant OpenID changé"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Requète invalide"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Langue changée"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Ajoutez votre application"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Sélectionner une Application"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "sous licence"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "par"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Poser une question"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problème de connexion à la base de données d'aide."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "S'y rendre manuellement."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Réponse"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Vous utilisez"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "sur un total de"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Votre mot de passe a été changé"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Mot de passe actuel"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nouveau mot de passe"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "Afficher"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Changer de mot de passe"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Langue"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Aider à traduire"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"utilisez cette adresse pour vous connecter à votre ownCloud depuis votre "
+"explorateur de fichiers"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nom"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Mot de passe"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Groupes"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Créer"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Supprimer"
+
+
diff --git a/l10n/id/core.po b/l10n/id/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..beae7dec82cb7e1df4c4d3130001a901928ad451
--- /dev/null
+++ b/l10n/id/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Muhammad Radifar <m_radifar05@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-27 17:17+0000\n"
+"Last-Translator: radifar <m_radifar05@yahoo.com>\n"
+"Language-Team: Indonesian (http://www.transifex.net/projects/p/owncloud/team/id/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: id\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Pribadi"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Pengguna"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Aplikasi"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Admin"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Bantuan"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Cloud tidak ditemukan"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Buat sebuah <strong>akun admin</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Username"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Password"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Konfigurasi database"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "akan digunakan"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Pengguna database"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Password database"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nama database"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Tingkat Lanjut"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Awalan tabel"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Folder data"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Selesaikan instalasi"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "memberikan anda kebebasan dalam mengendalikan data anda di internet"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Keluar"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Setelan"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Gagal masuk!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "selalu login"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Anda telah keluar."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "sebelum"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "selanjutnya"
+
+
diff --git a/l10n/id/files.po b/l10n/id/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..c3fcfc353675241b916fd554095654ae39b2d9e3
--- /dev/null
+++ b/l10n/id/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Muhammad Radifar <m_radifar05@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-13 14:38+0000\n"
+"Last-Translator: radifar <m_radifar05@yahoo.com>\n"
+"Language-Team: Indonesian (http://www.transifex.net/projects/p/owncloud/team/id/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: id\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Berkas"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Ukuran unggah maksimum"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Tidak ada apa-apa di sini. Unggah sesuatu!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Unggah"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Folder Baru"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Nama"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Unduh"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Ukuran"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Dimodifikasi"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Hapus"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Unggahan terlalu besar"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Berkas yang anda coba unggah melebihi ukuran maksimum untuk pengunggahan "
+"berkas di server ini."
+
+
diff --git a/l10n/id/media.po b/l10n/id/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..5e833cc559b2949c1854bf2d3c3af9183b3469d8
--- /dev/null
+++ b/l10n/id/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Muhammad Radifar <m_radifar05@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Indonesian (http://www.transifex.net/projects/p/owncloud/team/id/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: id\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Musik"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Jeda"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Lagu-lagu yang telah dipindai"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Pindai ulang Koleksi"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artis"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Judul"
+
+
diff --git a/l10n/id/settings.po b/l10n/id/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..890bf7b34af1447b2da92a54f5dd265372eac225
--- /dev/null
+++ b/l10n/id/settings.po
@@ -0,0 +1,130 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Muhammad Radifar <m_radifar05@yahoo.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-23 09:45+0000\n"
+"Last-Translator: radifar <m_radifar05@yahoo.com>\n"
+"Language-Team: Indonesian (http://www.transifex.net/projects/p/owncloud/team/id/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: id\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Otentikasi bermasalah"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID telah dirubah"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Permintaan tidak valid"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Bahasa telah diganti"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Tambahkan aplikasi anda"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Pilih satu aplikasi"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-terlisensi"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "oleh"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Ajukan pertanyaan"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Bermasalah saat menghubungi database bantuan."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Pergi kesana secara manual."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Jawab"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Anda menggunakan"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "dari yang tersedia"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Password anda telah dirubah"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Password saat ini"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Password baru"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "perlihatkan"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Rubah password"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Bahasa"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Bantu terjemahkan"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"gunakan alamat ini untuk terhubung dengan ownCloud anda dalam file manager "
+"anda"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nama"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Password"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Group"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Buat"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Hapus"
+
+
diff --git a/l10n/it/core.po b/l10n/it/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..8e85d67ca791f3dd128732a96ad988db58f2fa0f
--- /dev/null
+++ b/l10n/it/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <cosenal@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:17+0200\n"
+"PO-Revision-Date: 2011-08-23 09:17+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Italian (http://www.transifex.net/projects/p/owncloud/team/it/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Personale"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Utenti"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Applicazioni"
+
+#: strings.php:8
+msgid "Admin"
+msgstr ""
+
+#: strings.php:9
+msgid "Help"
+msgstr "Aiuto"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Login fallito!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "ricorda"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Crea un &lt;strong&gt;account amministratore&lt;/strong&gt;"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Nome utente"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Password"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Configura il database"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "sarà usato"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Utente database"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Password database"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nome database"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Opzioni avanzate"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Prefisso tabella"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Cartella dati"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Termina"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Cloud non trovata"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "da la libertà di controllare i tuoi dati su internet"
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "precedente"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "successivo"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Sei uscito."
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Log out"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Impostazioni"
+
+
diff --git a/l10n/it/files.po b/l10n/it/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..2210317cc370541c64d3741dd64f6104fd9fb47c
--- /dev/null
+++ b/l10n/it/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <cosenal@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-15 02:09+0000\n"
+"Last-Translator: zimba12 <cosenal@gmail.com>\n"
+"Language-Team: Italian (http://www.transifex.net/projects/p/owncloud/team/it/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "File"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Dimensione massima upload"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Non c'è niente qui. Carica qualcosa!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Carica"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Nuova Cartella"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Nome"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Scarica"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Dimensione"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Modificato"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Cancella"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Il file caricato è troppo grande"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"I file che stai provando a caricare superano la dimensione massima "
+"consentita su questo server."
+
+
diff --git a/l10n/it/media.po b/l10n/it/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..7f27ceaf21636bafae239c1ffdd364663746e9ce
--- /dev/null
+++ b/l10n/it/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Francesco Apruzzese <cescoap@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:09+0200\n"
+"PO-Revision-Date: 2011-08-21 23:18+0000\n"
+"Last-Translator: OpenCode <cescoap@gmail.com>\n"
+"Language-Team: Italian (http://www.transifex.net/projects/p/owncloud/team/it/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Musica"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr "Play"
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pausa"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr "Precedente"
+
+#: templates/music.php:6
+msgid "Next"
+msgstr "Successiva"
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr "Disattiva audio"
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr "Riattiva audio"
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Canzoni analizzate"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Rianalizza colezione"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artista"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Titolo"
+
+
diff --git a/l10n/it/settings.po b/l10n/it/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..929431d154fdcb59e50158d40942c88039f72931
--- /dev/null
+++ b/l10n/it/settings.po
@@ -0,0 +1,135 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Francesco Apruzzese <cescoap@gmail.com>, 2011.
+# Jan-Christoph Borchardt <JanCBorchardt@fsfe.org>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: 2011-08-27 23:13+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Italian (http://www.transifex.net/projects/p/owncloud/team/it/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: it\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Errore nell'autenticazione"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID Modificato"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Richiesta non valida"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Lingua modificata"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr ""
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Seleziona un applicazione"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-licensed"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "da"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Fai una domanda"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problemi di connessione al database di aiutare."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Vai lì manualmente."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Risposta"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Si utilizza"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "su un totale di"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "La tua password è stata cambiata"
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Password attuale"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nuova password"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "mostra"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Modifica password"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Lingua"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Aiuta nella traduzione"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"usa questo indirizzo per connettersi al proprio ownCloud nel tuo file "
+"manager"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nome"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Password"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Gruppi"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Crea"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Cancella"
+
+
diff --git a/l10n/l10n.pl b/l10n/l10n.pl
new file mode 100644
index 0000000000000000000000000000000000000000..27b14c41bef90970a74263a2b35921d9d93ccf32
--- /dev/null
+++ b/l10n/l10n.pl
@@ -0,0 +1,148 @@
+#!/usr/bin/perl
+use strict;
+use Locale::PO;
+use Cwd;
+use Data::Dumper;
+use File::Path;
+
+sub crawlPrograms{
+	my( $dir, $ignore ) = @_;
+	my @found = ();
+
+	opendir( DIR, $dir );
+	my @files = readdir( DIR );
+	closedir( DIR );
+	@files = sort( @files );
+
+	foreach my $i ( @files ){
+		next if substr( $i, 0, 1 ) eq '.';
+		if( $i eq 'l10n' && !$ignore ){
+			push( @found, $dir );
+		}
+		elsif( -d $dir.'/'.$i ){
+			push( @found, crawlPrograms( $dir.'/'.$i ));
+		}
+	}
+
+	return @found;
+}
+
+sub crawlFiles{
+	my( $dir ) = @_;
+	my @found = ();
+
+	opendir( DIR, $dir );
+	my @files = readdir( DIR );
+	closedir( DIR );
+	@files = sort( @files );
+
+	foreach my $i ( @files ){
+		next if substr( $i, 0, 1 ) eq '.';
+		next if $i eq 'l10n';
+		
+		if( -d $dir.'/'.$i ){
+			push( @found, crawlFiles( $dir.'/'.$i ));
+		}
+		else{
+			push(@found,$dir.'/'.$i) if $i =~ /\.js$/ || $i =~ /\.php$/;
+		}
+	}
+
+	return @found;
+}
+
+sub readIgnorelist{
+	return () unless -e 'l10n/ignorelist';
+	my %ignore = ();
+	open(IN,'l10n/ignorelist');
+	while(<IN>){
+		my $line = $_;
+		chomp($line);
+		$ignore{"./$line"}++;
+	}
+	close(IN);
+	return %ignore;
+}
+
+my $task = shift( @ARGV );
+my $place = '..';
+
+die( "Usage: l10n.pl task\ntask: read, write\n" ) unless $task && $place;
+
+# Our current position
+my $whereami = cwd();
+die( "Program must be executed in a l10n-folder called 'l10n'" ) unless $whereami =~ m/\/l10n$/;
+
+# Where are i18n-files?
+my @dirs = crawlPrograms( $place, 1 );
+
+# Languages
+my @languages = ();
+opendir( DIR, '.' );
+my @files = readdir( DIR );
+closedir( DIR );
+foreach my $i ( @files ){
+	push( @languages, $i ) if -d $i && substr( $i, 0, 1 ) ne '.';
+}
+
+if( $task eq 'read' ){
+	rmtree( 'templates' );
+	mkdir( 'templates' ) unless -d 'templates';
+	print "Mode: reading\n";
+	foreach my $dir ( @dirs ){
+		my @temp = split( /\//, $dir );
+		my $app = pop( @temp );
+		chdir( $dir );
+		my @totranslate = crawlFiles('.');
+		my %ignore = readIgnorelist();
+		my $output = "${whereami}/templates/$app.pot";
+		print "  Processing $app\n";
+
+		foreach my $file ( @totranslate ){
+			next if $ignore{$file};
+			my $keyword = ( $file =~ /\.js$/ ? 't:2' : 't');
+			my $language = ( $file =~ /\.js$/ ? 'C' : 'PHP');
+			my $joinexisting = ( -e $output ? '--join-existing' : '');
+			print "    Reading $file\n";
+			`xgettext --output="$output" $joinexisting --keyword=$keyword --language=$language "$file"`;
+		}
+		chdir( $whereami );
+	}
+}
+elsif( $task eq 'write' ){
+	print "Mode: write\n";
+	foreach my $dir ( @dirs ){
+		my @temp = split( /\//, $dir );
+		my $app = pop( @temp );
+		chdir( $dir.'/l10n' );
+		print "  Processing $app\n";
+		foreach my $language ( @languages ){
+			next if $language eq 'templates';
+			
+			my $input = "${whereami}/$language/$app.po";
+			next unless -e $input;
+
+			print "    Language $language\n";
+			my $array = Locale::PO->load_file_asarray( $input );
+			# Create array
+			my @strings = ();
+			foreach my $string ( @{$array} ){
+				next if $string->msgid() eq '""';
+				next if $string->msgstr() eq '""';
+				push( @strings, $string->msgid()." => ".$string->msgstr());
+			}
+			next if $#strings == -1; # Skip empty files
+
+			# Write PHP file
+			open( OUT, ">$language.php" );
+			print OUT "<?php \$TRANSLATIONS = array(\n";
+			print OUT join( ",\n", @strings );
+			print OUT "\n);\n";
+			close( OUT );
+		}
+		chdir( $whereami );
+	}
+}
+else{
+	print "unknown task!\n";
+}
diff --git a/l10n/nl/core.po b/l10n/nl/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..21a885d2cbc12732ca64fc7f15b2a25ba10d2ea2
--- /dev/null
+++ b/l10n/nl/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <icewind1991@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:17+0200\n"
+"PO-Revision-Date: 2011-08-23 09:17+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Dutch (http://www.transifex.net/projects/p/owncloud/team/nl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: nl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Persoonlijk"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Gebruikers"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Apps"
+
+#: strings.php:8
+msgid "Admin"
+msgstr ""
+
+#: strings.php:9
+msgid "Help"
+msgstr "Help"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Aanmelden mislukt."
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "onthoud gegevens"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Maak een <strong>admin-account</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Gebruikersnaam"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Wachtwoord"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Configureren van de database"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "zal gebruikt woorden"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Database gebruiker"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Database wachtwoord"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Database naam"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Geavanceerd"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Tabelnaam voorvoegsel"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Gegevens folder"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Installatie afronden"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Cloud niet gevonden"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "geeft je de vrijheid om je eigen data te controleren op het internet"
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "vorige"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "volgende"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "U bent afgemeld."
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Afmelden"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Instellingen"
+
+
diff --git a/l10n/nl/files.po b/l10n/nl/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..8591e5d7843b16bcfe65d15630b76b8f80ee1914
--- /dev/null
+++ b/l10n/nl/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <icewind1991@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-18 18:58+0200\n"
+"PO-Revision-Date: 2011-08-18 13:14+0000\n"
+"Last-Translator: icewind <icewind1991@gmail.com>\n"
+"Language-Team: Dutch (http://www.transifex.net/projects/p/owncloud/team/nl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: nl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Bestanden"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Maximaale bestands groote voor uploads"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Er bevind zich hier niks, upload een bestand."
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Uploaden"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Nieuwe Map"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Naam"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Download"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Bestandsgroote"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Laatst aangepast"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Verwijderen"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Bestanden te groot"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"De bestanden die U probeert up te loaden zijn grooter dan de maximaal "
+"toegstane  groote voor deze server."
+
+
diff --git a/l10n/nl/media.po b/l10n/nl/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..21a797fe700eceed6a300bbbe979a5cafcdbc543
--- /dev/null
+++ b/l10n/nl/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <icewind1991@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Dutch (http://www.transifex.net/projects/p/owncloud/team/nl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: nl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Muziek"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pauze"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "nummers gescanned"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Collectie opnieuw scannen"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artiest"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Titel"
+
+
diff --git a/l10n/nl/settings.po b/l10n/nl/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..174daa7550728e66c54b7dd8e06a599eee392316
--- /dev/null
+++ b/l10n/nl/settings.po
@@ -0,0 +1,134 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <icewind1991@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: 2011-08-27 23:13+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Dutch (http://www.transifex.net/projects/p/owncloud/team/nl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: nl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Authenticatiefout."
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID is aangepast"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Ongeldig verzoek"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Taal aangepast"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr ""
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Selecteer een App"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-gelicenseerd"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "door"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Stel een vraag"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr ""
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr ""
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Beantwoord"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "U gebruikt"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "van de beschikbare"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Uw wachtwoord is aangepast"
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Huidig wachtwoord"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nieuw wachtwoord"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "weergeven"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Verander wachtwoord"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Taal"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Help met vertalen"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"gebruik dit adres om verbinding te maken met ownCloud in uw bestandsbeheer "
+"programa"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Naam"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Wachtwoord"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Groepen"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Creëer"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "verwijderen"
+
+
diff --git a/l10n/pl/core.po b/l10n/pl/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..e7c00ba77c615fe484d89e07fe8ff2d3f76c7f35
--- /dev/null
+++ b/l10n/pl/core.po
@@ -0,0 +1,131 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Kamil Domański <kdomanski@kdemail.net>, 2011.
+#   <mosslar@gmail.com>, 2011.
+# Marcin Małecki <mosslar@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-23 10:14+0000\n"
+"Last-Translator: mosslar <gerber@tkdami.net>\n"
+"Language-Team: Polish (http://www.transifex.net/projects/p/owncloud/team/pl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Ustawienia osobiste"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Użytkownicy"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Aplikacje"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Administrator"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Pomoc"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Konta nie znaleziono "
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Stwórz jako <strong>konto administratora</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Użytkownik"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Hasło"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Konfiguracja bazy danych"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "zostanie użyte"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Użytkownik bazy danych"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Hasło do bazy danych"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nazwa bazy danych"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Zaawansowane"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Prefiks tablicy"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Katalog danych"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Zakończ instalację"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "daje Ci wolność kontroli nad Twoimi danymi w Internecie"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Wyloguj siÄ™"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Ustawienia"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Nie udało się zalogować!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "zapamiętaj"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Zostałeś wylogowany."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "wstecz"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "dalej"
+
+
diff --git a/l10n/pl/files.po b/l10n/pl/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..3856765cf663c08bef01aec816d7485db85c9ebb
--- /dev/null
+++ b/l10n/pl/files.po
@@ -0,0 +1,69 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-19 13:10+0200\n"
+"PO-Revision-Date: 2011-08-19 11:11+0000\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr ""
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr ""
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr ""
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr ""
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr ""
+
+#: templates/index.php:29
+msgid "Name"
+msgstr ""
+
+#: templates/index.php:31
+msgid "Download"
+msgstr ""
+
+#: templates/index.php:35
+msgid "Size"
+msgstr ""
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr ""
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr ""
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr ""
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+
+
diff --git a/l10n/pl/media.po b/l10n/pl/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..237da744eec447cbd0c564c29fbda756c0e3b371
--- /dev/null
+++ b/l10n/pl/media.po
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <mosslar@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Polish (http://www.transifex.net/projects/p/owncloud/team/pl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Muzyka"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Zatrzymaj"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Przeskanowane utwory"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Przeskanuj kolekcjÄ™"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artysta"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Tytuł"
+
+
diff --git a/l10n/pl/settings.po b/l10n/pl/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..dde3d9a7edf80f9907abb9b58b53d8d93f2c94bf
--- /dev/null
+++ b/l10n/pl/settings.po
@@ -0,0 +1,132 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Kamil Domański <kdomanski@kdemail.net>, 2011.
+#   <mosslar@gmail.com>, 2011.
+# Marcin Małecki <mosslar@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-23 10:15+0000\n"
+"Last-Translator: mosslar <gerber@tkdami.net>\n"
+"Language-Team: Polish (http://www.transifex.net/projects/p/owncloud/team/pl/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "BÅ‚Ä…d uwierzytelniania"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "Zmieniono OpenID"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Nieprawidłowe żądanie"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Język zmieniony"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Dodaj własną aplikacje"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Zaznacz aplikacje"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-licencjonowany"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "przez"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Zadaj pytanie"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problem z połączeniem z bazą danych."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Przejdź na stronę ręcznie."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Odpowiedź"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Używasz"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "z dostępnych"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Twoje hasło zostało zmienione"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Bieżące hasło"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nowe hasło"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "pokaż"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Zmień hasło"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Język"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Pomóż w tłumaczeniu"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"użyj tego adresu żeby połączyć się z twoim kontem ownCloud w menedżerze "
+"plików."
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nazwa"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Hasło"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Grupy"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Stwórz"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Skasuj"
+
+
diff --git a/l10n/pt_BR/core.po b/l10n/pt_BR/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..2786ff40aea6d5c944b67326a9cb1c2fef31b223
--- /dev/null
+++ b/l10n/pt_BR/core.po
@@ -0,0 +1,130 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Van Der Fran <transifex@vanderland.com>, 2011.
+#   <duda.nogueira@metasys.com.br>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-24 12:49+0000\n"
+"Last-Translator: dudanogueira <duda.nogueira@metasys.com.br>\n"
+"Language-Team: Portuguese (Brazilian) (http://www.transifex.net/projects/p/owncloud/team/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr "Pessoal"
+
+#: strings.php:6
+msgid "Users"
+msgstr "Usuários"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Apps"
+
+#: strings.php:8
+msgid "Admin"
+msgstr "Admin"
+
+#: strings.php:9
+msgid "Help"
+msgstr "Ajuda"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Cloud não encontrado"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Criar uma <strong>conta</strong> de <strong>administrador</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Nome de Usuário"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Senha"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Configurar o banco de dados"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "será usado"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Usuário de banco de dados"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Senha do banco de dados"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Nome do banco de dados"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Avançado"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Host 'servidor'"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Prefixo da tabela"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Pasta de dados"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Concluir configuração"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "te dá a liberdade de controlar seus próprios dados na internet"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr "Sair"
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Configurações"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Login sem sucesso"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "lembrete"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Você está desconectado."
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "anterior"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "próximo"
+
+
diff --git a/l10n/pt_BR/files.po b/l10n/pt_BR/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..865b3eaeb9138c48c73b32ecbd3ee53f4e82eb1f
--- /dev/null
+++ b/l10n/pt_BR/files.po
@@ -0,0 +1,72 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Van Der Fran <transifex@vanderland.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-13 12:41+0200\n"
+"PO-Revision-Date: 2011-08-15 16:00+0000\n"
+"Last-Translator: vanderland <transifex@vanderland.com>\n"
+"Language-Team: Portuguese (Brazilian) (http://www.transifex.net/projects/p/owncloud/team/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Arquivos"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Tamanho máximo para carregar"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Nada aqui.Carregar alguma coisa!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Carregar"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Nova Pasta"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Nome"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Baixar"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Tamanho"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Modificado"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Excluir"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "Arquivo muito grande"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Os arquivos que você está tentando carregar excedeu o tamanho máximo para "
+"arquivos no servidor."
+
+
diff --git a/l10n/pt_BR/media.po b/l10n/pt_BR/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..86ebcc103ff585cfd0a90ae5766c9e72b83050b6
--- /dev/null
+++ b/l10n/pt_BR/media.po
@@ -0,0 +1,69 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Van Der Fran <transifex@vanderland.com>, 2011.
+#   <duda.nogueira@metasys.com.br>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-24 12:52+0000\n"
+"Last-Translator: dudanogueira <duda.nogueira@metasys.com.br>\n"
+"Language-Team: Portuguese (Brazilian) (http://www.transifex.net/projects/p/owncloud/team/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Música"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr "Tocar"
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Pausa"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr "Anterior"
+
+#: templates/music.php:6
+msgid "Next"
+msgstr "Próximo"
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr "Mudo"
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr "Não Mudo"
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Músicas encontradas"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Atualizar a Coleção"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artista"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Álbum"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Título"
+
+
diff --git a/l10n/pt_BR/settings.po b/l10n/pt_BR/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..624078ef167eb04f56ec03740ef9f168c7aca2fb
--- /dev/null
+++ b/l10n/pt_BR/settings.po
@@ -0,0 +1,131 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+# Van Der Fran <transifex@vanderland.com>, 2011.
+#   <duda.nogueira@metasys.com.br>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:11+0200\n"
+"PO-Revision-Date: 2011-08-24 12:54+0000\n"
+"Last-Translator: dudanogueira <duda.nogueira@metasys.com.br>\n"
+"Language-Team: Portuguese (Brazilian) (http://www.transifex.net/projects/p/owncloud/team/pt_BR/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: pt_BR\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Erro de autenticação"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "Mudou OpenID"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Pedido inválido"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Mudou Idioma"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr "Adicionar o seu aplicativo"
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Selecione uma Aplicação"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-licenciados"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "por"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Faça uma pergunta"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problemas ao conectar na base de dados."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "Ir manualmente."
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Resposta"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Você usa"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "do disponível"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Sua senha foi modificada"
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Senha atual"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nova senha"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "mostrar"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Alterar senha"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Idioma"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Ajuda na Tradução"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"use este endereço para se conectar ao seu ownCloud no seu gerenciador de "
+"arquvos"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Nome"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Senha"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Grupos"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Criar"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Apagar"
+
+
diff --git a/l10n/sv/core.po b/l10n/sv/core.po
new file mode 100644
index 0000000000000000000000000000000000000000..c19e89505c370729ff7f56e6bff1c61fecf3d9f9
--- /dev/null
+++ b/l10n/sv/core.po
@@ -0,0 +1,129 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <hakan.thn@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-23 11:17+0200\n"
+"PO-Revision-Date: 2011-08-23 09:17+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Swedish (http://www.transifex.net/projects/p/owncloud/team/sv/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: sv\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr ""
+
+#: strings.php:6
+msgid "Users"
+msgstr "Användare"
+
+#: strings.php:7
+msgid "Apps"
+msgstr "Program"
+
+#: strings.php:8
+msgid "Admin"
+msgstr ""
+
+#: strings.php:9
+msgid "Help"
+msgstr "Hjälp"
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr "Inloggning misslyckades!"
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr "kom ihåg"
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr "Skapa ett <strong>administratörskonto</strong>"
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr "Användarnamn"
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr "Lösenord"
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr "Konfigurera databasen"
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr "kommer att användas"
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr "Databas-användare"
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr "Databas-lösenord"
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr "Databasnamn"
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr "Avancerat"
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr "Värd"
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr "Tabellprefix"
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr "Datamapp"
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr "Avsluta installation"
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr "Hittade inget moln"
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr "ger dig friheten att hantera ditt eget data på internet"
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr "föregående"
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr "nästa"
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr "Du är utloggad"
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr ""
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr "Inställningar"
+
+
diff --git a/l10n/sv/files.po b/l10n/sv/files.po
new file mode 100644
index 0000000000000000000000000000000000000000..d9ca9ae2cad81cd9ccb47f6954d950626c3cff1d
--- /dev/null
+++ b/l10n/sv/files.po
@@ -0,0 +1,71 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-18 10:45+0200\n"
+"PO-Revision-Date: 2011-08-18 11:08+0000\n"
+"Last-Translator: HakanS <hakan.thn@gmail.com>\n"
+"Language-Team: Swedish (http://www.transifex.net/projects/p/owncloud/team/sv/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: sv\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr "Filer"
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr "Maximal storlek att lägga upp"
+
+#: templates/part.list.php:1
+msgid "Nothing in here. Upload something!"
+msgstr "Ingenting här. Lägg upp något!"
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr "Lägg upp"
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr "Ny katalog"
+
+#: templates/index.php:29
+msgid "Name"
+msgstr "Namn"
+
+#: templates/index.php:31
+msgid "Download"
+msgstr "Ladda ned"
+
+#: templates/index.php:35
+msgid "Size"
+msgstr "Storlek"
+
+#: templates/index.php:36
+msgid "Modified"
+msgstr "Ändrad"
+
+#: templates/index.php:36
+msgid "Delete"
+msgstr "Ta bort"
+
+#: templates/index.php:44
+msgid "Upload too large"
+msgstr "För stor överföring"
+
+#: templates/index.php:46
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
+"Filerna du försöker lägga upp överstiger den maximala storleken för "
+"filöverföringar på servern."
+
+
diff --git a/l10n/sv/media.po b/l10n/sv/media.po
new file mode 100644
index 0000000000000000000000000000000000000000..7eea58d1b7a523c2a0fcf1a5d6deffce4c43a1df
--- /dev/null
+++ b/l10n/sv/media.po
@@ -0,0 +1,67 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-20 05:08+0200\n"
+"PO-Revision-Date: 2011-08-20 03:08+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Swedish (http://www.transifex.net/projects/p/owncloud/team/sv/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: sv\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr "Musik"
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr "Paus"
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr "Skannade låtar"
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr "Sök igenom samlingen"
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr "Artist"
+
+#: templates/music.php:38
+msgid "Album"
+msgstr "Album"
+
+#: templates/music.php:39
+msgid "Title"
+msgstr "Titel"
+
+
diff --git a/l10n/sv/settings.po b/l10n/sv/settings.po
new file mode 100644
index 0000000000000000000000000000000000000000..4507424b33cb956cc5557a7187bd13e4ac4f6f60
--- /dev/null
+++ b/l10n/sv/settings.po
@@ -0,0 +1,133 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# 
+#   <hakan.thn@gmail.com>, 2011.
+msgid ""
+msgstr ""
+"Project-Id-Version: ownCloud\n"
+"Report-Msgid-Bugs-To: http://bugs.kde.org/buglist.cgi?product=owncloud\n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: 2011-08-27 23:13+0000\n"
+"Last-Translator: JanCBorchardt <JanCBorchardt@fsfe.org>\n"
+"Language-Team: Swedish (http://www.transifex.net/projects/p/owncloud/team/sv/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: sv\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr "Autentiseringsfel"
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr "OpenID ändrat"
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr "Ogiltig begäran"
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr "Språk ändrades"
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr ""
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr "Välj en App"
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr "-licensierat"
+
+#: templates/apps.php:23
+msgid "by"
+msgstr "av"
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr "Ställ en fråga"
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr "Problem med att ansluta till hjälp-databasen."
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr "GÃ¥ dit manuellt"
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr "Svar"
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr "Du använder"
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr "av tillgängliga"
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr "Ditt lösenord ändrades"
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr "Nuvarande lösenord"
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr "Nytt lösenord"
+
+#: templates/personal.php:17
+msgid "show"
+msgstr "visa"
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr "Ändra lösenord"
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr "Språk"
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr "Hjälp till att översätta"
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+"använd denna adress för att ansluta till ditt ownCloud i din filhanterare"
+
+#: templates/users.php:16
+msgid "Name"
+msgstr "Namn"
+
+#: templates/users.php:17
+msgid "Password"
+msgstr "Lösenord"
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr "Grupper"
+
+#: templates/users.php:24
+msgid "Create"
+msgstr "Skapa"
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr "Ta bort"
+
+
diff --git a/l10n/templates/core.pot b/l10n/templates/core.pot
new file mode 100644
index 0000000000000000000000000000000000000000..5fef4a4eae0bf640e2ac78044c30280927195529
--- /dev/null
+++ b/l10n/templates/core.pot
@@ -0,0 +1,127 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: strings.php:5
+msgid "Personal"
+msgstr ""
+
+#: strings.php:6
+msgid "Users"
+msgstr ""
+
+#: strings.php:7
+msgid "Apps"
+msgstr ""
+
+#: strings.php:8
+msgid "Admin"
+msgstr ""
+
+#: strings.php:9
+msgid "Help"
+msgstr ""
+
+#: templates/404.php:12
+msgid "Cloud not found"
+msgstr ""
+
+#: templates/installation.php:20
+msgid "Create an <strong>admin account</strong>"
+msgstr ""
+
+#: templates/installation.php:21
+msgid "Username"
+msgstr ""
+
+#: templates/installation.php:22
+msgid "Password"
+msgstr ""
+
+#: templates/installation.php:27
+msgid "Configure the database"
+msgstr ""
+
+#: templates/installation.php:32 templates/installation.php:43
+#: templates/installation.php:53
+msgid "will be used"
+msgstr ""
+
+#: templates/installation.php:64
+msgid "Database user"
+msgstr ""
+
+#: templates/installation.php:65
+msgid "Database password"
+msgstr ""
+
+#: templates/installation.php:66
+msgid "Database name"
+msgstr ""
+
+#: templates/installation.php:74
+msgid "Advanced"
+msgstr ""
+
+#: templates/installation.php:77
+msgid "Host"
+msgstr ""
+
+#: templates/installation.php:78
+msgid "Table prefix"
+msgstr ""
+
+#: templates/installation.php:80
+msgid "Data folder"
+msgstr ""
+
+#: templates/installation.php:83
+msgid "Finish setup"
+msgstr ""
+
+#: templates/layout.guest.php:35
+msgid "gives you the freedom to control your own data on the internet"
+msgstr ""
+
+#: templates/layout.user.php:34
+msgid "Log out"
+msgstr ""
+
+#: templates/layout.user.php:46 templates/layout.user.php:47
+msgid "Settings"
+msgstr ""
+
+#: templates/login.php:4
+msgid "Login failed!"
+msgstr ""
+
+#: templates/login.php:9 templates/login.php:13
+msgid "remember"
+msgstr ""
+
+#: templates/logout.php:1
+msgid "You are logged out."
+msgstr ""
+
+#: templates/part.pagenavi.php:3
+msgid "prev"
+msgstr ""
+
+#: templates/part.pagenavi.php:20
+msgid "next"
+msgstr ""
diff --git a/l10n/templates/files.pot b/l10n/templates/files.pot
new file mode 100644
index 0000000000000000000000000000000000000000..b704bb160ebddff681ca57cdc1334572e4c15f95
--- /dev/null
+++ b/l10n/templates/files.pot
@@ -0,0 +1,68 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: appinfo/app.php:7
+msgid "Files"
+msgstr ""
+
+#: templates/admin.php:5
+msgid "Maximum upload size"
+msgstr ""
+
+#: templates/index.php:9
+msgid "Upload"
+msgstr ""
+
+#: templates/index.php:16
+msgid "New Folder"
+msgstr ""
+
+#: templates/index.php:24
+msgid "Nothing in here. Upload something!"
+msgstr ""
+
+#: templates/index.php:31
+msgid "Name"
+msgstr ""
+
+#: templates/index.php:33
+msgid "Download"
+msgstr ""
+
+#: templates/index.php:37
+msgid "Size"
+msgstr ""
+
+#: templates/index.php:38
+msgid "Modified"
+msgstr ""
+
+#: templates/index.php:38
+msgid "Delete"
+msgstr ""
+
+#: templates/index.php:46
+msgid "Upload too large"
+msgstr ""
+
+#: templates/index.php:48
+msgid ""
+"The files you are trying to upload exceed the maximum size for file uploads "
+"on this server."
+msgstr ""
diff --git a/l10n/templates/media.pot b/l10n/templates/media.pot
new file mode 100644
index 0000000000000000000000000000000000000000..3de067de167f95d4c7e7d04ef923ab55e3c3835e
--- /dev/null
+++ b/l10n/templates/media.pot
@@ -0,0 +1,66 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: appinfo/app.php:31
+msgid "Music"
+msgstr ""
+
+#: templates/music.php:3
+msgid "Play"
+msgstr ""
+
+#: templates/music.php:4 templates/music.php:30
+msgid "Pause"
+msgstr ""
+
+#: templates/music.php:5
+msgid "Previous"
+msgstr ""
+
+#: templates/music.php:6
+msgid "Next"
+msgstr ""
+
+#: templates/music.php:7
+msgid "Mute"
+msgstr ""
+
+#: templates/music.php:8
+msgid "Unmute"
+msgstr ""
+
+#: templates/music.php:28
+msgid "Songs scanned"
+msgstr ""
+
+#: templates/music.php:29
+msgid "Rescan Collection"
+msgstr ""
+
+#: templates/music.php:37
+msgid "Artist"
+msgstr ""
+
+#: templates/music.php:38
+msgid "Album"
+msgstr ""
+
+#: templates/music.php:39
+msgid "Title"
+msgstr ""
diff --git a/l10n/templates/settings.pot b/l10n/templates/settings.pot
new file mode 100644
index 0000000000000000000000000000000000000000..bae6d82bfc44131a398c9ae4f626d3ba8a2f39f0
--- /dev/null
+++ b/l10n/templates/settings.pot
@@ -0,0 +1,130 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-08-28 01:13+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: ajax/openid.php:13 ajax/setlanguage.php:13
+msgid "Authentication error"
+msgstr ""
+
+#: ajax/openid.php:21
+msgid "OpenID Changed"
+msgstr ""
+
+#: ajax/openid.php:23 ajax/setlanguage.php:23
+msgid "Invalid request"
+msgstr ""
+
+#: ajax/setlanguage.php:21
+msgid "Language changed"
+msgstr ""
+
+#: templates/apps.php:8
+msgid "Add your application"
+msgstr ""
+
+#: templates/apps.php:21
+msgid "Select an App"
+msgstr ""
+
+#: templates/apps.php:23
+msgid "-licensed"
+msgstr ""
+
+#: templates/apps.php:23
+msgid "by"
+msgstr ""
+
+#: templates/help.php:8
+msgid "Ask a question"
+msgstr ""
+
+#: templates/help.php:17
+msgid "Problems connecting to help database."
+msgstr ""
+
+#: templates/help.php:18
+msgid "Go there manually."
+msgstr ""
+
+#: templates/help.php:26
+msgid "Answer"
+msgstr ""
+
+#: templates/personal.php:8
+msgid "You use"
+msgstr ""
+
+#: templates/personal.php:8
+msgid "of the available"
+msgstr ""
+
+#: templates/personal.php:13
+msgid "Your password got changed"
+msgstr ""
+
+#: templates/personal.php:14
+msgid "Unable to change your password"
+msgstr ""
+
+#: templates/personal.php:15
+msgid "Current password"
+msgstr ""
+
+#: templates/personal.php:16
+msgid "New password"
+msgstr ""
+
+#: templates/personal.php:17
+msgid "show"
+msgstr ""
+
+#: templates/personal.php:18
+msgid "Change password"
+msgstr ""
+
+#: templates/personal.php:24
+msgid "Language"
+msgstr ""
+
+#: templates/personal.php:30
+msgid "Help translating"
+msgstr ""
+
+#: templates/personal.php:36
+msgid "use this address to connect to your ownCloud in your file manager"
+msgstr ""
+
+#: templates/users.php:16
+msgid "Name"
+msgstr ""
+
+#: templates/users.php:17
+msgid "Password"
+msgstr ""
+
+#: templates/users.php:18 templates/users.php:36
+msgid "Groups"
+msgstr ""
+
+#: templates/users.php:24
+msgid "Create"
+msgstr ""
+
+#: templates/users.php:48
+msgid "Delete"
+msgstr ""
diff --git a/lib/MDB2/Driver/Datatype/sqlite3.php b/lib/MDB2/Driver/Datatype/sqlite3.php
new file mode 100644
index 0000000000000000000000000000000000000000..378d5540cbe853264a5fdbfc9330b99d68d77417
--- /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 0000000000000000000000000000000000000000..0d6b329f0ad2f11e4306bff59b36260379205830
--- /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 0000000000000000000000000000000000000000..4e420b5d0b2c50feb4d167d0151e14cfd307d49f
--- /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 0000000000000000000000000000000000000000..81dc67dea65a04e1d59a7c22cdb641150f36ca9f
--- /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 0000000000000000000000000000000000000000..d488977b158cf1ac8c9df8ce5f81fd93be7d94f5
--- /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 0000000000000000000000000000000000000000..ccf6bb5368854dd66dedbe61d211fa7998ec2cc9
--- /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/app.php b/lib/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..61ea081c6ff4d558370974490e62748439596a8e
--- /dev/null
+++ b/lib/app.php
@@ -0,0 +1,356 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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 manages the apps. It allows them to register and integrate in the
+ * owncloud ecosystem. Furthermore, this class is responsible for installing,
+ * upgrading and removing apps.
+ */
+class OC_App{
+	static private $init = false;
+	static private $apps = array();
+	static private $activeapp = '';
+	static private $navigation = array();
+	static private $settingsForms = array();
+	static private $adminForms = array();
+	static private $personalForms = array();
+
+	/**
+	 * @brief loads all apps
+	 * @returns true/false
+	 *
+	 * This function walks through the owncloud directory and loads all apps
+	 * it can find. A directory contains an app if the file /appinfo/app.php
+	 * exists.
+	 */
+	public static function loadApps(){
+		global $SERVERROOT;
+
+		// Did we allready load everything?
+		if( self::$init ){
+			return true;
+		}
+
+		// Our very own core apps are hardcoded
+		foreach( array('files', 'settings') as $app ){
+			require( $app.'/appinfo/app.php' );
+		}
+
+		// The rest comes here
+		$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' );
+				}
+			}
+		}
+
+		self::$init = true;
+
+		// return
+		return true;
+	}
+
+	/**
+	 * @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 ){
+		if(!OC_Installer::isInstalled($app)){
+			OC_Installer::installShippedApp($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')
+	 *   - version: array with Version (major, minor, bugfix) ( array(1, 0, 2))
+	 *
+	 * The following keys are optional:
+	 *   - order: integer, that influences the position of your application in
+	 *     a list of applications. Lower values come first.
+	 *
+	 */
+	public static function register( $data ){
+		OC_App::$apps[] = $data;
+	}
+
+	/**
+	 * @brief returns information of all apps
+	 * @return array with all information
+	 *
+	 * This function returns all data it got via register().
+	 */
+	public static function get(){
+		return OC_App::$apps;
+	}
+
+	/**
+	 * @brief adds an entry to the navigation
+	 * @param $data array containing the data
+	 * @returns true/false
+	 *
+	 * 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')
+	 *   - href: link to the page
+	 *   - name: Human readable name ('Addressbook')
+	 *
+	 * The following keys are optional:
+	 *   - icon: path to the icon of the app
+	 *   - order: integer, that influences the position of your application in
+	 *     the navigation. Lower values come first.
+	 */
+	public static function addNavigationEntry( $data ){
+		$data['active']=false;
+		if(!isset($data['icon'])){
+			$data['icon']='';
+		}
+		OC_App::$navigation[] = $data;
+		return true;
+	}
+
+	/**
+	 * @brief marks a navigation entry as active
+	 * @param $id id of the entry
+	 * @returns true/false
+	 *
+	 * 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.
+	 */
+	public static function setActiveNavigationEntry( $id ){
+		self::$activeapp = $id;
+		return true;
+	}
+
+	/**
+	 * @brief gets the active Menu entry
+	 * @returns id or empty string
+	 *
+	 * This function returns the id of the active navigation entry (set by
+	 * setActiveNavigationEntry
+	 */
+	public static function getActiveNavigationEntry(){
+		return self::$activeapp;
+	}
+
+	/**
+	 * @brief Returns the Settings Navigation
+	 * @returns associative array
+	 *
+	 * This function returns an array containing all settings pages added. The
+	 * entries are sorted by the key 'order' ascending.
+	 */
+	public static function getSettingsNavigation(){
+		$l=new OC_L10N('core');
+		$admin=array(
+			array( "id" => "core_users", "order" => 2, "href" => OC_Helper::linkTo( "settings", "users.php" ), "name" => $l->t("Users"), "icon" => OC_Helper::imagePath( "settings", "users.svg" )),
+			array( "id" => "core_apps", "order" => 3, "href" => OC_Helper::linkTo( "settings", "apps.php?installed" ), "name" => $l->t("Apps"), "icon" => OC_Helper::imagePath( "settings", "apps.svg" )),
+		);
+		$settings=array(
+			array( "id" => "help", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "help.php" ), "name" => $l->t("Help"), "icon" => OC_Helper::imagePath( "settings", "help.svg" )),
+			array( "id" => "personal", "order" => 1, "href" => OC_Helper::linkTo( "settings", "personal.php" ), "name" => $l->t("Personal"), "icon" => OC_Helper::imagePath( "settings", "personal.svg" ))
+		);
+		if(count(self::$settingsForms)>0){
+			$settings[]=array( "id" => "settings", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "settings.php" ), "name" => $l->t("Settings"), "icon" => OC_Helper::imagePath( "settings", "settings.svg" ));
+		}
+		if(count(self::$adminForms)>0){
+			$admin[]=array( "id" => "admin", "order" => 1000, "href" => OC_Helper::linkTo( "settings", "admin.php" ), "name" => $l->t("Admin"), "icon" => OC_Helper::imagePath( "settings", "admin.svg" ));
+		}
+		if( OC_Group::inGroup( $_SESSION["user_id"], "admin" )){
+			$settings=array_merge($admin,$settings);
+		}
+		$navigation = self::proceedNavigation($settings);
+		return $navigation;
+	}
+
+	/// 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;
+			}
+			else{
+				$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;}' ));
+
+		return $list;
+	}
+	
+	/**
+	 * @brief Read app metadata from the info.xml file
+	 * @param string $appid id of the app or the path of the info.xml file
+	 * @returns array
+	*/
+	public static function getAppInfo($appid){
+		if(is_file($appid)){
+			$file=$appid;
+		}else{
+			$file=OC::$SERVERROOT.'/apps/'.$appid.'/appinfo/info.xml';
+			if(!is_file($file)){
+				return array();
+			}
+		}
+		$data=array();
+		$content=file_get_contents($file);
+		$xml = new SimpleXMLElement($content);
+		$data['info']=array();
+		foreach($xml->children() as $child){
+			$data[$child->getName()]=(string)$child;
+		}
+		return $data;
+	}
+	
+	/**
+	 * @brief Returns the navigation
+	 * @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
+	 * 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
+	 */
+	public static function getNavigation(){
+		$navigation = self::proceedNavigation( self::$navigation );
+		return $navigation;
+	}
+	
+	/**
+	 * get the id of loaded app
+	 * @return string
+	 */
+	public static function getCurrentApp(){
+		global $WEBROOT;
+		$script=substr($_SERVER["SCRIPT_NAME"],strlen($WEBROOT)+1);
+		$topFolder=substr($script,0,strpos($script,'/'));
+		if($topFolder=='apps'){
+			$length=strlen($topFolder);
+			return substr($script,$length+1,strpos($script,'/',$length+1)-$length-1);
+		}else{
+			return $topFolder;
+		}
+	}
+	
+	
+	/**
+	 * get the forms for either settings, admin or personal
+	 */
+	public static function getForms($type){
+		$forms=array();
+		switch($type){
+			case 'settings':
+				$source=self::$settingsForms;
+				break;
+			case 'admin':
+				$source=self::$adminForms;
+				break;
+			case 'personal':
+				$source=self::$personalForms;
+				break;
+		}
+		foreach($source as $form){
+			$forms[]=include $form;
+		}
+		return $forms;
+	}
+	
+	/**
+	 * register a settings form to be shown
+	 */
+	public static function registerSettings($app,$page){
+		self::$settingsForms[]='apps/'.$app.'/'.$page.'.php';
+	}
+	
+	/**
+	 * register an admin form to be shown
+	 */
+	public static function registerAdmin($app,$page){
+		self::$adminForms[]='apps/'.$app.'/'.$page.'.php';
+	}
+	
+	/**
+	 * register a personal form to be shown
+	 */
+	public static function registerPersonal($app,$page){
+		self::$personalForms[]='apps/'.$app.'/'.$page.'.php';
+	}
+	
+	/**
+	 * get a list of all apps in the apps folder
+	 */
+	public static function getAllApps(){
+		$apps=array();
+		$dh=opendir(OC::$SERVERROOT.'/apps');
+		while($file=readdir($dh)){
+			if(is_file(OC::$SERVERROOT.'/apps/'.$file.'/appinfo/app.php')){
+				$apps[]=$file;
+			}
+		}
+		return $apps;
+	}
+}
diff --git a/lib/appconfig.php b/lib/appconfig.php
new file mode 100644
index 0000000000000000000000000000000000000000..c64a15b8938812bfd256fcd4056c5c8b1746e87a
--- /dev/null
+++ b/lib/appconfig.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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/>.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE  `appconfig` (
+ * `appid` VARCHAR( 255 ) NOT NULL ,
+ * `configkey` VARCHAR( 255 ) NOT NULL ,
+ * `configvalue` VARCHAR( 255 ) NOT NULL
+ * )
+ *
+ */
+
+/**
+ * This class provides an easy way for apps to store config values in the
+ * database.
+ */
+class OC_Appconfig{
+	/**
+	 * @brief Get all apps using the config
+	 * @returns array with app ids
+	 *
+	 * This function returns a list of all apps that have at least one
+	 * entry in the appconfig table.
+	 */
+	public static function getApps(){
+		// No magic in here!
+		$query = OC_DB::prepare( 'SELECT DISTINCT( appid ) FROM *PREFIX*appconfig' );
+		$result = $query->execute();
+
+		$apps = array();
+		while( $row = $result->fetchRow()){
+			$apps[] = $row["appid"];
+		}
+
+		return $apps;
+	}
+
+	/**
+	 * @brief Get the available keys for an app
+	 * @param $app the app we are looking for
+	 * @returns array with key names
+	 *
+	 * This function gets all keys of an app. Please note that the values are
+	 * not returned.
+	 */
+	public static function getKeys( $app ){
+		// No magic in here as well
+		$query = OC_DB::prepare( 'SELECT configkey FROM *PREFIX*appconfig WHERE appid = ?' );
+		$result = $query->execute( array( $app ));
+
+		$keys = array();
+		while( $row = $result->fetchRow()){
+			$keys[] = $row["configkey"];
+		}
+
+		return $keys;
+	}
+
+	/**
+	 * @brief Gets the config value
+	 * @param $app app
+	 * @param $key key
+	 * @param $default = null, default value if the key does not exist
+	 * @returns the value or $default
+	 *
+	 * This function gets a value from the appconfig table. If the key does
+	 * not exist the default value will be returnes
+	 */
+	public static function getValue( $app, $key, $default = null ){
+		// At least some magic in here :-)
+		$query = OC_DB::prepare( 'SELECT configvalue FROM *PREFIX*appconfig WHERE appid = ? AND configkey = ?' );
+		$result = $query->execute( array( $app, $key ));
+
+		if( !$result->numRows()){
+			return $default;
+		}
+
+		$row = $result->fetchRow();
+
+		return $row["configvalue"];
+	}
+
+	/**
+	 * @brief sets a value in the appconfig
+	 * @param $app app
+	 * @param $key key
+	 * @param $value value
+	 * @returns true/false
+	 *
+	 * Sets a value. If the key did not exist before it will be created.
+	 */
+	public static function setValue( $app, $key, $value ){
+		// Does the key exist? yes: update. No: insert
+		$exists = self::getKeys( $app );
+
+		// null: does not exist
+		if( !in_array( $key, $exists )){
+			$query = OC_DB::prepare( 'INSERT INTO *PREFIX*appconfig ( appid, configkey, configvalue ) VALUES( ?, ?, ? )' );
+			$query->execute( array( $app, $key, $value ));
+		}
+		else{
+			$query = OC_DB::prepare( 'UPDATE *PREFIX*appconfig SET configvalue = ? WHERE appid = ? AND configkey = ?' );
+			$query->execute( array( $value, $app, $key ));
+		}
+	}
+
+	/**
+	 * @brief Deletes a key
+	 * @param $app app
+	 * @param $key key
+	 * @returns true/false
+	 *
+	 * Deletes a key.
+	 */
+	public static function deleteKey( $app, $key ){
+		// Boring!
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*appconfig WHERE appid = ? AND configkey = ?' );
+		$query->execute( array( $app, $key ));
+
+		return true;
+	}
+
+	/**
+	 * @brief Remove app from appconfig
+	 * @param $app app
+	 * @returns true/false
+	 *
+	 * Removes all keys in appconfig belonging to the app.
+	 */
+	public static function deleteApp( $app ){
+		// Nothing special
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*appconfig WHERE appid = ?' );
+		$query->execute( array( $app ));
+
+		return true;
+	}
+}
diff --git a/lib/base.php b/lib/base.php
new file mode 100644
index 0000000000000000000000000000000000000000..dc3ed4129cdde48ec0b0fa4bfca587865de01a08
--- /dev/null
+++ b/lib/base.php
@@ -0,0 +1,279 @@
+<?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/>.
+ *
+ */
+
+/**
+ * Class that is a namespace for all global OC variables
+ * No, we can not put this class in its own file because it is used by
+ * OC_autoload!
+ */
+class OC{
+	/**
+	 * Assoziative array for autoloading. classname => filename
+	 */
+	public static $CLASSPATH = array();
+	/**
+	 * $_SERVER['DOCUMENTROOT'] but without symlinks
+	 */
+	public static $DOCUMENTROOT = '';
+	/**
+	 * The installation path for owncloud on the server (e.g. /srv/http/owncloud)
+	 */
+	public static $SERVERROOT = '';
+	/**
+	 * the current request path relative to the owncloud root (e.g. files/index.php)
+	 */
+	public static $SUBURI = '';
+	/**
+	 * the owncloud root path for http requests (e.g. owncloud/)
+	 */
+	public static $WEBROOT = '';
+	/**
+	 * the folder that stores that data files for the filesystem of the user (e.g. /srv/http/owncloud/data/myusername/files)
+	 */
+	public static $CONFIG_DATADIRECTORY = '';
+	/**
+	 * the folder that stores the data for the root filesystem (e.g. /srv/http/owncloud/data)
+	 */
+	public static $CONFIG_DATADIRECTORY_ROOT = '';
+
+	/**
+	 * SPL autoload
+	 */
+	public static function autoload($className){
+		if(array_key_exists($className,OC::$CLASSPATH)){
+			require_once OC::$CLASSPATH[$className];
+		}
+		elseif(strpos($className,'OC_')===0){
+			require_once strtolower(str_replace('_','/',substr($className,3)) . '.php');
+		}
+	}
+}
+
+// this requires all our OC_* classes
+spl_autoload_register(array('OC','autoload'));
+
+// set some stuff
+//ob_start();
+error_reporting(E_ALL | E_STRICT);
+
+date_default_timezone_set('Europe/Berlin');
+ini_set('arg_separator.output','&amp;');
+ini_set('session.cookie_httponly','1;');
+session_start();
+
+// calculate the documentroot
+$DOCUMENTROOT=realpath($_SERVER['DOCUMENT_ROOT']);
+$SERVERROOT=str_replace("\\",'/',substr(__FILE__,0,-13));
+$SUBURI=substr(realpath($_SERVER["SCRIPT_FILENAME"]),strlen($SERVERROOT));
+$scriptName=$_SERVER["SCRIPT_NAME"];
+if(substr($scriptName,-1)=='/'){
+	$scriptName.='index.php';
+}
+$WEBROOT=substr($scriptName,0,strlen($scriptName)-strlen($SUBURI));
+
+OC::$SERVERROOT=$SERVERROOT;
+OC::$WEBROOT=$WEBROOT;
+
+if($WEBROOT!='' and $WEBROOT[0]!=='/'){
+	$WEBROOT='/'.$WEBROOT;
+}
+
+// We are going to use OC::* instead of globels soon
+OC::$WEBROOT = $WEBROOT;
+OC::$SERVERROOT = $SERVERROOT;
+OC::$DOCUMENTROOT = $DOCUMENTROOT;
+OC::$SUBURI = $SUBURI;
+
+// set the right include path
+set_include_path($SERVERROOT.'/lib'.PATH_SEPARATOR.$SERVERROOT.'/config'.PATH_SEPARATOR.$SERVERROOT.'/3rdparty'.PATH_SEPARATOR.get_include_path().PATH_SEPARATOR.$SERVERROOT);
+
+//Some libs we really depend on
+require_once('Sabre/autoload.php');
+
+// define runtime variables - unless this already has been done
+if( !isset( $RUNTIME_NOSETUPFS )){
+	$RUNTIME_NOSETUPFS = false;
+}
+if( !isset( $RUNTIME_NOAPPS )){
+	$RUNTIME_NOAPPS = false;
+}
+
+// TODO: we should get rid of this one, too
+// WARNING: to make everything even more confusing, DATADIRECTORY is a var that
+//   changes and DATATIRECTORY_ROOT stays the same, but is set by
+//   "datadirectory". Any questions?
+$CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", "$SERVERROOT/data" );
+
+// redirect to https site if configured
+if( OC_Config::getValue( "forcessl", false )){
+	if(!isset($_SERVER['HTTPS']) or $_SERVER['HTTPS'] != 'on') {
+		$url = "https://". $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
+		header("Location: $url");
+		exit();
+	}
+}
+
+$errors=OC_Util::checkServer();
+$error=(count($errors)>0);
+
+
+// User and Groups
+if( !OC_Config::getValue( "installed", false )){
+	$_SESSION['user_id'] = '';
+}
+
+
+OC_User::useBackend( OC_Config::getValue( "userbackend", "database" ));
+OC_Group::setBackend( OC_Config::getValue( "groupbackend", "database" ));
+
+// Add the stuff we need always
+OC_Util::addScript( "jquery-1.6.2.min" );
+OC_Util::addScript( "jquery-ui-1.8.14.custom.min" );
+OC_Util::addScript( "jquery-showpassword" );
+OC_Util::addScript( "jquery-tipsy" );
+OC_Util::addScript( "js" );
+OC_Util::addScript( "multiselect" );
+OC_Util::addScript('search','result');
+OC_Util::addStyle( "styles" );
+OC_Util::addStyle( "multiselect" );
+OC_Util::addStyle( "jquery-ui-1.8.14.custom" );
+OC_Util::addStyle( "jquery-tipsy" );
+
+// Load Apps
+// This includes plugins for users and filesystems as well
+if(!$error and !$RUNTIME_NOAPPS ){
+	OC_App::loadApps();
+}
+
+// Was in required file ... put it here
+OC_Filesystem::registerStorageType('local','OC_Filestorage_Local',array('datadir'=>'string'));
+
+// Set up file system unless forbidden
+if(!$error and !$RUNTIME_NOSETUPFS ){
+	OC_Util::setupFS();
+}
+
+// Last part: connect some hooks
+OC_HOOK::connect('OC_User', 'post_createUser', 'OC_Connector_Sabre_Principal', 'addPrincipal');
+OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Connector_Sabre_Principal', 'deletePrincipal');
+
+
+
+if($error) {
+	$tmpl = new OC_Template( '', 'error', 'guest' );
+	$tmpl->assign('errors',$errors);
+	$tmpl->printPage();
+	exit;
+}
+
+
+
+
+// FROM Connect.php
+function OC_CONNECT_TEST($path,$user,$password){
+	echo 'connecting...';
+	$remote=OC_Connect::connect($path,$user,$password);
+	if($remote->connected){
+		echo 'done<br/>';
+		if($remote->isLoggedIn()){
+			echo 'logged in, session working<br/>';
+			echo 'trying to get remote files...';
+			$files=$remote->getFiles('');
+			if($files){
+				echo count($files).' files found:<br/>';
+				foreach($files as $file){
+					echo "{$file['type']} {$file['name']}: {$file['size']} bytes<br/>";
+				}
+				echo 'getting file "'.$file['name'].'"...';
+				$size=$file['size'];
+				$file=$remote->getFile('',$file['name']);
+				if(file_exists($file)){
+					$newSize=filesize($file);
+					if($size!=$newSize){
+						echo "fail<br/>Error: $newSize bytes received, $size expected.";
+						echo '<br/><br/>Recieved file:<br/>';
+						readfile($file);
+						unlink($file);
+						return;
+					}
+					OC_Filesystem::fromTmpFile($file,'/remoteFile');
+					echo 'done<br/>';
+					echo 'sending file "burning_avatar.png"...';
+					$res=$remote->sendFile('','burning_avatar.png','','burning_avatar.png');
+					if($res){
+						echo 'done<br/>';
+					}else{
+						echo 'fail<br/>';
+					}
+				}else{
+					echo 'fail<br/>';
+				}
+			}else{
+				echo 'fail<br/>';
+			}
+		}else{
+			echo 'no longer logged in, session fail<br/>';
+		}
+	}else{
+		echo 'fail<br/>';
+	}
+	$remote->disconnect();
+	die();
+}
+
+// From files.php
+function zipAddDir($dir,$zip,$internalDir=''){
+    $dirname=basename($dir);
+    $zip->addEmptyDir($internalDir.$dirname);
+    $internalDir.=$dirname.='/';
+    $files=OC_Files::getdirectorycontent($dir);
+    foreach($files as $file){
+        $filename=$file['name'];
+        $file=$dir.'/'.$filename;
+        if(OC_Filesystem::is_file($file)){
+			$tmpFile=OC_Filesystem::toTmpFile($file);
+			OC_Files::$tmpFiles[]=$tmpFile;
+            $zip->addFile($tmpFile,$internalDir.$filename);
+        }elseif(OC_Filesystem::is_dir($file)){
+            zipAddDir($file,$zip,$internalDir);
+        }
+    }
+}
+
+if(!function_exists('sys_get_temp_dir')) {
+    function sys_get_temp_dir() {
+        if( $temp=getenv('TMP') )        return $temp;
+        if( $temp=getenv('TEMP') )        return $temp;
+        if( $temp=getenv('TMPDIR') )    return $temp;
+        $temp=tempnam(__FILE__,'');
+        if (file_exists($temp)) {
+          unlink($temp);
+          return dirname($temp);
+        }
+        return null;
+    }
+}
+
+require_once('fakedirstream.php');
+
+// FROM search.php
+new OC_Search_Provider_File();
diff --git a/lib/config.php b/lib/config.php
new file mode 100644
index 0000000000000000000000000000000000000000..dafb37edd332131cfaf0c1be40ed83de916fdd30
--- /dev/null
+++ b/lib/config.php
@@ -0,0 +1,189 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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/>.
+ *
+ */
+/*
+ *
+ * An example of config.php
+ *
+ * <?php
+ * $CONFIG = array(
+ *     "database" => "mysql",
+ *     "firstrun" => false,
+ *     "pi" => 3.14
+ * );
+ * ?>
+ *
+ */
+
+/**
+ * This class is responsible for reading and writing config.php, the very basic
+ * configuration file of owncloud.
+ */
+class OC_Config{
+	// associative array key => value
+	private static $cache = array();
+
+	// Is the cache filled?
+	private static $init = false;
+
+	/**
+	 * @brief Lists all available config keys
+	 * @returns array with key names
+	 *
+	 * This function returns all keys saved in config.php. Please note that it
+	 * does not return the values.
+	 */
+	public static function getKeys(){
+		self::readData();
+
+		return array_keys( self::$cache );
+	}
+
+	/**
+	 * @brief Gets a value from config.php
+	 * @param $key key
+	 * @param $default = null default value
+	 * @returns the value or $default
+	 *
+	 * This function gets the value from config.php. If it does not exist,
+	 * $default will be returned.
+	 */
+	public static function getValue( $key, $default = null ){
+		self::readData();
+
+		if( array_key_exists( $key, self::$cache )){
+			return self::$cache[$key];
+		}
+
+		return $default;
+	}
+
+	/**
+	 * @brief Sets a value
+	 * @param $key key
+	 * @param $value value
+	 * @returns true/false
+	 *
+	 * This function sets the value and writes the config.php. If the file can
+	 * not be written, false will be returned.
+	 */
+	public static function setValue( $key, $value ){
+		self::readData();
+
+		// Add change
+		self::$cache[$key] = $value;
+
+		// Write changes
+		self::writeData();
+
+		return true;
+	}
+
+	/**
+	 * @brief Removes a key from the config
+	 * @param $key key
+	 * @returns true/false
+	 *
+	 * This function removes a key from the config.php. If owncloud has no
+	 * write access to config.php, the function will return false.
+	 */
+	public static function deleteKey( $key ){
+		self::readData();
+
+		if( array_key_exists( $key, self::$cache )){
+			// Delete key from cache
+			unset( self::$cache[$key] );
+
+			// Write changes
+			self::writeData();
+		}
+
+		return true;
+	}
+
+	/**
+	 * @brief Loads the config file
+	 * @returns true/false
+	 *
+	 * Reads the config file and saves it to the cache
+	 */
+	private static function readData(){
+		if( self::$init ){
+			return true;
+		}
+
+		global $SERVERROOT;
+
+		if( !file_exists( "$SERVERROOT/config/config.php" )){
+			return false;
+		}
+
+		// Include the file, save the data from $CONFIG
+		include( "$SERVERROOT/config/config.php" );
+		if( isset( $CONFIG ) && is_array( $CONFIG )){
+			self::$cache = $CONFIG;
+		}
+
+		// We cached everything
+		self::$init = true;
+
+		return true;
+	}
+
+	/**
+	 * @brief Writes the config file
+	 * @returns true/false
+	 *
+	 * Saves the config to the config file.
+	 *
+	 * Known flaws: Strings are not escaped properly
+	 */
+	public static function writeData(){
+		// We need the serverroot path
+		global $SERVERROOT;
+
+		// Create a php file ...
+		$content = "<?php\n\$CONFIG = array(\n";
+
+		foreach( self::$cache as $key => $value ){
+			if( is_bool( $value )){
+				$value = $value ? 'true' : 'false';
+				$content .= "\"$key\" => $value,\n";
+			}
+			else{
+				$value = str_replace( "'", "\\'", $value );
+				$content .= "\"$key\" => '$value',\n";
+			}
+		}
+		$content .= ");\n?>\n";
+
+		// Write the file
+		$result=@file_put_contents( "$SERVERROOT/config/config.php", $content );
+		if(!$result) {
+			$tmpl = new OC_Template( '', 'error', 'guest' );
+			$tmpl->assign('errors',array(1=>array('error'=>"Can't write into config directory 'config'",'hint'=>"You can usually fix this by setting the owner of 'config' to the user that the web server uses (".exec('whoami').")")));
+			$tmpl->printPage();
+			exit;
+		}
+		return true;
+	}
+}
diff --git a/lib/connect.php b/lib/connect.php
new file mode 100644
index 0000000000000000000000000000000000000000..22e48750a622ba0211f0bfb63c2207cf88b92722
--- /dev/null
+++ b/lib/connect.php
@@ -0,0 +1,40 @@
+<?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/>.
+*
+*/
+
+/**
+ * Class for connecting multiply ownCloud installations
+ *
+ */
+class OC_Connect{
+	static private $clouds=array();
+
+	static function connect($path,$user,$password){
+		$cloud=new OC_REMOTE_CLOUD($path,$user,$password);
+		if($cloud->connected){
+			self::$clouds[$path]=$cloud;
+			return $cloud;
+		}else{
+			return false;
+		}
+	}
+}
diff --git a/lib/connector/sabre/auth.php b/lib/connector/sabre/auth.php
new file mode 100644
index 0000000000000000000000000000000000000000..1e87c7cee08422484a229fc2e1b93b02c43447a9
--- /dev/null
+++ b/lib/connector/sabre/auth.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * HTTP Basic authentication backend class
+ *
+ * This class can be used by authentication objects wishing to use HTTP Basic
+ * Most of the digest logic is handled, implementors just need to worry about
+ * the validateUserPass method.
+ *
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author James David Low (http://jameslow.com/)
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class OC_Connector_Sabre_Auth extends Sabre_DAV_Auth_Backend_AbstractBasic {
+	/**
+	 * Validates a username and password
+	 *
+	 * This method should return true or false depending on if login
+	 * succeeded.
+	 *
+	 * @return bool
+	 */
+	protected function validateUserPass($username, $password){
+		if(OC_User::login($username,$password)){
+			OC_Util::setUpFS();
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+} 
diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php
new file mode 100644
index 0000000000000000000000000000000000000000..139c6b784b1625e3a7a1a93a0a64ed94207fa6a9
--- /dev/null
+++ b/lib/connector/sabre/directory.php
@@ -0,0 +1,128 @@
+<?php
+/**
+ * Directory class 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sabre_DAV_ICollection, Sabre_DAV_IQuota {
+
+	/**
+	 * Creates a new file in the directory
+	 *
+	 * data is a readable stream resource
+	 *
+	 * @param string $name Name of the file
+	 * @param resource $data Initial payload
+	 * @return void
+	 */
+	public function createFile($name, $data = null) {
+
+		$newPath = $this->path . '/' . $name;
+		OC_Filesystem::file_put_contents($newPath,$data);
+
+	}
+
+	/**
+	 * Creates a new subdirectory
+	 *
+	 * @param string $name
+	 * @return void
+	 */
+	public function createDirectory($name) {
+
+		$newPath = $this->path . '/' . $name;
+		OC_Filesystem::mkdir($newPath);
+
+	}
+
+	/**
+	 * Returns a specific child node, referenced by its name
+	 *
+	 * @param string $name
+	 * @throws Sabre_DAV_Exception_FileNotFound
+	 * @return Sabre_DAV_INode
+	 */
+	public function getChild($name) {
+
+		$path = $this->path . '/' . $name;
+
+		if (!OC_Filesystem::file_exists($path)) throw new Sabre_DAV_Exception_FileNotFound('File with name ' . $path . ' could not be located');
+
+		if (OC_Filesystem::is_dir($path)) {
+
+			return new OC_Connector_Sabre_Directory($path);
+
+		} else {
+
+			return new OC_Connector_Sabre_File($path);
+
+		}
+
+	}
+
+	/**
+	 * Returns an array with all the child nodes
+	 *
+	 * @return Sabre_DAV_INode[]
+	 */
+	public function getChildren() {
+
+		$nodes = array();
+		// foreach(scandir($this->path) as $node) if($node!='.' && $node!='..') $nodes[] = $this->getChild($node);
+		if( OC_Filesystem::is_dir($this->path)){
+			$dh = OC_Filesystem::opendir($this->path);
+			while(( $node = readdir($dh)) !== false ){
+				if($node!='.' && $node!='..'){
+					$nodes[] = $this->getChild($node);
+				}
+			}
+		}
+		return $nodes;
+
+	}
+
+	/**
+	 * Checks if a child exists.
+	 *
+	 * @param string $name
+	 * @return bool
+	 */
+	public function childExists($name) {
+
+		$path = $this->path . '/' . $name;
+		return OC_Filesystem::file_exists($path);
+
+	}
+
+	/**
+	 * Deletes all files in this directory, and then itself
+	 *
+	 * @return void
+	 */
+	public function delete() {
+
+		foreach($this->getChildren() as $child) $child->delete();
+		OC_Filesystem::rmdir($this->path);
+
+	}
+
+	/**
+	 * Returns available diskspace information
+	 *
+	 * @return array
+	 */
+	public function getQuotaInfo() {
+
+		return array(
+			OC_Filesystem::filesize('/'),
+			OC_Filesystem::free_space()
+			);
+
+	}
+
+}
+
diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php
new file mode 100644
index 0000000000000000000000000000000000000000..b049f39c171aa8f39f686c1d268a1e56b3730059
--- /dev/null
+++ b/lib/connector/sabre/file.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * File class 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_DAV_IFile {
+
+	/**
+	 * Updates the data
+	 *
+	 * @param resource $data
+	 * @return void
+	 */
+	public function put($data) {
+
+		OC_Filesystem::file_put_contents($this->path,$data);
+
+	}
+
+	/**
+	 * Returns the data
+	 *
+	 * @return string
+	 */
+	public function get() {
+
+		return OC_Filesystem::file_get_contents($this->path);
+
+	}
+
+	/**
+	 * Delete the current file
+	 *
+	 * @return void
+	 */
+	public function delete() {
+
+		OC_Filesystem::unlink($this->path);
+
+	}
+
+	/**
+	 * Returns the size of the node, in bytes
+	 *
+	 * @return int
+	 */
+	public function getSize() {
+	
+		return OC_Filesystem::filesize($this->path);
+
+	}
+
+	/**
+	 * Returns the ETag for a file
+	 *
+	 * An ETag is a unique identifier representing the current version of the file. If the file changes, the ETag MUST change.
+	 * The ETag is an arbritrary string, but MUST be surrounded by double-quotes.
+	 *
+	 * Return null if the ETag can not effectively be determined
+	 *
+	 * @return mixed
+	 */
+	public function getETag() {
+
+		return null;
+
+	}
+
+	/**
+	 * Returns the mime-type for a file
+	 *
+	 * If null is returned, we'll assume application/octet-stream
+	 *
+	 * @return mixed
+	 */
+	public function getContentType() {
+
+		return OC_Filesystem::getMimeType($this->path);
+
+	}
+}
+
diff --git a/lib/connector/sabre/locks.php b/lib/connector/sabre/locks.php
new file mode 100644
index 0000000000000000000000000000000000000000..7164d9a09c12b0fbf5dee90c33971f80abb921ad
--- /dev/null
+++ b/lib/connector/sabre/locks.php
@@ -0,0 +1,151 @@
+<?php
+/**
+ * The Lock manager allows you to handle all file-locks centrally.
+ *
+ * This Lock Manager stores all its data in a database. You must pass a PDO
+ * connection object in the constructor.
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE locks (
+ *   `id` INTEGER UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ *   `userid` VARCHAR(200),
+ *   `owner` VARCHAR(100),
+ *   `timeout` INTEGER UNSIGNED,
+ *   `created` INTEGER,
+ *   `token` VARCHAR(100),
+ *   `scope` TINYINT,
+ *   `depth` TINYINT,
+ *   `uri` text
+ * );
+ *
+ */
+class OC_Connector_Sabre_Locks extends Sabre_DAV_Locks_Backend_Abstract {
+
+	/**
+	 * Returns a list of Sabre_DAV_Locks_LockInfo objects
+	 *
+	 * This method should return all the locks for a particular uri, including
+	 * locks that might be set on a parent uri.
+	 *
+	 * If returnChildLocks is set to true, this method should also look for
+	 * any locks in the subtree of the uri for locks.
+	 *
+	 * @param string $uri
+	 * @param bool $returnChildLocks
+	 * @return array
+	 */
+	public function getLocks($uri, $returnChildLocks) {
+
+		// NOTE: the following 10 lines or so could be easily replaced by
+		// pure sql. MySQL's non-standard string concatination prevents us
+		// from doing this though.
+		$query = 'SELECT * FROM *PREFIX*locks WHERE userid = ? AND (created + timeout) > ? AND ((uri = ?)';
+		$params = array(OC_User::getUser(),time(),$uri);
+
+		// We need to check locks for every part in the uri.
+		$uriParts = explode('/',$uri);
+
+		// We already covered the last part of the uri
+		array_pop($uriParts);
+
+		$currentPath='';
+
+		foreach($uriParts as $part) {
+
+			if ($currentPath) $currentPath.='/';
+			$currentPath.=$part;
+
+			$query.=' OR (depth!=0 AND uri = ?)';
+			$params[] = $currentPath;
+
+		}
+
+		if ($returnChildLocks) {
+
+			$query.=' OR (uri LIKE ?)';
+			$params[] = $uri . '/%';
+
+		}
+		$query.=')';
+
+		$stmt = OC_DB::prepare($query);
+		$result = $stmt->execute($params);
+		
+		$lockList = array();
+		while( $row = $result->fetchRow()){
+
+			$lockInfo = new Sabre_DAV_Locks_LockInfo();
+			$lockInfo->owner = $row['owner'];
+			$lockInfo->token = $row['token'];
+			$lockInfo->timeout = $row['timeout'];
+			$lockInfo->created = $row['created'];
+			$lockInfo->scope = $row['scope'];
+			$lockInfo->depth = $row['depth'];
+			$lockInfo->uri   = $row['uri'];
+			$lockList[] = $lockInfo;
+
+		}
+
+		return $lockList;
+
+	}
+
+	/**
+	 * Locks a uri
+	 *
+	 * @param string $uri
+	 * @param Sabre_DAV_Locks_LockInfo $lockInfo
+	 * @return bool
+	 */
+	public function lock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+		// We're making the lock timeout 5 minutes
+		$lockInfo->timeout = 300;
+		$lockInfo->created = time();
+		$lockInfo->uri = $uri;
+
+		$locks = $this->getLocks($uri,false);
+		$exists = false;
+		foreach($locks as $k=>$lock) {
+			if ($lock->token == $lockInfo->token) $exists = true;
+		}
+	
+		if ($exists) {
+			$query = OC_DB::prepare( 'UPDATE *PREFIX*locks SET owner = ?, timeout = ?, scope = ?, depth = ?, uri = ?, created = ? WHERE userid = ? AND token = ?' );
+			$result = $query->execute( array($lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,OC_User::getUser(),$lockInfo->token));
+		} else {
+			$query = OC_DB::prepare( 'INSERT INTO *PREFIX*locks (userid,owner,timeout,scope,depth,uri,created,token) VALUES (?,?,?,?,?,?,?,?)' );
+			$result = $query->execute( array(OC_User::getUser(),$lockInfo->owner,$lockInfo->timeout,$lockInfo->scope,$lockInfo->depth,$uri,$lockInfo->created,$lockInfo->token));
+		}
+
+		return true;
+
+	}
+
+	/**
+	 * Removes a lock from a uri
+	 *
+	 * @param string $uri
+	 * @param Sabre_DAV_Locks_LockInfo $lockInfo
+	 * @return bool
+	 */
+	public function unlock($uri,Sabre_DAV_Locks_LockInfo $lockInfo) {
+
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*locks WHERE userid = ? AND uri=? AND token=?' );
+		$result = $query->execute( array(OC_User::getUser(),$uri,$lockInfo->token));
+
+		return $result->numRows() === 1;
+
+	}
+
+}
diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php
new file mode 100644
index 0000000000000000000000000000000000000000..ace572a1ee3511e4f293d9045245288df91f91d2
--- /dev/null
+++ b/lib/connector/sabre/node.php
@@ -0,0 +1,157 @@
+<?php
+/**
+ * Base node-class 
+ *
+ * The node class implements the method used by both the File and the Directory classes 
+ * 
+ * @package Sabre
+ * @subpackage DAV
+ * @copyright Copyright (C) 2007-2011 Rooftop Solutions. All rights reserved.
+ * @author Evert Pot (http://www.rooftopsolutions.nl/) 
+ * @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE IF NOT EXISTS `properties` (
+ *   `userid` varchar(200) COLLATE utf8_unicode_ci NOT NULL,
+ *   `propertypath` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
+ *   `propertyname` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
+ *   `propertyvalue` text COLLATE utf8_unicode_ci NOT NULL
+ * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ *
+ */
+abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IProperties {
+
+	/**
+	 * The path to the current node
+	 *
+	 * @var string
+	 */
+	protected $path;
+
+	/**
+	 * Sets up the node, expects a full path name
+	 *
+	 * @param string $path
+	 * @return void
+	 */
+	public function __construct($path) {
+		$this->path = $path;
+	}
+
+
+
+	/**
+	 * Returns the name of the node
+	 *
+	 * @return string
+	 */
+	public function getName() {
+
+		list(, $name)  = Sabre_DAV_URLUtil::splitPath($this->path);
+		return $name;
+
+	}
+
+	/**
+	 * Renames the node
+	 *
+	 * @param string $name The new name
+	 * @return void
+	 */
+	public function setName($name) {
+
+		list($parentPath, ) = Sabre_DAV_URLUtil::splitPath($this->path);
+		list(, $newName) = Sabre_DAV_URLUtil::splitPath($name);
+
+		$newPath = $parentPath . '/' . $newName;
+		$oldPath = $this->path;
+
+		OC_Filesystem::rename($this->path,$newPath);
+	
+		$this->path = $newPath;
+		
+		$query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertypath = ? WHERE userid = ? AND propertypath = ?' );
+		$query->execute( array( $newPath,OC_User::getUser(), $oldPath ));
+
+	}
+
+
+
+	/**
+	 * Returns the last modification time, as a unix timestamp
+	 *
+	 * @return int
+	 */
+	public function getLastModified() {
+
+		return OC_Filesystem::filemtime($this->path);
+
+	}
+
+	/**
+	 * Updates properties on this node,
+	 *
+	 * @param array $mutations
+	 * @see Sabre_DAV_IProperties::updateProperties
+	 * @return bool|array
+	 */
+	public function updateProperties($properties) {
+		$existing = $this->getProperties(array());
+		foreach($properties as $propertyName => $propertyValue) {
+			// If it was null, we need to delete the property
+			if (is_null($propertyValue)) {
+				if(array_key_exists( $propertyName, $existing )){
+					$query = OC_DB::prepare( 'DELETE FROM *PREFIX*properties WHERE userid = ? AND propertypath = ? AND propertyname = ?' );
+					$query->execute( array( OC_User::getUser(), $this->path, $propertyName ));
+				}
+			}
+			else {
+				if(!array_key_exists( $propertyName, $existing )){
+					$query = OC_DB::prepare( 'INSERT INTO *PREFIX*properties (userid,propertypath,propertyname,propertyvalue) VALUES(?,?,?,?)' );
+					$query->execute( array( OC_User::getUser(), $this->path, $propertyName,$propertyValue ));
+				}
+				else{
+					$query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyvalue = ? WHERE userid = ? AND propertypath = ? AND propertyname = ?' );
+					$query->execute( array( $propertyValue,OC_User::getUser(), $this->path, $propertyName ));
+				}
+			}
+
+		}
+		return true;
+	}
+
+	/**
+	 * Returns a list of properties for this nodes.;
+	 *
+	 * The properties list is a list of propertynames the client requested, encoded as xmlnamespace#tagName, for example: http://www.example.org/namespace#author
+	 * If the array is empty, all properties should be returned
+	 *
+	 * @param array $properties
+	 * @return void
+	 */
+	function getProperties($properties) {
+		// At least some magic in here :-)
+		$query = OC_DB::prepare( 'SELECT * FROM *PREFIX*properties WHERE userid = ? AND propertypath = ?' );
+		$result = $query->execute( array( OC_User::getUser(), $this->path ));
+
+		$existing = array();
+		while( $row = $result->fetchRow()){
+			$existing[$row['propertyname']] = $row['propertyvalue'];
+		}
+
+		if(count($properties) == 0){
+			return $existing;
+		}
+		
+		// if the array was empty, we need to return everything
+		$props = array();
+		foreach($properties as $property) {
+			if (isset($existing[$property])) $props[$property] = $existing[$property];
+		}
+		return $props;
+	}
+}
diff --git a/lib/connector/sabre/principal.php b/lib/connector/sabre/principal.php
new file mode 100644
index 0000000000000000000000000000000000000000..9c386f85e15f889f249ac117d2d78dfae05129b1
--- /dev/null
+++ b/lib/connector/sabre/principal.php
@@ -0,0 +1,181 @@
+<?php
+
+class OC_Connector_Sabre_Principal implements Sabre_DAVACL_IPrincipalBackend {
+	/**
+	 * TODO: write doc
+	 */
+	public static function addPrincipal($params){
+		// Add the user
+		$uri = 'principals/'.$params['uid'];
+		$displayname = $params['uid'];
+		$query = OC_DB::prepare('INSERT INTO *PREFIX*principals (uri,displayname) VALUES(?,?)');
+		$query->execute(array($uri,$displayname));
+		
+		// Add calendar and addressbook read and write support (sharing calendars)
+		$uri = 'principals/'.$params['uid'].'/calendar-proxy-read';
+		$displayname = null;
+		$query->execute(array($uri,$displayname));
+		$uri = 'principals/'.$params['uid'].'/calendar-proxy-write';
+		$query->execute(array($uri,$displayname));
+		$uri = 'principals/'.$params['uid'].'/addressbook-proxy-read';
+		$query->execute(array($uri,$displayname));
+		$uri = 'principals/'.$params['uid'].'/addressbook-proxy-write';
+		$query->execute(array($uri,$displayname));
+
+		return true;
+	}
+	
+	/**
+	 * TODO: write doc
+	 */
+	public static function deletePrincipal($params){
+		$query = OC_DB::prepare('SELECT * FROM *PREFIX*principals');
+		$result = $query->execute();
+
+		$deleteprincipal = OC_DB::prepare('DELETE FROM *PREFIX*principals WHERE id = ?');
+		$deletegroup = OC_DB::prepare('DELETE FROM *PREFIX*principalgroups WHERE principal_id = ? OR member_id = ?');
+		// We have to delete the principals and relations! Principals include 
+		while($row = $result->fetchRow()){
+			// Checking if the principal is in the prefix
+			$array = explode('/',$row['uri']);
+			if ($array[1] != $params['uid']) continue;
+			$deleteprincipal->execute(array($row['id']));
+			$deletegroup->execute(array($row['id'],$row['id']));
+		}
+		return true;
+	}
+	/**
+	 * Returns a list of principals based on a prefix.
+	 *
+	 * This prefix will often contain something like 'principals'. You are only
+	 * expected to return principals that are in this base path.
+	 *
+	 * You are expected to return at least a 'uri' for every user, you can
+	 * return any additional properties if you wish so. Common properties are:
+	 *   {DAV:}displayname
+	 *
+	 * @param string $prefixPath
+	 * @return array
+	 */
+	public function getPrincipalsByPrefix( $prefixPath ){
+		$query = OC_DB::prepare('SELECT * FROM *PREFIX*principals');
+		$result = $query->execute();
+
+		$principals = array();
+
+		while($row = $result->fetchRow()){
+			// Checking if the principal is in the prefix
+			list($rowPrefix) = Sabre_DAV_URLUtil::splitPath($row['uri']);
+			if ($rowPrefix !== $prefixPath) continue;
+
+			$principals[] = array(
+				'uri' => $row['uri'],
+				'{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri'])
+			);
+
+		}
+
+		return $principals;
+	}
+
+	/**
+	 * Returns a specific principal, specified by it's path.
+	 * The returned structure should be the exact same as from
+	 * getPrincipalsByPrefix.
+	 *
+	 * @param string $path
+	 * @return array
+	 */
+	public function getPrincipalByPath($path) {
+		$query = OC_DB::prepare('SELECT * FROM *PREFIX*principals WHERE uri=?');
+		$result = $query->execute(array($path));
+
+		$users = array();
+
+		$row = $result->fetchRow();
+		if (!$row) return;
+
+		return array(
+			'id'  => $row['id'],
+			'uri' => $row['uri'],
+			'{DAV:}displayname' => $row['displayname']?$row['displayname']:basename($row['uri'])
+		);
+
+	}
+
+	/**
+	 * Returns the list of members for a group-principal
+	 *
+	 * @param string $principal
+	 * @return array
+	 */
+	public function getGroupMemberSet($principal) {
+		$principal = $this->getPrincipalByPath($principal);
+		if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+
+		$query = OC_DB::prepare('SELECT principals.uri as uri FROM *PREFIX*principalgroups AS groupmembers LEFT JOIN *PREFIX*principals AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
+		$result = $query->execute(array($principal['id']));
+	
+		$return = array();
+		while ($row = $result->fetchRow()){
+			$return[] = $row['uri'];
+		}
+		return $return;
+	}
+
+	/**
+	 * Returns the list of groups a principal is a member of
+	 *
+	 * @param string $principal
+	 * @return array
+	 */
+	public function getGroupMembership($principal) {
+		$principal = $this->getPrincipalByPath($principal);
+		if (!$principal) throw new Sabre_DAV_Exception('Principal not found');
+
+		$query = OC_DB::prepare('SELECT principals.uri as uri FROM *PREFIX*principalgroups AS groupmembers LEFT JOIN *PREFIX*principals AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.member_id = ?');
+		$result = $query->execute(array($principal['id']));
+
+		$return = array();
+		while ($row = $result->fetchRow()){
+			$return[] = $row['uri'];
+		}
+		return $return;
+	}
+
+	/**
+	 * Updates the list of group members for a group principal.
+	 *
+	 * The principals should be passed as a list of uri's.
+	 *
+	 * @param string $principal
+	 * @param array $members
+	 * @return void
+	 */
+	public function setGroupMemberSet($principal, array $members) {
+		$query = OC_DB::prepare('SELECT id, uri FROM *PREFIX*principals WHERE uri IN (? '.str_repeat(', ?', count($members)).')');
+		$result = $query->execute(array_merge(array($principal), $members));
+
+		$memberIds = array();
+		$principalId = null;
+
+		while($row = $$result->fetchRow()) {
+			if ($row['uri'] == $principal) {
+				$principalId = $row['id'];
+			}
+			else{
+				$memberIds[] = $row['id'];
+			}
+		}
+		if (!$principalId) throw new Sabre_DAV_Exception('Principal not found');
+
+		// Wiping out old members
+		$query = OC_DB::prepare('DELETE FROM *PREFIX*principalgroups WHERE principal_id = ?');
+		$query->execute(array($principalID));
+
+		$query = OC_DB::prepare('INSERT INTO *PREFIX*principalgroups (principal_id, member_id) VALUES (?, ?);');
+		foreach($memberIds as $memberId) {
+			$query->execute(array($principalId, $memberId));
+		}
+	}
+}
diff --git a/lib/crypt.php b/lib/crypt.php
new file mode 100755
index 0000000000000000000000000000000000000000..6002067948040a712517881ec1b4396a5bc5a2c2
--- /dev/null
+++ b/lib/crypt.php
@@ -0,0 +1,165 @@
+<?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/>.
+ *
+ */
+
+
+
+// Todo:
+//  - Crypt/decrypt button in the userinterface
+//  - Setting if crypto should be on by default
+//  - Add a setting "Don´t encrypt files larger than xx because of performance reasons"
+//  - Transparent decrypt/encrpt in filesystem.php. Autodetect if a file is encrypted (.encrypted extensio)
+//  - Don't use a password directly as encryption key. but a key which is stored on the server and encrypted with the user password. -> password change faster
+//  - IMPORTANT! Check if the block lenght of the encrypted data stays the same
+
+
+require_once('Crypt_Blowfish/Blowfish.php');
+
+/**
+ * This class is for crypting and decrypting
+ */
+class OC_Crypt {
+
+        static $encription_extension='.encrypted';
+
+	public static function init($login,$password) {
+		$_SESSION['user_password'] = $password;  // save the password as passcode for the encryption
+		if(OC_User::isLoggedIn()){
+			// does key exist?
+			if(!file_exists(OC_Config::getValue( "datadirectory").'/'.$login.'/encryption.key')){
+				OC_Crypt::createkey($_SESSION['user_password']);
+			}
+		}
+	}
+
+
+
+	public static function createkey($passcode) {
+		if(OC_User::isLoggedIn()){
+			// generate a random key
+			$key=mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999).mt_rand(10000,99999);
+
+			// encrypt the key with the passcode of the user
+			$enckey=OC_Crypt::encrypt($key,$passcode);
+
+			// Write the file
+		        $username=OC_USER::getUser();
+			@file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $enckey );
+		}
+	}
+
+	public static function changekeypasscode( $newpasscode) {
+		if(OC_User::isLoggedIn()){
+		        $username=OC_USER::getUser();
+
+			// read old key
+			$key=file_get_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key');
+
+			// decrypt key with old passcode
+			$key=OC_Crypt::decrypt($key, $_SESSION['user_password']);
+
+			// encrypt again with new passcode
+			$key=OC_Crypt::encrypt($key,$newpassword);
+
+			// store the new key
+			file_put_contents(OC_Config::getValue( "datadirectory").'/'.$username.'/encryption.key', $key );
+
+			 $_SESSION['user_password']=$newpasscode;
+		}
+	}
+
+	/**
+	 * @brief encrypts an content
+	 * @param $content the cleartext message you want to encrypt
+	 * @param $key the encryption key
+	 * @returns encrypted content
+	 *
+	 * This function encrypts an content
+	 */
+	public static function encrypt( $content, $key) {
+		$bf = new Crypt_Blowfish($key);
+		return($bf->encrypt($content));
+	}
+
+
+        /**
+         * @brief decryption of an content
+         * @param $content the cleartext message you want to decrypt
+         * @param $key the encryption key
+         * @returns cleartext content
+         *
+         * This function decrypts an content
+         */
+        public static function decrypt( $content, $key) {
+		$bf = new Crypt_Blowfish($key);
+		return($bf->encrypt($contents));
+        }       
+
+
+        /**
+         * @brief encryption of a file
+         * @param $filename
+         * @param $key the encryption key
+         *
+         * This function encrypts a file
+         */
+	public static function encryptfile( $filename, $key) {
+		$handleread  = fopen($filename, "rb");
+		if($handleread<>FALSE) {
+			$handlewrite = fopen($filename.OC_Crypt::$encription_extension, "wb");
+			while (!feof($handleread)) {
+				$content = fread($handleread, 8192);
+				$enccontent=OC_CRYPT::encrypt( $content, $key);
+				fwrite($handlewrite, $enccontent);
+			}
+			fclose($handlewrite);
+			unlink($filename);
+		}
+		fclose($handleread);
+	}
+
+
+        /**
+         * @brief decryption of a file
+         * @param $filename
+         * @param $key the decryption key
+         *
+         * This function decrypts a file
+         */
+	public static function decryptfile( $filename, $key) {
+		$handleread  = fopen($filename.OC_Crypt::$encription_extension, "rb");
+		if($handleread<>FALSE) {
+			$handlewrite = fopen($filename, "wb");
+			while (!feof($handleread)) {
+				$content = fread($handleread, 8192);
+				$enccontent=OC_CRYPT::decrypt( $content, $key);
+				fwrite($handlewrite, $enccontent);
+			}
+			fclose($handlewrite);
+			unlink($filename.OC_Crypt::$encription_extension);
+		}
+		fclose($handleread);
+	}
+
+
+
+
+}
diff --git a/lib/db.php b/lib/db.php
new file mode 100644
index 0000000000000000000000000000000000000000..414525ae20706103b7f1e4d9288d3552440a05b8
--- /dev/null
+++ b/lib/db.php
@@ -0,0 +1,403 @@
+<?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/>.
+ *
+ */
+
+/**
+ * This class manages the access to the database. It basically is a wrapper for
+ * MDB2 with some adaptions.
+ */
+class OC_DB {
+	static private $DBConnection=false;
+	static private $schema=false;
+	static private $affected=0;
+	static private $result=false;
+
+	/**
+	 * @brief connects to the database
+	 * @returns true if connection can be established or nothing (die())
+	 *
+	 * Connects to the database as specified in config.php
+	 */
+	static public function connect(){
+		// The global data we need
+		$CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" );;
+		$CONFIG_DBHOST = OC_Config::getValue( "dbhost", "" );;
+		$CONFIG_DBUSER = OC_Config::getValue( "dbuser", "" );;
+		$CONFIG_DBPASSWORD = OC_Config::getValue( "dbpassword", "" );;
+		$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );;
+		global $SERVERROOT;
+		$datadir=OC_Config::getValue( "datadirectory", "$SERVERROOT/data" );
+
+		// do nothing if the connection already has been established
+		if(!self::$DBConnection){
+			// Require MDB2.php (not required in the head of the file so we only load it when needed)
+			require_once('MDB2.php');
+
+			// Prepare options array
+			$options = array(
+			  'portability' => MDB2_PORTABILITY_ALL,
+			  'log_line_break' => '<br>',
+			  'idxname_format' => '%s',
+			  'debug' => true,
+			  'quote_identifier' => true  );
+
+			// Add the dsn according to the database type
+			if( $CONFIG_DBTYPE == 'sqlite' or $CONFIG_DBTYPE == 'sqlite3' ){
+				// sqlite
+				$dsn = array(
+				  'phptype'  => $CONFIG_DBTYPE,
+				  'database' => "$datadir/$CONFIG_DBNAME.db",
+				  'mode' => '0644' );
+			}
+			elseif( $CONFIG_DBTYPE == 'mysql' ){
+				// MySQL
+				$dsn = array(
+				  'phptype'  => 'mysql',
+				  'username' => $CONFIG_DBUSER,
+				  'password' => $CONFIG_DBPASSWORD,
+				  'hostspec' => $CONFIG_DBHOST,
+				  'database' => $CONFIG_DBNAME );
+			}
+			elseif( $CONFIG_DBTYPE == 'pgsql' ){
+				// PostgreSQL
+				$dsn = array(
+				  'phptype'  => 'pgsql',
+				  'username' => $CONFIG_DBUSER,
+				  'password' => $CONFIG_DBPASSWORD,
+				  'hostspec' => $CONFIG_DBHOST,
+				  'database' => $CONFIG_DBNAME );
+			}
+
+			// Try to establish connection
+			self::$DBConnection = MDB2::factory( $dsn, $options );
+
+			// Die if we could not connect
+			if( PEAR::isError( self::$DBConnection )){
+				echo( '<b>can not connect to database, using '.$CONFIG_DBTYPE.'. ('.self::$DBConnection->getUserInfo().')</center>');
+				$error = self::$DBConnection->getMessage();
+				error_log( $error);
+				error_log( self::$DBConnection->getUserInfo());
+				die( $error );
+			}
+
+			// 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!
+		return true;
+	}
+
+	/**
+	 * @brief SQL query
+	 * @param $query Query string
+	 * @returns result as MDB2_Result
+	 *
+	 * SQL query via MDB2 query()
+	 */
+	static public function query( $query ){
+		// Optimize the query
+		$query = self::processQuery( $query );
+
+		self::connect();
+		//fix differences between sql versions
+
+		// return the result
+		$result = self::$DBConnection->exec( $query );
+
+		// Die if we have an error (error means: bad query, not 0 results!)
+		if( PEAR::isError($result)) {
+			$entry = 'DB Error: "'.$result->getMessage().'"<br />';
+			$entry .= 'Offending command was: '.$query.'<br />';
+			error_log( $entry );
+			die( $entry );
+		}
+
+		return $result;
+	}
+
+	/**
+	 * @brief Prepare a SQL query
+	 * @param $query Query string
+	 * @returns prepared SQL query
+	 *
+	 * SQL query via MDB2 prepare(), needs to be execute()'d!
+	 */
+	static public function prepare( $query ){
+		// Optimize the query
+		$query = self::processQuery( $query );
+
+		self::connect();
+		// return the result
+		$result = self::$DBConnection->prepare( $query );
+
+		// Die if we have an error (error means: bad query, not 0 results!)
+		if( PEAR::isError($result)) {
+			$entry = 'DB Error: "'.$result->getMessage().'"<br />';
+			$entry .= 'Offending command was: '.$query.'<br />';
+			error_log( $entry );
+			die( $entry );
+		}
+
+		return $result;
+	}
+
+	/**
+	 * @brief gets last value of autoincrement
+	 * @returns id
+	 *
+	 * MDB2 lastInsertID()
+	 *
+	 * Call this method right after the insert command or other functions may
+	 * cause trouble!
+	 */
+	public static function insertid(){
+		self::connect();
+		return self::$DBConnection->lastInsertID();
+	}
+
+	/**
+	 * @brief Disconnect
+	 * @returns true/false
+	 *
+	 * This is good bye, good bye, yeah!
+	 */
+	public static function disconnect(){
+		// Cut connection if required
+		if(self::$DBConnection){
+			self::$DBConnection->disconnect();
+			self::$DBConnection=false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * @brief Escapes bad characters
+	 * @param $string string with dangerous characters
+	 * @returns escaped string
+	 *
+	 * MDB2 escape()
+	 */
+	public static function escape( $string ){
+		self::connect();
+		return self::$DBConnection->escape( $string );
+	}
+
+	/**
+	 * @brief saves database scheme to xml file
+	 * @param $file name of file
+	 * @returns true/false
+	 *
+	 * TODO: write more documentation
+	 */
+	public static function getDbStructure( $file ){
+		self::connectScheme();
+
+		// write the scheme
+		$definition = self::$schema->getDefinitionFromDatabase();
+		$dump_options = array(
+			'output_mode' => 'file',
+			'output' => $file,
+			'end_of_line' => "\n"
+		);
+		self::$schema->dumpDatabase( $definition, $dump_options, MDB2_SCHEMA_DUMP_STRUCTURE );
+
+		return true;
+	}
+
+	/**
+	 * @brief Creates tables from XML file
+	 * @param $file file to read structure from
+	 * @returns true/false
+	 *
+	 * TODO: write more documentation
+	 */
+	public static function createDbFromStructure( $file ){
+		$CONFIG_DBNAME  = OC_Config::getValue( "dbname", "owncloud" );
+		$CONFIG_DBTABLEPREFIX = OC_Config::getValue( "dbtableprefix", "oc_" );
+		$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
+
+		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 );
+		if( $CONFIG_DBTYPE == 'pgsql' ){ //mysql support it too but sqlite don't
+			$content = str_replace( '<default>0000-00-00 00:00:00</default>', '<default>CURRENT_TIMESTAMP</default>', $content );
+		}
+		file_put_contents( $file2, $content );
+
+		// Try to create tables
+		$definition = self::$schema->parseDatabaseDefinitionFile( $file2 );
+		
+		// Delete our temporary file
+		unlink( $file2 );
+
+		// Die in case something went wrong
+		if( $definition instanceof MDB2_Schema_Error ){
+			die( $definition->getMessage().': '.$definition->getUserInfo());
+		}
+// 		if(OC_Config::getValue('dbtype','sqlite')=='sqlite'){
+// 			$definition['overwrite']=true;//always overwrite for sqlite
+// 		}
+		$ret=self::$schema->createDatabase( $definition );
+
+		// Die in case something went wrong
+		if( $ret instanceof MDB2_Error ){
+			die ($ret->getMessage() . ': ' . $ret->getUserInfo());
+		}
+
+		return true;
+	}
+
+	/**
+	 * @brief connects to a MDB2 database scheme
+	 * @returns true/false
+	 *
+	 * Connects to a MDB2 database scheme
+	 */
+	private static function connectScheme(){
+		// We need a database connection
+		self::connect();
+
+		// Connect if this did not happen before
+		if(!self::$schema){
+			require_once('MDB2/Schema.php');
+			self::$schema=MDB2_Schema::factory(self::$DBConnection);
+		}
+
+		return true;
+	}
+
+	/**
+	 * @brief does minor chages to query
+	 * @param $query Query string
+	 * @returns corrected query string
+	 *
+	 * This function replaces *PREFIX* with the value of $CONFIG_DBTABLEPREFIX
+	 * 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
+		$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?
+		if( $CONFIG_DBTYPE == 'sqlite' ){
+			$query = str_replace( '`', '\'', $query );
+		}
+		elseif( $CONFIG_DBTYPE == 'pgsql' ){
+			$query = str_replace( '`', '"', $query );
+		}
+
+		// replace table name prefix
+		$query = str_replace( '*PREFIX*', $CONFIG_DBTABLEPREFIX, $query );
+
+		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);
+		}
+	}
+	
+	/**
+	 * Start a transaction or set a savepoint.
+	 * @param string $savePoint (optional) name of the savepoint to set
+	 */
+	public static function beginTransaction($savePoint=''){
+		self::connect();
+		if (!self::$DBConnection->supports('transactions')) {
+			return false;
+		}
+		if($savePoint && !self::$DBConnection->supports('savepoints')){
+			return false;
+		}
+		if($savePoint){
+			self::$DBConnection->beginTransaction($savePoint);
+		}else{
+			self::$DBConnection->beginTransaction();
+		}
+	}
+
+	/**
+	 * Commit the database changes done during a transaction that is in progress or release a savepoint.
+	 * @param string $savePoint (optional) name of the savepoint to commit
+	 */
+	public static function commit($savePoint=''){
+		self::connect();
+		if(!self::$DBConnection->inTransaction()){
+			return false;
+		}
+		if($savePoint){
+			self::$DBConnection->commit($savePoint);
+		}else{
+			self::$DBConnection->commit();
+		}
+	}
+}
diff --git a/lib/fakedirstream.php b/lib/fakedirstream.php
new file mode 100644
index 0000000000000000000000000000000000000000..fa3e64da62c0bd4e44c2141113c79c8d158a7a75
--- /dev/null
+++ b/lib/fakedirstream.php
@@ -0,0 +1,45 @@
+<?php
+global $FAKEDIRS;
+$FAKEDIRS=array();
+
+class fakeDirStream{
+	private $name;
+	private $data;
+	private $index;
+
+	public function dir_opendir($path,$options){
+		global $FAKEDIRS;
+		$url=parse_url($path);
+		$this->name=substr($path,strlen('fakedir://'));
+		$this->index=0;
+		if(isset($FAKEDIRS[$this->name])){
+			$this->data=$FAKEDIRS[$this->name];
+		}else{
+			$this->data=array();
+		}
+		return true;
+	}
+
+	public function dir_readdir(){
+		if($this->index>=count($this->data)){
+			return false;
+		}
+		$filename=$this->data[$this->index];
+		$this->index++;
+		return $filename;
+	}
+
+	public function dir_closedir() {
+		$this->data=false;
+		$this->name='';
+		return true;
+	}
+
+	public function dir_rewinddir() {
+		$this->index=0;
+		return true;
+	}
+}
+ 
+stream_wrapper_register("fakedir", "fakeDirStream");
+
diff --git a/lib/fileproxy.php b/lib/fileproxy.php
new file mode 100644
index 0000000000000000000000000000000000000000..549b7015a6a11aef72b4d3fdceb0f7b310cfdd12
--- /dev/null
+++ b/lib/fileproxy.php
@@ -0,0 +1,111 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2011 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+/**
+ * Class for manipulating filesystem requests
+ *
+ * Manipulation happens by using 2 kind of proxy operations, pre and post proxies
+ * that manipulate the filesystem call and the result of the call respectively
+ *
+ * A pre-proxy recieves the filepath as arugments (or 2 filespaths in case of operations like copy or move) and return a boolean
+ * If a pre-proxy returnes false the file operation will be canceled
+ * All filesystem operations have a pre-proxy
+ *
+ * A post-proxy recieves 2 arguments, the filepath and the result of the operation.
+ * The return calue of the post-proxy will be used as the new result of the operation
+ * The operations that have a post-proxy are
+ *      file_get_contents, is_file, is_dir, file_exists, stat, is_readable, is_writable, fileatime, filemtime, filectime, file_get_contents, getMimeType, hash, free_space and search
+ */
+
+class OC_FileProxy{
+	private static $proxies=array();
+	
+	/**
+	 * check if this proxy implments a specific proxy operation
+	 * @param string #proxy name of the proxy operation
+	 * @return bool
+	 */
+	public function provides($operation){
+		return method_exists($this,$operation);
+	}
+	
+	/**
+	 * fallback function when a proxy operation is not implement
+	 * @param string $function the name of the proxy operation
+	 * @param mixed
+	 *
+	 * this implements a dummy proxy for all operations
+	 */
+	public function __call($function,$arguments){
+		if(substr($function,0,3)=='pre'){
+			return true;
+		}else{
+			return $arguments[1];
+		}
+	}
+	
+	/**
+	 * register a proxy to be used
+	 * @param OC_FileProxy $proxy
+	 */
+	public static function register($proxy){
+		self::$proxies[]=$proxy;
+	}
+	
+	public static function getProxies($operation,$post){
+		$operation=(($post)?'post':'pre').$operation;
+		$proxies=array();
+		foreach(self::$proxies as $proxy){
+			if($proxy->provides($operation)){
+				$proxies[]=$proxy;
+			}
+		}
+		return $proxies;
+	}
+
+	public static function runPreProxies($operation,$filepath,$filepath2=null){
+		$proxies=self::getProxies($operation,false);
+		$operation='pre'.$operation;
+		foreach($proxies as $proxy){
+			if($filepath2){
+				if(!$proxy->$operation($filepath,$filepath2)){
+					return false;
+				}
+			}else{
+				if(!$proxy->$operation($filepath)){
+					return false;
+				}
+			}
+		}
+		return true;
+	}
+
+	public static function runPostProxies($operation,$path,$result){
+		$proxies=self::getProxies($operation,true);
+		$operation='post'.$operation;
+		foreach($proxies as $proxy){
+			$result=$proxy->$operation($path,$result);
+		}
+		return $result;
+	}
+}
\ No newline at end of file
diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php
new file mode 100644
index 0000000000000000000000000000000000000000..af8ddee1473a525efc4a856a6c74a339ca87ce06
--- /dev/null
+++ b/lib/fileproxy/quota.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2011 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+/**
+ * user quota managment
+ */
+
+class OC_FileProxy_Quota extends OC_FileProxy{
+	private function getFreeSpace(){
+		$usedSpace=OC_Filesystem::filesize('');
+		$totalSpace=OC_Preferences::getValue(OC_User::getUser(),'files','quota',0);
+		if($totalSpace==0){
+			return 0;
+		}
+		return $totalSpace-$usedSpace;
+	}
+	
+	public function postFree_space($path,$space){
+		$free=$this->getFreeSpace();
+		if($free==0){
+			return $space;
+		}
+		return min($free,$space);
+	}
+
+	public function preFile_put_contents($path,$data){
+		return (strlen($data)<$this->getFreeSpace() or $this->getFreeSpace()==0);
+	}
+
+	public function preCopy($path1,$path2){
+		return (OC_Filesystem::filesize($path1)<$this->getFreeSpace() or $this->getFreeSpace()==0);
+	}
+
+	public function preFromTmpFile($tmpfile,$path){
+		return (filesize($tmpfile)<$this->getFreeSpace() or $this->getFreeSpace()==0);
+	}
+
+	public function preFromUploadedFile($tmpfile,$path){
+		return (filesize($tmpfile)<$this->getFreeSpace() or $this->getFreeSpace()==0);
+	}
+}
\ No newline at end of file
diff --git a/lib/files.php b/lib/files.php
new file mode 100644
index 0000000000000000000000000000000000000000..f1789d9c7ace0f9012a6b9c30ba77fd76b7b6a05
--- /dev/null
+++ b/lib/files.php
@@ -0,0 +1,294 @@
+<?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/>.
+*
+*/
+
+/**
+ * Class for fileserver access
+ *
+ */
+class OC_Files {
+	static $tmpFiles=array();
+
+	/**
+	* get the content of a directory
+	* @param dir $directory
+	*/
+	public static function getDirectoryContent($directory){
+		global $CONFIG_DATADIRECTORY;
+		if(strpos($directory,$CONFIG_DATADIRECTORY)===0){
+			$directory=substr($directory,strlen($CONFIG_DATADIRECTORY));
+		}
+		$filesfound=true;
+		$content=array();
+		$dirs=array();
+		$file=array();
+		$files=array();
+		if(OC_Filesystem::is_dir($directory)) {
+			if ($dh = OC_Filesystem::opendir($directory)) {
+			while (($filename = readdir($dh)) !== false) {
+				if($filename<>'.' and $filename<>'..' and substr($filename,0,1)!='.'){
+					$file=array();
+					$filesfound=true;
+					$file['name']=$filename;
+					$file['directory']=$directory;
+					$stat=OC_Filesystem::stat($directory.'/'.$filename);
+					$file=array_merge($file,$stat);
+					$file['size']=OC_Filesystem::filesize($directory.'/'.$filename);
+					$file['mime']=OC_Files::getMimeType($directory .'/'. $filename);
+					$file['readable']=OC_Filesystem::is_readable($directory .'/'. $filename);
+					$file['writeable']=OC_Filesystem::is_writeable($directory .'/'. $filename);
+					$file['type']=OC_Filesystem::filetype($directory .'/'. $filename);
+					if($file['type']=='dir'){
+						$dirs[$file['name']]=$file;
+					}else{
+						$files[$file['name']]=$file;
+					}
+				}
+			}
+			closedir($dh);
+			}
+		}
+		uksort($dirs, "strnatcasecmp");
+		uksort($files, "strnatcasecmp");
+		$content=array_merge($dirs,$files);
+		if($filesfound){
+			return $content;
+		}else{
+			return false;
+		}
+	}
+
+
+
+	/**
+	* return the content of a file or return a zip file containning multiply files
+	*
+	* @param dir  $dir
+	* @param file $file ; seperated list of files to download
+	*/
+	public static function get($dir,$files){
+		if(strpos($files,';')){
+			$files=explode(';',$files);
+		}
+
+		if(is_array($files)){
+			$zip = new ZipArchive();
+			$filename = sys_get_temp_dir()."/ownCloud.zip";
+			if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
+				exit("cannot open <$filename>\n");
+			}
+			foreach($files as $file){
+				$file=$dir.'/'.$file;
+				if(OC_Filesystem::is_file($file)){
+					$tmpFile=OC_Filesystem::toTmpFile($file);
+					self::$tmpFiles[]=$tmpFile;
+					$zip->addFile($tmpFile,basename($file));
+				}elseif(OC_Filesystem::is_dir($file)){
+					zipAddDir($file,$zip);
+				}
+			}
+			$zip->close();
+		}elseif(OC_Filesystem::is_dir($dir.'/'.$files)){
+			$zip = new ZipArchive();
+			$filename = sys_get_temp_dir()."/ownCloud.zip";
+			if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) {
+				exit("cannot open <$filename>\n");
+			}
+			$file=$dir.'/'.$files;
+			zipAddDir($file,$zip);
+			$zip->close();
+		}else{
+			$zip=false;
+			$filename=$dir.'/'.$files;
+		}
+		if($zip or OC_Filesystem::is_readable($filename)){
+			header('Content-Disposition: attachment; filename="'.basename($filename).'"');
+			header('Content-Transfer-Encoding: binary');
+			header('Expires: 0');
+			header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+			header('Pragma: public');
+			if($zip){
+				header('Content-Type: application/zip');
+				header('Content-Length: ' . filesize($filename));
+			}else{
+				header('Content-Type: ' . OC_Filesystem::getMimeType($filename));
+				header('Content-Length: ' . OC_Filesystem::filesize($filename));
+			}
+		}elseif($zip or !OC_Filesystem::file_exists($filename)){
+			header("HTTP/1.0 404 Not Found");
+			$tmpl = new OC_Template( '', '404', 'guest' );
+			$tmpl->assign('file',$filename);
+			$tmpl->printPage();
+// 			die('404 Not Found');
+		}else{
+			header("HTTP/1.0 403 Forbidden");
+			die('403 Forbidden');
+		}
+		@ob_end_clean();
+		if($zip){
+			readfile($filename);
+			unlink($filename);
+		}else{
+			OC_Filesystem::readfile($filename);
+		}
+		foreach(self::$tmpFiles as $tmpFile){
+			if(file_exists($tmpFile) and is_file($tmpFile)){
+				unlink($tmpFile);
+			}
+		}
+	}
+
+	/**
+	* move a file or folder
+	*
+	* @param dir  $sourceDir
+	* @param file $source
+	* @param dir  $targetDir
+	* @param file $target
+	*/
+	public static function move($sourceDir,$source,$targetDir,$target){
+		if(OC_User::isLoggedIn()){
+			$targetFile=$targetDir.'/'.$target;
+			$sourceFile=$sourceDir.'/'.$source;
+			return OC_Filesystem::rename($sourceFile,$targetFile);
+		}
+	}
+
+	/**
+	* copy a file or folder
+	*
+	* @param dir  $sourceDir
+	* @param file $source
+	* @param dir  $targetDir
+	* @param file $target
+	*/
+	public static function copy($sourceDir,$source,$targetDir,$target){
+		if(OC_User::isLoggedIn()){
+			$targetFile=$targetDir.'/'.$target;
+			$sourceFile=$sourceDir.'/'.$source;
+			return OC_Filesystem::copy($sourceFile,$targetFile);
+		}
+	}
+
+	/**
+	* create a new file or folder
+	*
+	* @param dir  $dir
+	* @param file $name
+	* @param type $type
+	*/
+	public static function newFile($dir,$name,$type){
+		if(OC_User::isLoggedIn()){
+			$file=$dir.'/'.$name;
+			if($type=='dir'){
+				return OC_Filesystem::mkdir($file);
+			}elseif($type=='file'){
+				$fileHandle=OC_Filesystem::fopen($file, 'w');
+				if($fileHandle){
+					fclose($fileHandle);
+					return true;
+				}else{
+					return false;
+				}
+			}
+		}
+	}
+
+	/**
+	* deletes a file or folder
+	*
+	* @param dir  $dir
+	* @param file $name
+	*/
+	public static function delete($dir,$file){
+		if(OC_User::isLoggedIn()){
+			$file=$dir.'/'.$file;
+			return OC_Filesystem::unlink($file);
+		}
+	}
+
+	/**
+	* try to detect the mime type of a file
+	*
+	* @param  string  path
+	* @return string  guessed mime type
+	*/
+	static function getMimeType($path){
+		return OC_Filesystem::getMimeType($path);
+	}
+
+	/**
+	* get a file tree
+	*
+	* @param  string  path
+	* @return array
+	*/
+	static function getTree($path){
+		return OC_Filesystem::getTree($path);
+	}
+
+	/**
+	* pull a file from a remote server
+	* @param  string  source
+	* @param  string  token
+	* @param  string  dir
+	* @param  string  file
+	* @return string  guessed mime type
+	*/
+	static function pull($source,$token,$dir,$file){
+		$tmpfile=tempnam(sys_get_temp_dir(),'remoteCloudFile');
+		$fp=fopen($tmpfile,'w+');
+		$url=$source.="/files/pull.php?token=$token";
+		$ch=curl_init();
+		curl_setopt($ch,CURLOPT_URL,$url);
+		curl_setopt($ch, CURLOPT_FILE, $fp);
+		curl_exec($ch);
+		fclose($fp);
+		$info=curl_getinfo($ch);
+		$httpCode=$info['http_code'];
+		curl_close($ch);
+		if($httpCode==200 or $httpCode==0){
+			OC_Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file);
+			return true;
+		}else{
+			return false;
+		}
+	}
+	
+	/**
+	 * set the maximum upload size limit for apache hosts using .htaccess
+	 * @param int size filesisze in bytes
+	 */
+	static function setUploadLimit($size){
+		global $SERVERROOT;
+		global $WEBROOT;
+		$size=OC_Helper::humanFileSize($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/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
new file mode 100644
index 0000000000000000000000000000000000000000..34fa6457fd28a4b9a9d1f52023ad2e97b22c8219
--- /dev/null
+++ b/lib/filestorage.php
@@ -0,0 +1,57 @@
+<?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/>.
+*/
+
+/**
+ * Privde a common interface to all different storage options
+ */
+class OC_Filestorage{
+	public function __construct($parameters){}
+	public function mkdir($path){}
+	public function rmdir($path){}
+	public function opendir($path){}
+	public function is_dir($path){}
+	public function is_file($path){}
+	public function stat($path){}
+	public function filetype($path){}
+	public function filesize($path){}
+	public function is_readable($path){}
+	public function is_writeable($path){}
+	public function file_exists($path){}
+	public function readfile($path){}
+	public function filectime($path){}
+	public function filemtime($path){}
+	public function fileatime($path){}
+	public function file_get_contents($path){}
+	public function file_put_contents($path,$data){}
+	public function unlink($path){}
+	public function rename($path1,$path2){}
+	public function copy($path1,$path2){}
+	public function fopen($path,$mode){}
+	public function toTmpFile($path){}//copy the file to a temporary file, used for cross-storage file actions
+	public function fromTmpFile($tmpPath,$path){}//copy a file from a temporary file, used for cross-storage file actions
+	public function fromUploadedFile($tmpPath,$path){}//copy a file from a temporary file, used for cross-storage file actions
+	public function getMimeType($path){}
+	public function hash($type,$path,$raw){}
+	public function free_space($path){}
+	public function search($query){}
+	public function getLocalFile($path){}// get a path to a local version of the file, whether the original file is local or remote
+}
diff --git a/lib/filestorage/local.php b/lib/filestorage/local.php
new file mode 100644
index 0000000000000000000000000000000000000000..07759b0e88c9af7353b4e353a3fbf3e1656b50e6
--- /dev/null
+++ b/lib/filestorage/local.php
@@ -0,0 +1,327 @@
+<?php
+/**
+ * for local filestore, we only have to map the paths
+ */
+class OC_Filestorage_Local extends OC_Filestorage{
+	private $datadir;
+	private static $mimetypes=null;
+	public function __construct($arguments){
+		$this->datadir=$arguments['datadir'];
+		if(substr($this->datadir,-1)!=='/'){
+			$this->datadir.='/';
+		}
+	}
+	public function mkdir($path){
+		if($return=mkdir($this->datadir.$path)){
+			$this->clearFolderSizeCache($path);
+		}
+		return $return;
+	}
+	public function rmdir($path){
+		if($return=rmdir($this->datadir.$path)){
+			$this->clearFolderSizeCache($path);
+		}
+		return $return;
+	}
+	public function opendir($path){
+		return opendir($this->datadir.$path);
+	}
+	public function is_dir($path){
+		return (is_dir($this->datadir.$path) or substr($path,-1)=='/');
+	}
+	public function is_file($path){
+		return is_file($this->datadir.$path);
+	}
+	public function stat($path){
+		return stat($this->datadir.$path);
+	}
+	public function filetype($path){
+		$filetype=filetype($this->datadir.$path);
+		if($filetype=='link'){
+			$filetype=filetype(readlink($this->datadir.$path));
+		}
+		return $filetype;
+	}
+	public function filesize($path){
+		if($this->is_dir($path)){
+			return $this->getFolderSize($path);
+		}else{
+			return filesize($this->datadir.$path);
+		}
+	}
+	public function is_readable($path){
+		return is_readable($this->datadir.$path);
+	}
+	public function is_writeable($path){
+		return is_writeable($this->datadir.$path);
+	}
+	public function file_exists($path){
+		return file_exists($this->datadir.$path);
+	}
+	public function readfile($path){
+		return readfile($this->datadir.$path);
+	}
+	public function filectime($path){
+		return filectime($this->datadir.$path);
+	}
+	public function filemtime($path){
+		return filemtime($this->datadir.$path);
+	}
+	public function fileatime($path){
+		return fileatime($this->datadir.$path);
+	}
+	public function file_get_contents($path){
+		return file_get_contents($this->datadir.$path);
+	}
+	public function file_put_contents($path,$data){
+		if($return=file_put_contents($this->datadir.$path,$data)){
+			$this->clearFolderSizeCache($path);
+		}
+	}
+	public function unlink($path){
+		$return=$this->delTree($path);
+		$this->clearFolderSizeCache($path);
+		return $return;
+	}
+	public function rename($path1,$path2){
+		if($return=rename($this->datadir.$path1,$this->datadir.$path2)){
+			$this->clearFolderSizeCache($path1);
+			$this->clearFolderSizeCache($path2);
+		}
+		return $return;
+	}
+	public function copy($path1,$path2){
+		if($this->is_dir($path2)){
+			if(!$this->file_exists($path2)){
+				$this->mkdir($path2);
+			}
+			$source=substr($path1,strrpos($path1,'/')+1);
+			$path2.=$source;
+		}
+		if($return=copy($this->datadir.$path1,$this->datadir.$path2)){
+			$this->clearFolderSizeCache($path2);
+		}
+		return $return;
+	}
+	public function fopen($path,$mode){
+		if($return=fopen($this->datadir.$path,$mode)){
+			switch($mode){
+				case 'r':
+					break;
+				case 'r+':
+				case 'w+':
+				case 'x+':
+				case 'a+':
+					$this->clearFolderSizeCache($path);
+					break;
+				case 'w':
+				case 'x':
+				case 'a':
+					$this->clearFolderSizeCache($path);
+					break;
+			}
+		}
+		return $return;
+	}
+
+	public function getMimeType($fspath){
+		if($this->is_readable($fspath)){
+			if (@is_dir($this->datadir.$fspath)) {
+				// directories are easy
+				return "httpd/unix-directory";
+			}elseif (function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){
+				$mimeType =strtolower(finfo_file($finfo,$this->datadir.$fspath));
+				$mimeType=substr($mimeType,0,strpos($mimeType,';'));
+				finfo_close($finfo);
+				return $mimeType;
+			} else if (function_exists("mime_content_type")) {
+				// use mime magic extension if available
+				$mime_type = mime_content_type($this->datadir.$fspath);
+			} else if (OC_Helper::canExecute("file")) {
+				// it looks like we have a 'file' command,
+				// lets see it it does have mime support
+				$fp = popen("file -i -b '{$this->datadir}$fspath' 2>/dev/null", "r");
+				$reply = fgets($fp);
+				pclose($fp);
+
+				//trim the character set from the end of the response
+				$mime_type=substr($reply,0,strrpos($reply,' '));
+			}
+			if (empty($mime_type)) {
+				// Fallback solution: (try to guess the type by the file extension
+				if(!self::$mimetypes){
+					self::$mimetypes=include('mimetypes.list.php');
+				}
+				$extention=strtolower(strrchr(basename($fspath), "."));
+				$extention=substr($extention,1);//remove leading .
+				$mime_type=(isset(self::$mimetypes[$extention]))?self::$mimetypes[$extention]:'application/octet-stream';
+			}
+			return $mime_type;
+		}
+	}
+
+	public function toTmpFile($path){
+		$tmpFolder=sys_get_temp_dir();
+		$filename=tempnam($tmpFolder,'OC_TEMP_FILE_'.substr($path,strrpos($path,'.')));
+		$fileStats = stat($this->datadir.$path);
+		if(copy($this->datadir.$path,$filename)){
+			touch($filename, $fileStats['mtime'], $fileStats['atime']);
+			return $filename;
+		}else{
+			return false;
+		}
+	}
+
+	public function fromTmpFile($tmpFile,$path){
+		$fileStats = stat($tmpFile);
+		if(rename($tmpFile,$this->datadir.$path)){
+			touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']);
+			$this->clearFolderSizeCache($path);
+			return true;
+		}else{
+			return false;
+		}
+	}
+
+	public function fromUploadedFile($tmpFile,$path){
+		$fileStats = stat($tmpFile);
+		if(move_uploaded_file($tmpFile,$this->datadir.$path)){
+			touch($this->datadir.$path, $fileStats['mtime'], $fileStats['atime']);
+			$this->clearFolderSizeCache($path);
+			return true;
+		}else{
+			return false;
+		}
+	}
+
+	private function delTree($dir) {
+		error_log('del'.$dir);
+		$dirRelative=$dir;
+		$dir=$this->datadir.$dir;
+		if (!file_exists($dir)) return true;
+		if (!is_dir($dir) || is_link($dir)) return unlink($dir);
+		foreach (scandir($dir) as $item) {
+			if ($item == '.' || $item == '..') continue;
+			if(is_file($dir.'/'.$item)){
+				if(unlink($dir.'/'.$item)){
+					$this->clearFolderSizeCache($dir);
+				}
+			}elseif(is_dir($dir.'/'.$item)){
+				if (!$this->delTree($dirRelative. "/" . $item)){
+					return false;
+				};
+			}
+		}
+		if($return=rmdir($dir)){
+			$this->clearFolderSizeCache($dir);
+		}
+		return $return;
+	}
+
+	public function hash($type,$path,$raw){
+		return hash_file($type,$this->datadir.$path,$raw);
+	}
+
+	public function free_space($path){
+		return disk_free_space($this->datadir.$path);
+	}
+
+	public function search($query){
+		return $this->searchInDir($query);
+	}
+	public function getLocalFile($path){
+			return $this->datadir.$path;
+	}
+
+	private function searchInDir($query,$dir=''){
+		$files=array();
+		foreach (scandir($this->datadir.$dir) as $item) {
+			if ($item == '.' || $item == '..') continue;
+			if(strstr(strtolower($item),strtolower($query))!==false){
+				$files[]=$dir.'/'.$item;
+			}
+			if(is_dir($this->datadir.$dir.'/'.$item)){
+				$files=array_merge($files,$this->searchInDir($query,$dir.'/'.$item));
+			}
+		}
+		return $files;
+	}
+
+	/**
+	 * @brief get the size of folder and it's content
+	 * @param string $path file path
+	 * @return int size of folder and it's content
+	 */
+	public function getFolderSize($path){
+		$path=str_replace('//','/',$path);
+		if($this->is_dir($path) and substr($path,-1)!='/'){
+			$path.='/';
+		}
+		$query=OC_DB::prepare("SELECT size FROM *PREFIX*foldersize WHERE path=?");
+		$size=$query->execute(array($path))->fetchAll();
+		if(count($size)>0){// we already the size, just return it
+			return $size[0]['size'];
+		}else{//the size of the folder isn't know, calulate it
+			return $this->calculateFolderSize($path);
+		}
+	}
+
+	/**
+	 * @brief calulate the size of folder and it's content and cache it
+	 * @param string $path file path
+	 * @return int size of folder and it's content
+	 */
+	public function calculateFolderSize($path){
+		if($this->is_file($path)){
+			$path=dirname($path);
+		}
+		$path=str_replace('//','/',$path);
+		if($this->is_dir($path) and substr($path,-1)!='/'){
+			$path.='/';
+		}
+		$size=0;
+		if ($dh = $this->opendir($path)) {
+			while (($filename = readdir($dh)) !== false) {
+				if($filename!='.' and $filename!='..'){
+					$subFile=$path.'/'.$filename;
+					if($this->is_file($subFile)){
+						$size+=$this->filesize($subFile);
+					}else{
+						$size+=$this->getFolderSize($subFile);
+					}
+				}
+			}
+			if($size>0){
+				$query=OC_DB::prepare("INSERT INTO *PREFIX*foldersize VALUES(?,?)");
+				$result=$query->execute(array($path,$size));
+			}
+		}
+		return $size;
+	}
+
+	/**
+	 * @brief clear the folder size cache of folders containing a file
+	 * @param string $path
+	 */
+	public function clearFolderSizeCache($path){
+		if($this->is_file($path)){
+			$path=dirname($path);
+		}
+		$path=str_replace('//','/',$path);
+		if($this->is_dir($path) and substr($path,-1)!='/'){
+			$path.='/';
+		}
+		$query=OC_DB::prepare("DELETE FROM *PREFIX*foldersize WHERE path = ?");
+		$result=$query->execute(array($path));
+		if($path!='/' and $path!=''){
+			$parts=explode('/',$path);
+			//pop empty part
+			$part=array_pop($parts);
+			if(empty($part)){
+				array_pop($parts);
+			}
+			$parent=implode('/',$parts);
+			$this->clearFolderSizeCache($parent);
+		}
+	}
+}
diff --git a/lib/filestorage/remote.php b/lib/filestorage/remote.php
new file mode 100644
index 0000000000000000000000000000000000000000..fb14c4121a2d633951dc4a7149f3ebbd00582292
--- /dev/null
+++ b/lib/filestorage/remote.php
@@ -0,0 +1,350 @@
+<?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/>.
+* 
+*/
+
+class OC_Filestorage_Remote extends OC_Filestorage{
+	private $url;
+	private $username;
+	private $password;
+	private $remote=false;
+	private $statCache;
+	private $statCacheDir=false;
+	private $changed=array();
+	
+	private function cacheDir($dir){
+		if($this->statCacheDir!=$dir or $this->statCacheDir===false){
+			$this->statCache=$this->remote->getFiles($dir);
+			$keys=array_keys($this->statCache);
+			$this->statCacheDir=$dir;
+		}
+	}
+	
+	public function __construct($arguments){
+		$this->url=$arguments['url'];
+		$this->username=$arguments['username'];
+		$this->password=$arguments['password'];
+	}
+	private function connect(){
+		if($this->remote===false){
+			$this->remote=OC_Connect::connect($this->url,$this->username,$this->password);
+		}
+	}
+	public function mkdir($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$return=$this->remote->newFile($parent,$name,'dir');
+		if($return){
+			$this->notifyObservers($path,OC_FILEACTION_CREATE);
+		}
+		return $return;
+	}
+	public function rmdir($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$return=$this->remote->delete($parent,$name);
+		if($return){
+			$this->notifyObservers($path,OC_FILEACTION_DELETE);
+		}
+		return $return;
+	}
+	public function opendir($path){
+		$this->connect();
+		$this->cacheDir($path);
+		$dirs=array_keys($this->statCache);
+		$id=uniqid();
+		global $FAKEDIRS;
+		$FAKEDIRS[$id]=$dirs;
+		if($return=opendir("fakedir://$id")){
+			$this->notifyObservers($path,OC_FILEACTION_READ);
+		}
+		return $return;
+	}
+	public function is_dir($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($path);
+		if($path=='' or $path=='/'){
+			return true;
+		}
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return ($this->statCache[$name]['type'=='dir']);
+	}
+	public function is_file($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return ($this->statCache[$name]['type'!='dir']);
+	}
+	public function stat($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return $false;
+		}
+		return $this->statCache[$name];
+	}
+	public function filetype($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['type'];
+	}
+	public function filesize($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return $false;
+		}
+		return $this->statCache[$name]['size'];
+	}
+	public function is_readable($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['readable'];
+	}
+	public function is_writeable($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['writeable'];
+	}
+	public function file_exists($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		return isset($this->statCache[$name]);
+	}
+	public function readfile($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		readfile($file);
+		unlink($file);
+	}
+	public function filectime($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['ctime'];
+	}
+	public function filemtime($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['mtime'];
+	}
+	public function fileatime($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['atime'];
+	}
+	public function file_get_contents($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		file_get_contents($file);
+		unlink($file);
+	}
+	public function file_put_contents($path,$data){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		$file=tempnam(sys_get_temp_dir(),'oc_');
+		file_put_contents($file,$data);
+		if($return=$this->remote->sendTmpFile($file,$parent,$name)){
+			$this->notifyObservers($path,OC_FILEACTION_WRITE);
+		}
+	}
+	public function unlink($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if($return=$this->remote->delete($paren,$name)){
+			$this->notifyObservers($path,OC_FILEACTION_DELETE);
+		}
+		return $return;
+	}
+	public function rename($path1,$path2){
+		$this->connect();
+		$parent1=dirname($path1);
+		$name1=substr($path1,strlen($parent1)+1);
+		$parent2=dirname($path2);
+		$name2=substr($path2,strlen($parent2)+1);
+		if($return=$this->remote->move($parent1,$name1,$parent2,$name2)){
+			$this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME);
+		}
+		return $return;
+	}
+	public function copy($path1,$path2){
+		$this->connect();
+		$parent1=dirname($path1);
+		$name1=substr($path1,strlen($parent1)+1);
+		$parent2=dirname($path2);
+		$name2=substr($path2,strlen($parent2)+1);
+		if($return=$this->copy->rename($parent1,$name1,$parent2,$name2)){
+			$this->notifyObservers($path1.'->'.$path2,OC_FILEACTION_RENAME);
+		}
+		return $return;
+	}
+	public function fopen($path,$mode){
+		$this->connect();
+		$changed=false;
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		$file=$this->remote->getFile($parent,$name);
+		if($return=fopen($file,$mode)){
+			switch($mode){
+				case 'r':
+					$this->notifyObservers($path,OC_FILEACTION_READ);
+					break;
+				case 'r+':
+				case 'w+':
+				case 'x+':
+				case 'a+':
+					$this->notifyObservers($path,OC_FILEACTION_READ | OC_FILEACTION_WRITE);
+					$this->changed[]=array('dir'=>$parent,'file'=>$name,'tmp'=>$file);
+					break;
+				case 'w':
+				case 'x':
+				case 'a':
+					$this->notifyObservers($path,OC_FILEACTION_WRITE);
+					$this->changed[]=array('dir'=>$parent,'file'=>$name,'tmp'=>$file);
+					break;
+			}
+		}
+		return $return;
+	}
+	
+	public function getMimeType($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if(substr($name,0,1)=='/'){
+			$name=substr($name,1);
+		}
+		$this->cacheDir($parent);
+		if(!isset($this->statCache[$name])){
+			return false;
+		}
+		return $this->statCache[$name]['mime'];
+	}
+	
+	public function toTmpFile($path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if(substr($name,0,1)=='/'){
+			$name=substr($name,1);
+		}
+		$filename=$this->remote->getFile($parent,$name);
+		if($filename){
+			$this->notifyObservers($path,OC_FILEACTION_READ);
+			return $filename;
+		}else{
+			return false;
+		}
+	}
+	
+	public function fromTmpFile($tmpFile,$path){
+		$this->connect();
+		$parent=dirname($path);
+		$name=substr($path,strlen($parent)+1);
+		if($this->remote->sendTmpFile($tmpFile,$parent,$name)){
+			$this->notifyObservers($path,OC_FILEACTION_CREATE);
+			return true;
+		}else{
+			return false;
+		}
+	}
+	
+	public function delTree($dir) {
+		$this->connect();
+		$parent=dirname($dir);
+		$name=substr($dir,strlen($parent)+1);
+		$return=$this->remote->delete($parent,$name);
+		if($return=rmdir($dir)){
+			$this->notifyObservers($dir,OC_FILEACTION_DELETE);
+		}
+		return $return;
+	}
+	
+	public function find($path){
+		return $this->getTree($path);
+	}
+	
+	public function getTree($dir) {
+		$this->connect();
+		if($return=$this->remote->getTree($dir)){
+			$this->notifyObservers($dir,OC_FILEACTION_READ);
+		}
+		return $return;
+	}
+	
+	public function __destruct(){
+		foreach($this->changed as $changed){
+			$this->remote->sendTmpFile($changed['tmp'],$changed['dir'],$changed['file']);
+		}
+	}
+}
diff --git a/lib/filesystem.php b/lib/filesystem.php
new file mode 100644
index 0000000000000000000000000000000000000000..76032fae204483ac0c7032d0b7cdcd309f22ab1a
--- /dev/null
+++ b/lib/filesystem.php
@@ -0,0 +1,484 @@
+<?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/>.
+* 
+*/
+
+
+/**
+ * Class for abstraction of filesystem functions
+ * This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object
+ * this class should also handle all the file premission related stuff
+ *
+ * Hooks provided:
+ *   read(path)
+ *   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();
+	static private $fakeRoot='';
+	static private $storageTypes=array();
+	
+	
+	/**
+	* register a storage type
+	* @param  string  type
+	* @param  string  classname
+	* @param  array  arguments     an associative array in the form of name=>type (eg array('datadir'=>'string'))
+	*/
+	static public function registerStorageType($type,$classname,$arguments){
+		self::$storageTypes[$type]=array('type'=>$type,'classname'=>$classname,'arguments'=>$arguments);
+	}
+	
+	/**
+	* check if the filesystem supports a specific storagetype
+	* @param  string  type
+	* @return bool
+	*/
+	static public function hasStorageType($type){
+		return isset(self::$storageTypes[$type]);
+	}
+	
+	/**
+	* get the list of names of storagetypes that the filesystem supports
+	* @return array
+	*/
+	static public function getStorageTypeNames(){
+		return array_keys(self::$storageTypes);
+	}
+	
+	/**
+	 * 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
+	* @return OC_Filestorage
+	*/
+	static public function createStorage($type,$arguments){
+		if(!self::hasStorageType($type)){
+			return false;
+		}
+		$className=self::$storageTypes[$type]['classname'];
+		if(class_exists($className)){
+			return new $className($arguments);
+		}else{
+			return false;
+		}
+	}
+	
+	/**
+	* change the root to a fake toor
+	* @param  string  fakeRoot
+	* @return bool
+	*/
+	static public function chroot($fakeRoot){
+		if(!$fakeRoot==''){
+			if($fakeRoot[0]!=='/'){
+				$fakeRoot='/'.$fakeRoot;
+			}
+		}
+		self::$fakeRoot=$fakeRoot;
+	}
+	
+	/**
+	* get the part of the path relative to the mountpoint of the storage it's stored in
+	* @param  string  path
+	* @return bool
+	*/
+	static public function getInternalPath($path){
+		$mountPoint=self::getMountPoint($path);
+		$path=self::$fakeRoot.$path;
+		$internalPath=substr($path,strlen($mountPoint));
+		return $internalPath;
+	}
+	
+	/**
+	* check if the current users has the right premissions to read a file
+	* @param  string  path
+	* @return bool
+	*/
+	static private function canRead($path){
+		if(substr($path,0,1)!=='/'){
+			$path='/'.$path;
+		}
+		if(strstr($path,'/../') || strrchr($path, '/') === '/..' ){
+			return false;
+		}
+		return true;//dummy untill premissions are correctly implemented, also the correcty value because for now users are locked in their seperate data dir and can read/write everything in there
+	}
+	/**
+	* check if the current users has the right premissions to write a file
+	* @param  string  path
+	* @return bool
+	*/
+	static private function canWrite($path){
+		if(substr($path,0,1)!=='/'){
+			$path='/'.$path;
+		}
+		if(strstr($path,'/../') || strrchr($path, '/') === '/..' ){
+			return false;
+		}
+		return true;//dummy untill premissions are correctly implemented, also the correcty value because for now users are locked in their seperate data dir and can read/write everything in there
+	}
+	
+	/**
+	* mount an OC_Filestorage in our virtual filesystem
+	* @param OC_Filestorage storage
+	* @param string mountpoint
+	*/
+	static public function mount($storage,$mountpoint){
+		if(substr($mountpoint,0,1)!=='/'){
+			$mountpoint='/'.$mountpoint;
+		}
+		self::$storages[self::$fakeRoot.$mountpoint]=$storage;
+	}
+	
+	/**
+	* get the storage object for a path
+	* @param string path
+	* @return OC_Filestorage
+	*/
+	static public function getStorage($path){
+		$mountpoint=self::getMountPoint($path);
+		if($mountpoint){
+			return self::$storages[$mountpoint];
+		}
+	}
+	
+	/**
+	* get the mountpoint of the storage object for a path
+	( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account
+	*
+	* @param string path
+	* @return string
+	*/
+	static public function getMountPoint($path){
+		if(!$path){
+			$path='/';
+		}
+		if(substr($path,0,1)!=='/'){
+			$path='/'.$path;
+		}
+		if(substr($path,-1)!=='/'){
+			$path=$path.'/';
+		}
+		$path=self::$fakeRoot.$path;
+		$foundMountPoint='';
+		foreach(self::$storages as $mountpoint=>$storage){
+			if(substr($mountpoint,-1)!=='/'){
+				$mountpoint=$mountpoint.'/';
+			}
+			if($mountpoint==$path){
+				return $mountpoint;
+			}
+			if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)){
+				$foundMountPoint=$mountpoint;
+			}
+		}
+		return $foundMountPoint;
+	}
+	
+	/**
+	* return the path to a local version of the file
+	* we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed
+	* @param string path
+	* @return string
+	*/
+	static public function getLocalFile($path){
+		$parent=substr($path,0,strrpos($path,'/'));
+		if(self::canRead($parent) and $storage=self::getStorage($path)){
+			return $storage->getLocalFile(self::getInternalPath($path));
+		}
+	}
+	
+	static public function mkdir($path){
+		return self::basicOperation('mkdir',$path,array('create','write'));
+	}
+	static public function rmdir($path){
+		return self::basicOperation('rmdir',$path,array('delete'));
+	}
+	static public function opendir($path){
+		return self::basicOperation('opendir',$path,array('read'));
+	}
+	static public function is_dir($path){
+		if($path=='/'){
+			return true;
+		}
+		return self::basicOperation('is_dir',$path);
+	}
+	static public function is_file($path){
+		if($path=='/'){
+			return false;
+		}
+		return self::basicOperation('is_file',$path);
+	}
+	static public function stat($path){
+		return self::basicOperation('stat',$path);
+	}
+	static public function filetype($path){
+		return self::basicOperation('filetype',$path);
+	}
+	static public function filesize($path){
+		return self::basicOperation('filesize',$path);
+	}
+	static public function readfile($path){
+		return self::basicOperation('readfile',$path,array('read'));
+	}
+	static public function is_readable($path){
+		return self::basicOperation('is_readable',$path);
+	}
+	static public function is_writeable($path){
+		return self::basicOperation('is_writeable',$path);
+	}
+	static public function file_exists($path){
+		if($path=='/'){
+			return true;
+		}
+		return self::basicOperation('file_exists',$path);
+	}
+	static public function filectime($path){
+		return self::basicOperation('filectime',$path);
+	}
+	static public function filemtime($path){
+		return self::basicOperation('filemtime',$path);
+	}
+	static public function fileatime($path){
+		return self::basicOperation('fileatime',$path);
+	}
+	static public function file_get_contents($path){
+		return self::basicOperation('file_get_contents',$path,array('read'));
+	}
+	static public function file_put_contents($path,$data){
+		error_log($data);
+		return self::basicOperation('file_put_contents',$path,array('create','write'),$data);
+	}
+	static public function unlink($path){
+		return self::basicOperation('unlink',$path,array('delete'));
+	}
+	static public function rename($path1,$path2){
+		if(OC_FileProxy::runPreProxies('rename',$path1,$path2) and self::canWrite($path1) and self::canWrite($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($tmpFile,self::getInternalPath($path2));
+					$storage1->unlink(self::getInternalPath($path1));
+				}
+				OC_Hook::emit( 'OC_Filesystem', 'post_rename', array( 'oldpath' => $path1, 'newpath'=>$path2));
+				return $result;
+			}
+		}
+	}
+	static public function copy($path1,$path2){
+		if(OC_FileProxy::runPreProxies('copy',$path1,$path2) and self::canRead($path1) and self::canWrite($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($tmpFile,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' => $path2));
+				}
+				OC_Hook::emit( 'OC_Filesystem', 'post_write', array( 'path' => $path2));
+				return $result;
+			}
+		}
+	}
+	static public function fopen($path,$mode){
+		$hooks=array();
+		switch($mode){
+			case 'r':
+				$hooks[]='read';
+				break;
+			case 'r+':
+			case 'w+':
+			case 'x+':
+			case 'a+':
+				$hooks[]='read';
+				$hooks[]='write';
+				break;
+			case 'w':
+			case 'x':
+			case 'a':
+				$hooks[]='write';
+				break;
+		}
+		
+		return self::basicOperation('fopen',$path,$hooks);
+	}
+	static public function toTmpFile($path){
+		if(OC_FileProxy::runPreProxies('toTmpFile',$path) and self::canRead($path) and $storage=self::getStorage($path)){
+			OC_Hook::emit( 'OC_Filesystem', 'read', array( 'path' => $path));
+			return $storage->toTmpFile(self::getInternalPath($path));
+		}
+	}
+	static public function fromTmpFile($tmpFile,$path){
+		if(OC_FileProxy::runPreProxies('copy',$tmpFile,$path) and self::canWrite($path) and $storage=self::getStorage($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;
+			}
+		}
+	}
+	static public function fromUploadedFile($tmpFile,$path){
+		error_log('upload');
+		if(OC_FileProxy::runPreProxies('fromUploadedFile',$tmpFile,$path) and self::canWrite($path) and $storage=self::getStorage($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;
+			}
+		}
+	}
+	static public function getMimeType($path){
+		return self::basicOperation('getMimeType',$path);
+	}
+	static public function hash($type,$path){
+		return self::basicOperation('hash',$path,array('read'));
+	}
+	
+	static public function free_space($path='/'){
+		return self::basicOperation('free_space',$path);
+	}
+	
+	static public function search($query){
+		$files=array();
+		$fakeRoot=self::$fakeRoot;
+		$fakeRootLength=strlen($fakeRoot);
+		foreach(self::$storages as $mountpoint=>$storage){
+			$results=$storage->search($query);
+			if(is_array($results)){
+				foreach($results as $result){
+					$file=str_replace('//','/',$mountpoint.$result);
+					if(substr($file,0,$fakeRootLength)==$fakeRoot){
+						$file=substr($file,$fakeRootLength);
+						$files[]=$file;
+					}
+				}
+			}
+		}
+		return $files;
+		
+	}
+
+	/**
+	 * abstraction for running most basic operations
+	 * @param string $operation
+	 * @param string #path
+	 * @param array (optional) hooks
+	 * @param mixed (optional) $extraParam
+	 * @return mixed
+	 */
+	private static function basicOperation($operation,$path,$hooks=array(),$extraParam=null){
+		if(OC_FileProxy::runPreProxies($operation,$path) and self::canRead($path) and $storage=self::getStorage($path)){
+			$interalPath=self::getInternalPath($path);
+			$run=true;
+			foreach($hooks as $hook){
+				if($hook!='read'){
+					OC_Hook::emit( 'OC_Filesystem', $hook, array( 'path' => $path, 'run' => &$run));
+				}else{
+					OC_Hook::emit( 'OC_Filesystem', $hook, array( 'path' => $path));
+				}
+			}
+			if($run){
+				if($extraParam){
+					$result=$storage->$operation($interalPath,$extraParam);
+				}else{
+					$result=$storage->$operation($interalPath);
+				}
+				$result=OC_FileProxy::runPostProxies($operation,$path,$result);
+				foreach($hooks as $hook){
+					if($hook!='read'){
+						OC_Hook::emit( 'OC_Filesystem', 'post_'.$hook, array( 'path' => $path));
+					}
+				}
+				return $result;
+			}
+		}
+		return null;
+	}
+}
diff --git a/lib/group.php b/lib/group.php
new file mode 100644
index 0000000000000000000000000000000000000000..fbff41e30e9017545028814a954ed297db05b46c
--- /dev/null
+++ b/lib/group.php
@@ -0,0 +1,254 @@
+<?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/>.
+ *
+ */
+
+/**
+ * This class provides all methods needed for managing groups.
+ *
+ * Hooks provided:
+ *   pre_createGroup(&run, gid)
+ *   post_createGroup(gid)
+ *   pre_deleteGroup(&run, gid)
+ *   post_deleteGroup(gid)
+ *   pre_addToGroup(&run, uid, gid)
+ *   post_addToGroup(uid, gid)
+ *   pre_removeFromGroup(&run, uid, gid)
+ *   post_removeFromGroup(uid, gid)
+ */
+class OC_Group {
+	// The backend used for user management
+	private static $_backend;
+
+	// Backends available (except database)
+	private static $_backends = array();
+
+	/**
+	 * @brief registers backend
+	 * @param $name name of the backend
+	 * @returns true/false
+	 *
+	 * Makes a list of backends that can be used by other modules
+	 */
+	public static function registerBackend( $name ){
+		self::$_backends[] = $name;
+		return true;
+	}
+
+	/**
+	 * @brief gets available backends
+	 * @returns array of backends
+	 *
+	 * Returns the names of all backends.
+	 */
+	public static function getBackends(){
+		return self::$_backends;
+	}
+
+	/**
+	 * @brief set the group backend
+	 * @param  string  $backend  The backend to use for user managment
+	 * @returns true/false
+	 */
+	public static function setBackend( $backend = 'database' ){
+		// You'll never know what happens
+		if( null === $backend OR !is_string( $backend )){
+			$backend = 'database';
+		}
+
+		// Load backend
+		switch( $backend ){
+			case 'database':
+			case 'mysql':
+			case 'sqlite':
+				self::$_backend = new OC_Group_Database();
+				break;
+			default:
+				$className = 'OC_GROUP_' . strToUpper($backend);
+				self::$_backend = new $className();
+				break;
+		}
+	}
+
+	/**
+	 * @brief Try to create a new group
+	 * @param $gid The name of the group to create
+	 * @returns true/false
+	 *
+	 * Trys to create a new group. If the group name already exists, false will
+	 * be returned. Basic checking of Group name
+	 *
+	 * Allowed characters in the username are: "a-z", "A-Z", "0-9" and "_.@-"
+	 */
+	public static function createGroup( $gid ){
+		// Check the name for bad characters
+		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-"
+		if( preg_match( '/[^a-zA-Z0-9 _\.@\-]/', $gid )){
+			return false;
+		}
+		// No empty group names!
+		if( !$gid ){
+			return false;
+		}
+		// No duplicate group names
+		if( in_array( $gid, self::getGroups())){
+			return false;
+		}
+
+		$run = true;
+		OC_Hook::emit( "OC_Group", "pre_createGroup", array( "run" => &$run, "gid" => $gid ));
+
+		if( $run && self::$_backend->createGroup( $gid )){
+			OC_Hook::emit( "OC_Group", "post_createGroup", array( "gid" => $gid ));
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @brief delete a group
+	 * @param $gid gid of the group to delete
+	 * @returns true/false
+	 *
+	 * Deletes a group and removes it from the group_user-table
+	 */
+	public static function deleteGroup( $gid ){
+		// Prevent users from deleting group admin
+		if( $gid == "admin" ){
+			return false;
+		}
+
+		$run = true;
+		OC_Hook::emit( "OC_Group", "pre_deleteGroup", array( "run" => &$run, "gid" => $gid ));
+
+		if( $run && self::$_backend->deleteGroup( $gid )){
+			OC_Hook::emit( "OC_Group", "post_deleteGroup", array( "gid" => $gid ));
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @brief is user in group?
+	 * @param $uid uid of the user
+	 * @param $gid gid of the group
+	 * @returns true/false
+	 *
+	 * Checks whether the user is member of a group or not.
+	 */
+	public static function inGroup( $uid, $gid ){
+		return self::$_backend->inGroup($uid, $gid);
+	}
+
+	/**
+	 * @brief Add a user to a group
+	 * @param $uid Name of the user to add to group
+	 * @param $gid Name of the group in which add the user
+	 * @returns true/false
+	 *
+	 * Adds a user to a group.
+	 */
+	public static function addToGroup( $uid, $gid ){
+		// Does the user exist?
+		if( !OC_User::userExists($uid)){
+			return false;
+		}
+		// Does the group exist?
+		if( !OC_Group::groupExists($gid)){
+			return false;
+		}
+
+		// Go go go
+		$run = true;
+		OC_Hook::emit( "OC_Group", "pre_addToGroup", array( "run" => &$run, "uid" => $uid, "gid" => $gid ));
+
+		if( $run && self::$_backend->addToGroup( $uid, $gid )){
+			OC_Hook::emit( "OC_Group", "post_addToGroup", array( "uid" => $uid, "gid" => $gid ));
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @brief Removes a user from a group
+	 * @param $uid Name of the user to remove from group
+	 * @param $gid Name of the group from which remove the user
+	 * @returns true/false
+	 *
+	 * removes the user from a group.
+	 */
+	public static function removeFromGroup( $uid, $gid ){
+		$run = true;
+		OC_Hook::emit( "OC_Group", "pre_removeFromGroup", array( "run" => &$run, "uid" => $uid, "gid" => $gid ));
+
+		if( $run && self::$_backend->removeFromGroup( $uid, $gid )){
+			OC_Hook::emit( "OC_Group", "post_removeFromGroup", array( "uid" => $uid, "gid" => $gid ));
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @brief Get all groups a user belongs to
+	 * @param $uid Name of the user
+	 * @returns array with group names
+	 *
+	 * This function fetches all groups a user belongs to. It does not check
+	 * if the user exists at all.
+	 */
+	public static function getUserGroups( $uid ){
+		return self::$_backend->getUserGroups($uid);
+	}
+
+	/**
+	 * @brief get a list of all groups
+	 * @returns array with group names
+	 *
+	 * Returns a list with all groups
+	 */
+	public static function getGroups(){
+		return self::$_backend->getGroups();
+	}
+	
+	/**
+	 * check if a group exists
+	 * @param string $gid
+	 * @return bool
+	 */
+	public static function groupExists($gid){
+		return in_array( $gid, self::getGroups());
+	}
+	
+	/**
+	 * @brief get a list of all users in a group
+	 * @returns array with user ids
+	 */
+	public static function usersInGroup($gid){
+		return self::$_backend->usersInGroup($gid);
+	}
+}
diff --git a/lib/group/backend.php b/lib/group/backend.php
new file mode 100644
index 0000000000000000000000000000000000000000..43f94e7ea1a08ba4def39601865dacd77c694ec8
--- /dev/null
+++ b/lib/group/backend.php
@@ -0,0 +1,102 @@
+<?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/>.
+*
+*/
+
+
+
+/**
+ * Abstract base class for user management
+ */
+abstract class OC_Group_Backend {
+	/**
+	 * @brief Try to create a new group
+	 * @param $gid The name of the group to create
+	 * @returns true/false
+	 *
+	 * Trys to create a new group. If the group name already exists, false will
+	 * be returned.
+	 */
+	public static function createGroup($gid){}
+
+	/**
+	 * @brief delete a group
+	 * @param $gid gid of the group to delete
+	 * @returns true/false
+	 *
+	 * Deletes a group and removes it from the group_user-table
+	 */
+	public static function removeGroup($gid){}
+
+	/**
+	 * @brief is user in group?
+	 * @param $uid uid of the user
+	 * @param $gid gid of the group
+	 * @returns true/false
+	 *
+	 * Checks whether the user is member of a group or not.
+	 */
+	public static function inGroup($uid, $gid){}
+
+	/**
+	 * @brief Add a user to a group
+	 * @param $uid Name of the user to add to group
+	 * @param $gid Name of the group in which add the user
+	 * @returns true/false
+	 *
+	 * Adds a user to a group.
+	 */
+	public static function addToGroup($uid, $gid){}
+
+	/**
+	 * @brief Removes a user from a group
+	 * @param $uid Name of the user to remove from group
+	 * @param $gid Name of the group from which remove the user
+	 * @returns true/false
+	 *
+	 * removes the user from a group.
+	 */
+	public static function removeFromGroup($uid,$gid){}
+
+	/**
+	 * @brief Get all groups a user belongs to
+	 * @param $uid Name of the user
+	 * @returns array with group names
+	 *
+	 * This function fetches all groups a user belongs to. It does not check
+	 * if the user exists at all.
+	 */
+	public static function getUserGroups($uid){}
+
+	/**
+	 * @brief get a list of all groups
+	 * @returns array with group names
+	 *
+	 * Returns a list with all groups
+	 */
+	public static function getGroups(){}
+	
+	/**
+	 * @brief get a list of all users in a group
+	 * @returns array with user ids
+	 */
+	public static function usersInGroup($gid){}
+}
diff --git a/lib/group/database.php b/lib/group/database.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a9fc53d39fa0e330532237506dfbe8426b1f43c
--- /dev/null
+++ b/lib/group/database.php
@@ -0,0 +1,191 @@
+<?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/>.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE `groups` (
+ *   `gid` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
+ *   PRIMARY KEY (`gid`)
+ * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ *
+ * CREATE TABLE `group_user` (
+ *   `gid` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
+ *   `uid` varchar(64) COLLATE utf8_unicode_ci NOT NULL
+ * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ *
+ */
+
+/**
+ * Class for group management in a SQL Database (e.g. MySQL, SQLite)
+ */
+class OC_Group_Database extends OC_Group_Backend {
+	static private $userGroupCache=array();
+
+	/**
+	 * @brief Try to create a new group
+	 * @param $gid The name of the group to create
+	 * @returns true/false
+	 *
+	 * Trys to create a new group. If the group name already exists, false will
+	 * be returned.
+	 */
+	public static function createGroup( $gid ){
+		// Check for existence
+		$query = OC_DB::prepare( "SELECT gid FROM `*PREFIX*groups` WHERE gid = ?" );
+		$result = $query->execute( array( $gid ));
+
+		if( $result->numRows() > 0 ){
+			// Can not add an existing group
+			return false;
+		}
+		else{
+			// Add group and exit
+			$query = OC_DB::prepare( "INSERT INTO `*PREFIX*groups` ( `gid` ) VALUES( ? )" );
+			$result = $query->execute( array( $gid ));
+
+			return $result ? true : false;
+		}
+	}
+
+	/**
+	 * @brief delete a group
+	 * @param $gid gid of the group to delete
+	 * @returns true/false
+	 *
+	 * Deletes a group and removes it from the group_user-table
+	 */
+	public static function deleteGroup( $gid ){
+		// Delete the group
+		$query = OC_DB::prepare( "DELETE FROM `*PREFIX*groups` WHERE gid = ?" );
+		$result = $query->execute( array( $gid ));
+
+		// Delete the group-user relation
+		$query = OC_DB::prepare( "DELETE FROM `*PREFIX*group_user` WHERE gid = ?" );
+		$result = $query->execute( array( $gid ));
+
+		return true;
+	}
+
+	/**
+	 * @brief is user in group?
+	 * @param $uid uid of the user
+	 * @param $gid gid of the group
+	 * @returns true/false
+	 *
+	 * Checks whether the user is member of a group or not.
+	 */
+	public static function inGroup( $uid, $gid ){
+		// check
+		$query = OC_DB::prepare( "SELECT uid FROM `*PREFIX*group_user` WHERE gid = ? AND uid = ?" );
+		$result = $query->execute( array( $gid, $uid ));
+
+		return $result->numRows() > 0 ? true : false;
+	}
+
+	/**
+	 * @brief Add a user to a group
+	 * @param $uid Name of the user to add to group
+	 * @param $gid Name of the group in which add the user
+	 * @returns true/false
+	 *
+	 * Adds a user to a group.
+	 */
+	public static function addToGroup( $uid, $gid ){
+		// No duplicate entries!
+		if( !self::inGroup( $uid, $gid )){
+			$query = OC_DB::prepare( "INSERT INTO `*PREFIX*group_user` ( `uid`, `gid` ) VALUES( ?, ? )" );
+			$result = $query->execute( array( $uid, $gid ));
+		}
+		return true;
+	}
+
+	/**
+	 * @brief Removes a user from a group
+	 * @param $uid Name of the user to remove from group
+	 * @param $gid Name of the group from which remove the user
+	 * @returns true/false
+	 *
+	 * removes the user from a group.
+	 */
+	public static function removeFromGroup( $uid, $gid ){
+		$query = OC_DB::prepare( "DELETE FROM `*PREFIX*group_user` WHERE `uid` = ? AND `gid` = ?" );
+		$result = $query->execute( array( $uid, $gid ));
+
+		return true;
+	}
+
+	/**
+	 * @brief Get all groups a user belongs to
+	 * @param $uid Name of the user
+	 * @returns array with group names
+	 *
+	 * This function fetches all groups a user belongs to. It does not check
+	 * if the user exists at all.
+	 */
+	public static function getUserGroups( $uid ){
+		// No magic!
+		$query = OC_DB::prepare( "SELECT gid FROM `*PREFIX*group_user` WHERE uid = ?" );
+		$result = $query->execute( array( $uid ));
+
+		$groups = array();
+		while( $row = $result->fetchRow()){
+			$groups[] = $row["gid"];
+		}
+
+		return $groups;
+	}
+
+	/**
+	 * @brief get a list of all groups
+	 * @returns array with group names
+	 *
+	 * Returns a list with all groups
+	 */
+	public static function getGroups(){
+		$query = OC_DB::prepare( "SELECT gid FROM `*PREFIX*groups`" );
+		$result = $query->execute();
+
+		$groups = array();
+		while( $row = $result->fetchRow()){
+			$groups[] = $row["gid"];
+		}
+
+		return $groups;
+	}
+	
+	/**
+	 * @brief get a list of all users in a group
+	 * @returns array with user ids
+	 */
+	public static function usersInGroup($gid){
+		$query=OC_DB::prepare('SELECT uid FROM *PREFIX*group_user WHERE gid=?');
+		$users=array();
+		$result=$query->execute(array($gid));
+		while($row=$result->fetchRow()){
+			$users[]=$row['uid'];
+		}
+		return $users;
+	}
+}
diff --git a/lib/helper.php b/lib/helper.php
new file mode 100755
index 0000000000000000000000000000000000000000..272f4607011ac085fe95c3f3e08dd7b98222ff1c
--- /dev/null
+++ b/lib/helper.php
@@ -0,0 +1,320 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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/>.
+ *
+ */
+
+/**
+ * Collection of useful functions
+ */
+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 , $redirect_url=NULL ){
+		global $WEBROOT;
+		global $SERVERROOT;
+		
+		if( $app != '' ){
+			$app .= '/';
+			// Check if the app is in the app folder
+			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;
+			}
+		}
+
+		if($redirect_url)
+			return $urlLinkTo.'?redirect_url='.$redirect_url;
+		else
+			return $urlLinkTo;
+
+	}
+
+	/**
+	 * @brief Creates path to an image
+	 * @param $app app
+	 * @param $image image name
+	 * @returns the url
+	 *
+	 * Returns the path to the image.
+	 */
+	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";
+		}
+		elseif( !empty( $app )){
+			return "$WEBROOT/$app/img/$image";
+		}
+		else{
+			return "$WEBROOT/core/img/$image";
+		}
+	}
+
+	/**
+	 * @brief get path to icon of file type
+	 * @param $mimetype mimetype
+	 * @returns the url
+	 *
+	 * Returns the path to the image of this file type.
+	 */
+	public static function mimetypeIcon( $mimetype ){
+		global $SERVERROOT;
+		global $WEBROOT;
+		// Replace slash with a minus
+		$mimetype = str_replace( "/", "-", $mimetype );
+
+		// Is it a dir?
+		if( $mimetype == "dir" ){
+			return "$WEBROOT/core/img/places/folder.svg";
+		}
+
+		// Icon exists?
+		if( file_exists( "$SERVERROOT/core/img/filetypes/$mimetype.svg" )){
+			return "$WEBROOT/core/img/filetypes/$mimetype.svg";
+		}
+		//try only the first part of the filetype
+		$mimetype=substr($mimetype,0,strpos($mimetype,'-'));
+		if( file_exists( "$SERVERROOT/core/img/filetypes/$mimetype.svg" )){
+			return "$WEBROOT/core/img/filetypes/$mimetype.svg";
+		}
+		else{
+			return "$WEBROOT/core/img/filetypes/file.svg";
+		}
+	}
+
+	/**
+	 * @brief Make a human file size
+	 * @param $bytes file size in bytes
+	 * @returns a human readable file size
+	 *
+	 * Makes 2048 to 2 kB.
+	 */
+	public static function humanFileSize( $bytes ){
+		if( $bytes < 1024 ){
+			return "$bytes B";
+		}
+		$bytes = round( $bytes / 1024, 1 );
+		if( $bytes < 1024 ){
+			return "$bytes kB";
+		}
+		$bytes = round( $bytes / 1024, 1 );
+		if( $bytes < 1024 ){
+			return "$bytes MB";
+		}
+
+		// Wow, heavy duty for owncloud
+		$bytes = round( $bytes / 1024, 1 );
+		return "$bytes GB";
+	}
+	
+	/**
+	 * @brief Make a computer file size
+	 * @param $str file size in a fancy format
+	 * @returns a file size in bytes
+	 *
+	 * Makes 2kB to 2048.
+	 *
+	 * Inspired by: http://www.php.net/manual/en/function.filesize.php#92418
+	 */
+	public static function computerFileSize( $str ){
+		$bytes = 0;
+
+		$bytes_array = array(
+			'B' => 1,
+			'K' => 1024,
+			'KB' => 1024,
+			'MB' => 1024 * 1024,
+			'M'  => 1024 * 1024,
+			'GB' => 1024 * 1024 * 1024,
+			'G'  => 1024 * 1024 * 1024,
+			'TB' => 1024 * 1024 * 1024 * 1024,
+			'T'  => 1024 * 1024 * 1024 * 1024,
+			'PB' => 1024 * 1024 * 1024 * 1024 * 1024,
+			'P'  => 1024 * 1024 * 1024 * 1024 * 1024,
+		);
+
+		$bytes = floatval($str);
+
+		if (preg_match('#([KMGTP]?B?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) {
+			$bytes *= $bytes_array[$matches[1]];
+		}
+
+		$bytes = intval(round($bytes, 2));
+
+		return $bytes; 
+	}
+	
+	/**
+	 * @brief Recusive editing of file permissions
+	 * @param $path path to file or folder
+	 * @param $filemode unix style file permissions as integer
+	 *
+	 */
+	static function chmodr($path, $filemode) {
+		if (!is_dir($path))
+			return chmod($path, $filemode);
+		$dh = opendir($path);
+		while (($file = readdir($dh)) !== false) {
+			if($file != '.' && $file != '..') {
+				$fullpath = $path.'/'.$file;
+				if(is_link($fullpath))
+					return FALSE;
+				elseif(!is_dir($fullpath) && !chmod($fullpath, $filemode))
+						return FALSE;
+				elseif(!self::chmodr($fullpath, $filemode))
+					return FALSE;
+			}
+		}
+		closedir($dh);
+		if(@chmod($path, $filemode))
+			return TRUE;
+		else
+			return FALSE;
+	}
+
+	/**
+	 * @brief Recusive copying of folders
+	 * @param string $src source folder
+	 * @param string $dest target folder
+	 *
+	 */
+	static function copyr($src, $dest) {
+		if(is_dir($src)){
+			if(!is_dir($dest)){
+				mkdir($dest);
+			}
+			$files = scandir($src);
+			foreach ($files as $file){
+				if ($file != "." && $file != ".."){
+					self::copyr("$src/$file", "$dest/$file");
+				}
+			}
+		}elseif(file_exists($src)){
+			copy($src, $dest);
+		}
+	}
+	
+	/**
+	 * @brief Recusive deletion of folders
+	 * @param string $dir path to the folder
+	 *
+	 */
+	static function rmdirr($dir) {
+		if(is_dir($dir)) {
+			$files=scandir($dir);
+			foreach($files as $file){
+				if ($file != "." && $file != ".."){
+					self::rmdirr("$dir/$file");
+				}
+			}
+			rmdir($dir);
+		}elseif(file_exists($dir)){
+			unlink($dir);
+		}
+	}
+	
+	/**
+	 * @brief Checks $_REQUEST contains a var for the $s key. If so, returns the html-escaped value of this var; otherwise returns the default value provided by $d.
+	 * @param $s name of the var to escape, if set.
+	 * @param $d default value.
+	 * @returns the print-safe value.
+	 *
+	 */
+	 
+	//FIXME: should also check for value validation (i.e. the email is an email).
+	public static function init_var($s, $d="") {
+		$r = $d;
+		if(isset($_REQUEST[$s]) && !empty($_REQUEST[$s]))
+			$r = stripslashes(htmlspecialchars($_REQUEST[$s]));
+		
+		return $r;
+	}
+	
+	public static function init_radio($s, $v, $d) {
+		if((isset($_REQUEST[$s]) && $_REQUEST[$s]==$v) || $v == $d)
+			print "checked=\"checked\" ";
+	}
+
+	/**
+	* detect if a given program is found in the search PATH
+	*
+	* @param  string  program name
+	* @param  string  optional search path, defaults to $PATH
+	* @return bool    true if executable program found in path
+	*/
+	public static function canExecute($name, $path = false){
+		// path defaults to PATH from environment if not set
+		if ($path === false) {
+			$path = getenv("PATH");
+		}
+		// check method depends on operating system
+		if (!strncmp(PHP_OS, "WIN", 3)) {
+			// on Windows an appropriate COM or EXE file needs to exist
+			$exts = array(".exe", ".com");
+			$check_fn = "file_exists";
+		} else {
+			// anywhere else we look for an executable file of that name
+			$exts = array("");
+			$check_fn = "is_executable";
+		}
+		// Default check will be done with $path directories :
+		$dirs = explode(PATH_SEPARATOR, $path);
+		// WARNING : We have to check if open_basedir is enabled :
+		$obd = ini_get('open_basedir');
+		if($obd != "none")
+			$obd_values = explode(PATH_SEPARATOR, $obd);
+		if(count($obd_values) > 0 and $obd_values[0])
+		{
+			// open_basedir is in effect !
+			// We need to check if the program is in one of these dirs :
+			$dirs = $obd_values;
+		}
+		foreach($dirs as $dir)
+		{
+			foreach($exts as $ext)
+			{
+				if($check_fn("$dir/$name".$ext))
+					return true;
+			}
+		}
+		return false;
+	}
+}
diff --git a/lib/hook.php b/lib/hook.php
new file mode 100644
index 0000000000000000000000000000000000000000..b069a7da6c0f36988fb270b4935107190025cc97
--- /dev/null
+++ b/lib/hook.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * This class manages the hooks. It basically provides two functions: adding
+ * slots and emitting signals.
+ */
+class OC_Hook{
+	static private $registered = array();
+
+	/**
+	 * @brief connects a function to a hook
+	 * @param $signalclass class name of emitter
+	 * @param $signalname name of signal
+	 * @param $slotclass class name of slot
+	 * @param $slotname name of slot
+	 * @returns true/false
+	 *
+	 * This function makes it very easy to connect to use hooks.
+	 *
+	 * TODO: write example
+	 */
+	static public function connect( $signalclass, $signalname, $slotclass, $slotname ){
+		// Cerate the data structure
+		if( !array_key_exists( $signalclass, self::$registered )){
+			self::$registered[$signalclass] = array();
+		}
+		if( !array_key_exists( $signalname, self::$registered[$signalclass] )){
+			self::$registered[$signalclass][$signalname] = array();
+		}
+
+		// register hook
+		self::$registered[$signalclass][$signalname][] = array(
+		  "class" => $slotclass,
+		  "name" => $slotname );
+
+		// No chance for failure ;-)
+		return true;
+	}
+
+	/**
+	 * @brief emitts a signal
+	 * @param $signalclass class name of emitter
+	 * @param $signalname name of signal
+	 * @param $params defautl: array() array with additional data
+	 * @returns true if slots exists or false if not
+	 *
+	 * Emits a signal. To get data from the slot use references!
+	 *
+	 * TODO: write example
+	 */
+	static public function emit( $signalclass, $signalname, $params = array()){
+		// Return false if there are no slots
+		if( !array_key_exists( $signalclass, self::$registered )){
+			return false;
+		}
+		if( !array_key_exists( $signalname, self::$registered[$signalclass] )){
+			return false;
+		}
+
+		// Call all slots
+		foreach( self::$registered[$signalclass][$signalname] as $i ){
+			call_user_func( array( $i["class"], $i["name"] ), $params );
+		}
+
+		// return true
+		return true;
+	}
+}
+ 
diff --git a/lib/installer.php b/lib/installer.php
new file mode 100644
index 0000000000000000000000000000000000000000..e6810a267dbd6ec73282dc8242402f5c86391ecd
--- /dev/null
+++ b/lib/installer.php
@@ -0,0 +1,286 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Robin Appelman
+ * @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 provides the functionality needed to install, update and remove plugins/apps
+ */
+class OC_Installer{
+	/**
+	 * @brief Installs an app
+	 * @param $data array with all information
+	 * @returns integer
+	 *
+	 * This function installs an app. All information needed are passed in the
+	 * associative array $data.
+	 * The following keys are required:
+	 *   - source: string, can be "path" or "http"
+	 *
+	 * One of the following keys is required:
+	 *   - path: path to the file containing the app
+	 *   - href: link to the downloadable file containing the app
+	 *
+	 * The following keys are optional:
+	 *   - pretend: boolean, if set true the system won't do anything
+	 *   - noinstall: boolean, if true appinfo/install.php won't be loaded
+	 *   - inactive: boolean, if set true the appconfig/app.sample.php won't be
+	 *     renamed
+	 *
+	 * This function works as follows
+	 *   -# fetching the file
+	 *   -# unzipping it
+	 *   -# installing the database at appinfo/database.xml
+	 *   -# including appinfo/install.php
+	 *   -# setting the installed version
+	 *
+	 * It is the task of oc_app_install to create the tables and do whatever is
+	 * needed to get the app working.
+	 */
+	public static function installApp( $data = array()){
+		global $SERVERROOT;
+		
+		if(!isset($data['source'])){
+			error_log("No source specified when installing app");
+			return false;
+		}
+		
+		//download the file if necesary
+		if($data['source']=='http'){
+			$path=tempnam(sys_get_temp_dir(),'oc_installer_');
+			if(!isset($data['href'])){
+				error_log("No href specified when installing app from http");
+				return false;
+			}
+			copy($data['href'],$path);
+		}else{
+			if(!isset($data['path'])){
+				error_log("No path specified when installing app from local file");
+				return false;
+			}
+			$path=$data['path'];
+		}
+		
+		//extract the archive in a temporary folder
+		$extractDir=tempnam(sys_get_temp_dir(),'oc_installer_uncompressed_');
+		unlink($extractDir);
+		mkdir($extractDir);
+		$zip = new ZipArchive;
+		if($zip->open($path)===true){
+			$zip->extractTo($extractDir);
+			$zip->close();
+		} else {
+			error_log("Failed to open archive when installing app");
+			OC_Helper::rmdirr($extractDir);
+			if($data['source']=='http'){
+				unlink($path);
+			}
+			return false;
+		}
+		
+		//load the info.xml file of the app
+		if(!is_file($extractDir.'/appinfo/info.xml')){
+			error_log("App does not provide an info.xml file");
+			OC_Helper::rmdirr($extractDir);
+			if($data['source']=='http'){
+				unlink($path);
+			}
+			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(self::isInstalled( $info['id'] )){
+			error_log("App already installed");
+			OC_Helper::rmdirr($extractDir);
+			if($data['source']=='http'){
+				unlink($path);
+			}
+			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 false;
+		}
+		
+		//copy the app to the correct place
+		if(!mkdir($basedir)){
+			error_log('Can\'t create app folder ('.$basedir.')');
+			OC_Helper::rmdirr($extractDir);
+			if($data['source']=='http'){
+				unlink($path);
+			}
+			return false;
+		}
+		OC_Helper::copyr($extractDir,$basedir);
+		
+		//remove temporary files
+		OC_Helper::rmdirr($extractDir);
+		if($data['source']=='http'){
+			unlink($path);
+		}
+		
+		//install the database
+		if(is_file($basedir.'/appinfo/database.xml')){
+			OC_DB::createDbFromStructure($basedir.'/appinfo/database.xml');
+		}
+		
+		//run appinfo/install.php
+		if(!isset($data['noinstall']) or $data['noinstall']==false and is_file($basedir.'/appinfo/install.php')){
+			include($basedir.'/appinfo/install.php');
+		}
+		
+		//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;
+	}
+
+	/**
+	 * @brief Update an application
+	 * @param $data array with all information
+	 * @returns integer
+	 *
+	 * This function installs an app. All information needed are passed in the
+	 * associative array $data.
+	 * The following keys are required:
+	 *   - source: string, can be "path" or "http"
+	 *
+	 * One of the following keys is required:
+	 *   - path: path to the file containing the app
+	 *   - href: link to the downloadable file containing the app
+	 *
+	 * The following keys are optional:
+	 *   - pretend: boolean, if set true the system won't do anything
+	 *   - noupgrade: boolean, if true appinfo/upgrade.php won't be loaded
+	 *
+	 * This function works as follows
+	 *   -# fetching the file
+	 *   -# removing the old files
+	 *   -# unzipping new file
+	 *   -# including appinfo/upgrade.php
+	 *   -# setting the installed version
+	 *
+	 * upgrade.php can determine the current installed version of the app using "OC_Appconfig::getValue($appid,'installed_version')"
+	 */
+	public static function upgradeApp( $data = array()){
+		// TODO: write function
+		return true;
+	}
+
+	/**
+	 * @brief Removes an app
+	 * @param $name name of the application to remove
+	 * @param $options array with options
+	 * @returns true/false
+	 *
+	 * This function removes an app. $options is an associative array. The
+	 * following keys are optional:ja
+	 *   - keeppreferences: boolean, if true the user preferences won't be deleted
+	 *   - keepappconfig: boolean, if true the config will be kept
+	 *   - keeptables: boolean, if true the database will be kept
+	 *   - keepfiles: boolean, if true the user files will be kept
+	 *
+	 * This function works as follows
+	 *   -# including appinfo/remove.php
+	 *   -# removing the files
+	 *
+	 * The function will not delete preferences, tables and the configuration,
+	 * this has to be done by the function oc_app_uninstall().
+	 */
+	public static function removeApp( $name, $options = array()){
+		// 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)){
+						OC_Installer::installShippedApp($filename);
+						if( $enabled ){
+							OC_Appconfig::setValue($filename,'enabled','yes');
+						}else{
+							OC_Appconfig::setValue($filename,'enabled','no');
+						}
+					}
+				}
+			}
+		}
+		closedir( $dir );
+	}
+
+	/**
+	 * install an app already placed in the app folder
+	 * @param string $app id of the app to install
+	 * @return bool
+	 */
+	public static function installShippedApp($app){
+		//install the database
+		if(is_file(OC::$SERVERROOT."/apps/$app/appinfo/database.xml")){
+			OC_DB::createDbFromStructure(OC::$SERVERROOT."/apps/$app/appinfo/database.xml");
+		}
+
+		//run appinfo/install.php
+		if(is_file(OC::$SERVERROOT."/apps/$app/appinfo/install.php")){
+			include(OC::$SERVERROOT."/apps/$app/appinfo/install.php");
+		}
+		$info=OC_App::getAppInfo(OC::$SERVERROOT."/apps/$app/appinfo/info.xml");
+		OC_Appconfig::setValue($app,'installed_version',$info['version']);
+	}
+}
diff --git a/lib/l10n.php b/lib/l10n.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d565ad0799b94538d84ace98cf965068fc9e092
--- /dev/null
+++ b/lib/l10n.php
@@ -0,0 +1,265 @@
+<?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
+	 * @param $parameters default:array() Parameters for sprintf
+	 * @returns Translation or the same text
+	 *
+	 * Returns the translation. If no translation is found, $text will be
+	 * returned.
+	 */
+	public function t($text, $parameters = array()){
+		if(array_key_exists($text, $this->translations)){
+			return vsprintf( $this->translations[$text], $parameters );
+		}
+		return vsprintf( $text, $parameters );
+	}
+
+	/**
+	 * @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':
+			case 'datetime':
+			case 'time':
+				if( $data instanceof DateTime ) return $data->format($this->localizations[$type]);
+				elseif( is_string( $data )) $data = strtotime( $data );
+				return date( $this->localizations[$type], $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/mimetypes.list.php b/lib/mimetypes.list.php
new file mode 100644
index 0000000000000000000000000000000000000000..2467925719995b5ece271f60de2a0807470dbfd8
--- /dev/null
+++ b/lib/mimetypes.list.php
@@ -0,0 +1,80 @@
+<?php
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2011 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+/**
+ * list of mimetypes by extention
+ */
+
+return array(
+	'css'=>'text/css',
+	'flac'=>'audio/flac',
+	'gif'=>'image/gif',
+	'gzip'=>'application/x-gzip',
+	'gz'=>'application/x-gzip',
+	'html'=>'text/html',
+	'htm'=>'text/html',
+	'jpeg'=>'image/jpeg',
+	'jpg'=>'image/jpeg',
+	'js'=>'application/javascript',
+	'oga'=>'audio/ogg',
+	'ogg'=>'audio/ogg',
+	'ogv'=>'video/ogg',
+	'pdf'=>'application/pdf',
+	'png'=>'image/png',
+	'svg'=>'image/svg+xml',
+	'tar'=>'application/x-tar',
+	'tgz'=>'application/x-compressed',
+	'tar.gz'=>'application/x-compressed',
+	'tif'=>'image/tiff',
+	'tiff'=>'image/tiff',
+	'txt'=>'text/plain',
+	'zip'=>'application/zip',
+	'wav'=>'audio/wav',
+	'odt'=>'application/vnd.oasis.opendocument.text',
+	'ods'=>'application/vnd.oasis.opendocument.spreadsheet',
+	'odg'=>'application/vnd.oasis.opendocument.graphics',
+	'odp'=>'application/vnd.oasis.opendocument.presentation',
+	'kra'=>'application/x-krita',
+	'mp3'=>'audio/mpeg',
+	'doc'=>'application/msword',
+	'docx'=>'application/msword',
+	'xls'=>'application/msexcel',
+	'xlsx'=>'application/msexcel',
+	'php'=>'application/x-php',
+	'exe'=>'application/x-ms-dos-executable',
+	'pl'=>'application/x-pearl',
+	'py'=>'application/x-python',
+	'blend'=>'application/x-blender',
+	'xcf'=>'application/x-gimp',
+	'psd'=>'application/x-photoshop',
+	'xml'=>'application/xml',
+	'avi'=>'video/x-msvideo',
+	'dv'=>'video/dv',
+	'm2t'=>'video/mp2t',
+	'mp4'=>'video/mp4',
+	'm4v'=>'video/mp4',
+	'mpg'=>'video/mpeg',
+	'mpeg'=>'video/mpeg',
+	'mov'=>'video/quicktime',
+	'webm'=>'video/webm',
+	'wmv'=>'video/x-ms-asf'
+);
diff --git a/lib/ocs.php b/lib/ocs.php
new file mode 100644
index 0000000000000000000000000000000000000000..536ee754e8438b6d268881c2275ebbc0b76644eb
--- /dev/null
+++ b/lib/ocs.php
@@ -0,0 +1,532 @@
+<?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/>.
+*
+*/
+
+
+
+/**
+ * Class to handle open collaboration services API requests
+ *
+ */
+class OC_OCS {
+
+  /**
+   * reads input date from get/post/cookies and converts the date to a special data-type
+   *
+   * @param variable $key
+   * @param variable-type $type
+   * @param priority $getpriority
+   * @param default  $default
+   * @return data
+   */
+  public static function readData($key,$type='raw',$getpriority=false,$default='') {
+    if($getpriority) {
+      if(isset($_GET[$key])) {
+        $data=$_GET[$key];
+      } elseif(isset($_POST[$key])) {
+        $data=$_POST[$key];
+      } else {
+        if($default=='') {
+          if(($type=='int') or ($type=='float')) $data=0; else $data='';
+        } else {
+          $data=$default;
+        }
+      }
+    } else {
+      if(isset($_POST[$key])) {
+        $data=$_POST[$key];
+      } elseif(isset($_GET[$key])) {
+        $data=$_GET[$key];
+      } elseif(isset($_COOKIE[$key])) {
+        $data=$_COOKIE[$key];
+      } else {
+        if($default=='') {
+          if(($type=='int') or ($type=='float')) $data=0; else $data='';
+        } else {
+          $data=$default;
+        }
+      }
+    }
+
+    if($type=='raw') return($data);
+    elseif($type=='text') return(addslashes(strip_tags($data)));
+    elseif($type=='int')  { $data = (int) $data; return($data); }
+    elseif($type=='float')  { $data = (float) $data; return($data); }
+    elseif($type=='array')  { $data = $data; return($data); }
+  }
+
+
+  /**
+    main function to handle the REST request
+  **/
+  public static function handle() {
+
+    // overwrite the 404 error page returncode
+    header("HTTP/1.0 200 OK");
+
+
+    if($_SERVER['REQUEST_METHOD'] == 'GET') {
+       $method='get';
+    }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') {
+       $method='put';
+       parse_str(file_get_contents("php://input"),$put_vars);
+    }elseif($_SERVER['REQUEST_METHOD'] == 'POST') {
+       $method='post';
+    }else{
+      echo('internal server error: method not supported');
+      exit();
+    }
+
+    // preprocess url
+    $url=$_SERVER['REQUEST_URI'];
+    if(substr($url,(strlen($url)-1))<>'/') $url.='/';
+    $ex=explode('/',$url);
+    $paracount=count($ex);
+
+    // eventhandler
+    // CONFIG
+    // apiconfig - GET - CONFIG
+    if(($method=='get') and (strtolower($ex[$paracount-3])=='v1.php') and (strtolower($ex[$paracount-2])=='config')){
+      $format=OC_OCS::readdata('format','text');
+      OC_OCS::apiconfig($format);
+
+    // PERSON
+    // personcheck - POST - PERSON/CHECK
+    }elseif(($method=='post') and (strtolower($ex[$paracount-4])=='v1.php') and (strtolower($ex[$paracount-3])=='person') and  (strtolower($ex[$paracount-2])=='check')){
+      $format=OC_OCS::readdata('format','text');
+      $login=OC_OCS::readdata('login','text');
+      $passwd=OC_OCS::readdata('password','text');
+      OC_OCS::personcheck($format,$login,$passwd);
+
+    // ACTIVITY
+    // activityget - GET ACTIVITY   page,pagesize als urlparameter
+    }elseif(($method=='get') and (strtolower($ex[$paracount-3])=='v1.php')and (strtolower($ex[$paracount-2])=='activity')){
+      $format=OC_OCS::readdata('format','text');
+      $page=OC_OCS::readdata('page','int');
+      $pagesize=OC_OCS::readdata('pagesize','int');
+      if($pagesize<1 or $pagesize>100) $pagesize=10;
+      OC_OCS::activityget($format,$page,$pagesize);
+
+    // activityput - POST ACTIVITY
+    }elseif(($method=='post') and (strtolower($ex[$paracount-3])=='v1.php')and (strtolower($ex[$paracount-2])=='activity')){
+      $format=OC_OCS::readdata('format','text');
+      $message=OC_OCS::readdata('message','text');
+      OC_OCS::activityput($format,$message);
+
+    // PRIVATEDATA
+    // get - GET DATA
+    }elseif(($method=='get') and (strtolower($ex[$paracount-4])=='v1.php')and (strtolower($ex[$paracount-2])=='getattribute')){
+      $format=OC_OCS::readdata('format','text');
+      OC_OCS::privateDataGet($format);
+
+    }elseif(($method=='get') and (strtolower($ex[$paracount-5])=='v1.php')and (strtolower($ex[$paracount-3])=='getattribute')){
+      $format=OC_OCS::readdata('format','text');
+      $app=$ex[$paracount-2];
+      OC_OCS::privateDataGet($format, $app);
+	}elseif(($method=='get') and (strtolower($ex[$paracount-6])=='v1.php')and (strtolower($ex[$paracount-4])=='getattribute')){
+      $format=OC_OCS::readdata('format','text');
+      $key=$ex[$paracount-2];
+      $app=$ex[$paracount-3];
+      OC_OCS::privateDataGet($format, $app,$key);
+
+    // set - POST DATA
+    }elseif(($method=='post') and (strtolower($ex[$paracount-6])=='v1.php')and (strtolower($ex[$paracount-4])=='setattribute')){
+      $format=OC_OCS::readdata('format','text');
+      $key=$ex[$paracount-2];
+      $app=$ex[$paracount-3];
+      $value=OC_OCS::readdata('value','text');
+      OC_OCS::privatedataset($format, $app, $key, $value);
+    // delete - POST DATA
+    }elseif(($method=='post') and (strtolower($ex[$paracount-6])=='v1.php')and (strtolower($ex[$paracount-4])=='deleteattribute')){
+      $format=OC_OCS::readdata('format','text');
+      $key=$ex[$paracount-2];
+      $app=$ex[$paracount-3];
+      OC_OCS::privatedatadelete($format, $app, $key);
+
+    }else{
+      $format=OC_OCS::readdata('format','text');
+      $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n";
+      $txt.=OC_OCS::getdebugoutput();
+      echo(OC_OCS::generatexml($format,'failed',999,$txt));
+    }
+    exit();
+  }
+
+  /**
+   * generated some debug information to make it easier to find faild API calls
+   * @return debug data string
+   */
+  private static function getDebugOutput() {
+    $txt='';
+    $txt.="debug output:\n";
+    if(isset($_SERVER['REQUEST_METHOD'])) $txt.='http request method: '.$_SERVER['REQUEST_METHOD']."\n";
+    if(isset($_SERVER['REQUEST_URI'])) $txt.='http request uri: '.$_SERVER['REQUEST_URI']."\n";
+    if(isset($_GET)) foreach($_GET as $key=>$value) $txt.='get parameter: '.$key.'->'.$value."\n";
+    if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n";
+    return($txt);
+  }
+
+  /**
+   * checks if the user is authenticated
+   * checks the IP whitlist, apikeys and login/password combination
+   * if $forceuser is true and the authentication failed it returns an 401 http response.
+   * if $forceuser is false and authentification fails it returns an empty username string
+   * @param bool $forceuser
+   * @return username string
+   */
+  private static function checkPassword($forceuser=true) {
+    //valid user account ?
+    if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser='';
+    if(isset($_SERVER['PHP_AUTH_PW']))   $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw='';
+
+    if(empty($authuser)) {
+      if($forceuser){
+        header('WWW-Authenticate: Basic realm="your valid user account or api key"');
+        header('HTTP/1.0 401 Unauthorized');
+        exit;
+      }else{
+        $identifieduser='';
+      }
+    }else{
+      if(!OC_User::login($authuser,$authpw)){
+        if($forceuser){
+          header('WWW-Authenticate: Basic realm="your valid user account or api key"');
+          header('HTTP/1.0 401 Unauthorized');
+          exit;
+        }else{
+          $identifieduser='';
+        }
+      }else{
+        $identifieduser=$authuser;
+      }
+    }
+
+    return($identifieduser);
+  }
+
+
+  /**
+   * generates the xml or json response for the API call from an multidimenional data array.
+   * @param string $format
+   * @param string $status
+   * @param string $statuscode
+   * @param string $message
+   * @param array $data
+   * @param string $tag
+   * @param string $tagattribute
+   * @param int $dimension
+   * @param int $itemscount
+   * @param int $itemsperpage
+   * @return string xml/json
+   */
+  private static function generateXml($format,$status,$statuscode,$message,$data=array(),$tag='',$tagattribute='',$dimension=-1,$itemscount='',$itemsperpage='') {
+    if($format=='json') {
+
+      $json=array();
+      $json['status']=$status;
+      $json['statuscode']=$statuscode;
+      $json['message']=$message;
+      $json['totalitems']=$itemscount;
+      $json['itemsperpage']=$itemsperpage;
+      $json['data']=$data;
+      return(json_encode($json));
+
+
+    }else{
+      $txt='';
+      $writer = xmlwriter_open_memory();
+      xmlwriter_set_indent( $writer, 2 );
+      xmlwriter_start_document($writer );
+      xmlwriter_start_element($writer,'ocs');
+      xmlwriter_start_element($writer,'meta');
+      xmlwriter_write_element($writer,'status',$status);
+      xmlwriter_write_element($writer,'statuscode',$statuscode);
+      xmlwriter_write_element($writer,'message',$message);
+      if($itemscount<>'') xmlwriter_write_element($writer,'totalitems',$itemscount);
+      if(!empty($itemsperpage)) xmlwriter_write_element($writer,'itemsperpage',$itemsperpage);
+      xmlwriter_end_element($writer);
+      if($dimension=='0') {
+        // 0 dimensions
+        xmlwriter_write_element($writer,'data',$data);
+
+      }elseif($dimension=='1') {
+        xmlwriter_start_element($writer,'data');
+        foreach($data as $key=>$entry) {
+          xmlwriter_write_element($writer,$key,$entry);
+        }
+        xmlwriter_end_element($writer);
+
+      }elseif($dimension=='2') {
+        xmlwriter_start_element($writer,'data');
+        foreach($data as $entry) {
+          xmlwriter_start_element($writer,$tag);
+          if(!empty($tagattribute)) {
+            xmlwriter_write_attribute($writer,'details',$tagattribute);
+          }
+          foreach($entry as $key=>$value) {
+            if(is_array($value)){
+              foreach($value as $k=>$v) {
+                xmlwriter_write_element($writer,$k,$v);
+              }
+            } else {
+              xmlwriter_write_element($writer,$key,$value);
+            }
+          }
+          xmlwriter_end_element($writer);
+        }
+        xmlwriter_end_element($writer);
+
+      }elseif($dimension=='3') {
+        xmlwriter_start_element($writer,'data');
+        foreach($data as $entrykey=>$entry) {
+          xmlwriter_start_element($writer,$tag);
+          if(!empty($tagattribute)) {
+            xmlwriter_write_attribute($writer,'details',$tagattribute);
+          }
+          foreach($entry as $key=>$value) {
+            if(is_array($value)){
+              xmlwriter_start_element($writer,$entrykey);
+              foreach($value as $k=>$v) {
+                xmlwriter_write_element($writer,$k,$v);
+              }
+              xmlwriter_end_element($writer);
+            } else {
+              xmlwriter_write_element($writer,$key,$value);
+            }
+          }
+          xmlwriter_end_element($writer);
+        }
+        xmlwriter_end_element($writer);
+      }elseif($dimension=='dynamic') {
+        xmlwriter_start_element($writer,'data');
+        OC_OCS::toxml($writer,$data,'comment');
+        xmlwriter_end_element($writer);
+      }
+
+      xmlwriter_end_element($writer);
+
+      xmlwriter_end_document( $writer );
+      $txt.=xmlwriter_output_memory( $writer );
+      unset($writer);
+      return($txt);
+    }
+  }
+
+  public static function toXml($writer,$data,$node) {
+    foreach($data as $key => $value) {
+      if (is_numeric($key)) {
+        $key = $node;
+      }
+      if (is_array($value)){
+        xmlwriter_start_element($writer,$key);
+        OC_OCS::toxml($writer,$value,$node);
+        xmlwriter_end_element($writer);
+      }else{
+        xmlwriter_write_element($writer,$key,$value);
+      }
+
+    }
+  }
+
+
+
+
+  /**
+   * return the config data of this server
+   * @param string $format
+   * @return string xml/json
+   */
+  private static function apiConfig($format) {
+    $user=OC_OCS::checkpassword(false);
+    $url=substr($_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'],0,-11).'';
+
+    $xml['version']='1.5';
+    $xml['website']='ownCloud';
+    $xml['host']=$_SERVER['HTTP_HOST'];
+    $xml['contact']='';
+    $xml['ssl']='false';
+    echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'config','',1));
+  }
+
+
+  /**
+   * check if the provided login/apikey/password is valid
+   * @param string $format
+   * @param string $login
+   * @param string $passwd
+   * @return string xml/json
+   */
+  private static function personCheck($format,$login,$passwd) {
+    if($login<>''){
+      if(OC_User::login($login,$passwd)){
+        $xml['person']['personid']=$login;
+        echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'person','check',2));
+      }else{
+        echo(OC_OCS::generatexml($format,'failed',102,'login not valid'));
+      }
+    }else{
+      echo(OC_OCS::generatexml($format,'failed',101,'please specify all mandatory fields'));
+    }
+  }
+
+
+
+  // ACTIVITY API #############################################
+
+  /**
+   * get my activities
+   * @param string $format
+   * @param string $page
+   * @param string $pagesize
+   * @return string xml/json
+   */
+  private static function activityGet($format,$page,$pagesize) {
+    $user=OC_OCS::checkpassword();
+    
+	//TODO
+
+    $txt=OC_OCS::generatexml($format,'ok',100,'',$xml,'activity','full',2,$totalcount,$pagesize);
+    echo($txt);
+  }
+
+  /**
+   * submit a activity
+   * @param string $format
+   * @param string $message
+   * @return string xml/json
+   */
+  private static function activityPut($format,$message) {
+    // not implemented in ownCloud
+    $user=OC_OCS::checkpassword();
+    echo(OC_OCS::generatexml($format,'ok',100,''));
+  }
+
+  // PRIVATEDATA API #############################################
+
+  /**
+   * get private data and create the xml for ocs
+   * @param string $format
+   * @param string $app
+   * @param string $key
+   * @return string xml/json
+   */
+  private static function privateDataGet($format,$app="",$key="") {
+    $user=OC_OCS::checkpassword();
+	$result=OC_OCS::getData($user,$app,$key);
+    $xml=array();
+    foreach($result as $i=>$log) {
+      $xml[$i]['key']=$log['key'];
+      $xml[$i]['app']=$log['app'];
+      $xml[$i]['value']=$log['value'];
+    }
+
+
+    $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'privatedata', 'full', 2, count($xml), 0);//TODO: replace 'privatedata' with 'attribute' once a new libattice has been released that works with it
+    echo($txt);
+  }
+
+  /**
+   * set private data referenced by $key to $value and generate the xml for ocs
+   * @param string $format
+   * @param string $app
+   * @param string $key
+   * @param string $value
+   * @return string xml/json
+   */
+	private static function privateDataSet($format, $app, $key, $value) {
+		$user=OC_OCS::checkpassword();
+		if(OC_OCS::setData($user,$app,$key,$value)){
+			echo(OC_OCS::generatexml($format,'ok',100,''));
+		}
+	}
+
+	/**
+	* delete private data referenced by $key and generate the xml for ocs
+	* @param string $format
+	* @param string $app
+	* @param string $key
+	* @return string xml/json
+	*/
+	private static function privateDataDelete($format, $app, $key) {
+		if($key=="" or $app==""){
+			return; //key and app are NOT optional here
+		}
+		$user=OC_OCS::checkpassword();
+		if(OC_OCS::deleteData($user,$app,$key)){
+			echo(OC_OCS::generatexml($format,'ok',100,''));
+		}
+	}
+	
+	/**
+	* get private data
+	* @param string $user
+	* @param string $app
+	* @param string $key
+	* @param bool $like use LIKE instead of = when comparing keys
+	* @return array
+	*/
+	public static function getData($user,$app="",$key="") {
+		if($app){
+			$apps=array($app);
+		}else{
+			$apps=OC_Preferences::getApps($user);
+		}
+		if($key){
+			$keys=array($key);
+		}else{
+			foreach($apps as $app){
+				$keys=OC_Preferences::getKeys($user,$app);
+			}
+		}
+		$result=array();
+		foreach($apps as $app){
+			foreach($keys as $key){
+				$value=OC_Preferences::getValue($user,$app,$key);
+				$result[]=array('app'=>$app,'key'=>$key,'value'=>$value);
+			}
+		}
+		return $result;
+	}
+
+	/**
+	* set private data referenced by $key to $value
+	* @param string $user
+	* @param string $app
+	* @param string $key
+	* @param string $value
+	* @return bool
+	*/
+	public static function setData($user, $app, $key, $value) {
+		return OC_Preferences::setValue($user,$app,$key,$value);
+	}
+
+	/**
+	* delete private data referenced by $key
+	* @param string $user
+	* @param string $app
+	* @param string $key
+	* @return string xml/json
+	*/
+	public static function deleteData($user, $app, $key) {
+		return OC_Preferences::deleteKey($user,$app,$key);
+	}
+}
diff --git a/lib/ocsclient.php b/lib/ocsclient.php
new file mode 100644
index 0000000000000000000000000000000000000000..654c5e0527b511af36a97a6593a6eee1314f9061
--- /dev/null
+++ b/lib/ocsclient.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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 provides an easy way for apps to store config values in the
+ * database.
+ */
+
+class OC_OCSClient{
+
+	/**
+	 * @brief Get all the categories from the OCS server
+	 * @returns array with category ids
+	 *
+	 * This function returns a list of all the application categories on the OCS server
+	 */
+	public static function getCategories(){
+		$url='http://api.apps.owncloud.com/v1/content/categories';
+	
+		$xml=@file_get_contents($url);
+		if($xml==FALSE){
+			return NULL;
+		}
+		$data=simplexml_load_string($xml);
+	
+		$tmp=$data->data;
+		$cats=array();
+
+		foreach($tmp->category as $key=>$value) {
+
+			$id= (int) $value->id;
+			$name= (string) $value->name;
+			$cats[$id]=$name;
+
+		}
+
+		return $cats;
+	}
+
+	/**
+	 * @brief Get all the applications from the OCS server
+	 * @returns array with application data
+	 *
+	 * This function returns a list of all the applications on the OCS server
+	 */
+	public static function getApplications($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);
+		if($xml==FALSE){
+			return NULL;
+		}
+		$data=simplexml_load_string($xml);
+
+		$tmp=$data->data->content;
+		for($i = 0; $i < count($tmp); $i++) {
+			$app=array();
+			$app['id']=(string)$tmp[$i]->id;
+			$app['name']=(string)$tmp[$i]->name;
+			$app['type']=(string)$tmp[$i]->typeid;
+			$app['typename']=(string)$tmp[$i]->typename;
+			$app['personid']=(string)$tmp[$i]->personid;
+			$app['license']=(string)$tmp[$i]->license;
+			$app['detailpage']=(string)$tmp[$i]->detailpage;
+			$app['preview']=(string)$tmp[$i]->smallpreviewpic1;
+			$app['changed']=strtotime($tmp[$i]->changed);
+			$app['description']=(string)$tmp[$i]->description;
+	
+			$apps[]=$app;
+		} 
+		return $apps;
+	}
+
+
+	/**
+	 * @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($page,$pagesize){	
+		$p= (int) $page;
+		$s= (int) $pagesize;
+		$url='http://api.apps.owncloud.com/v1/knowledgebase/data?type=150&page='.$p.'&pagesize='.$s;
+
+		$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;
+			$kb['detailpage']=$tmp[$i]->detailpage;
+			$kbe[]=$kb;
+		}
+		$total=$data->meta->totalitems;
+		$kbe['totalitems']=$total;
+                return $kbe;
+	}
+
+
+
+}
diff --git a/lib/preferences.php b/lib/preferences.php
new file mode 100644
index 0000000000000000000000000000000000000000..d53cdd538e0c4dec0d872e54b066b77211a774d6
--- /dev/null
+++ b/lib/preferences.php
@@ -0,0 +1,219 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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/>.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE  `preferences` (
+ * `userid` VARCHAR( 255 ) NOT NULL ,
+ * `appid` VARCHAR( 255 ) NOT NULL ,
+ * `configkey` VARCHAR( 255 ) NOT NULL ,
+ * `configvalue` VARCHAR( 255 ) NOT NULL
+ * )
+ *
+ */
+
+/**
+ * This class provides an easy way for storing user preferences.
+ */
+class OC_Preferences{
+	/**
+	 * @brief Get all users using the preferences
+	 * @returns array with user ids
+	 *
+	 * This function returns a list of all users that have at least one entry
+	 * in the preferences table.
+	 */
+	public static function getUsers(){
+		// No need for more comments
+		$query = OC_DB::prepare( 'SELECT DISTINCT( userid ) FROM *PREFIX*preferences' );
+		$result = $query->execute();
+
+		$users = array();
+		while( $row = $result->fetchRow()){
+			$users[] = $row["userid"];
+		}
+
+		return $users;
+	}
+
+	/**
+	 * @brief Get all apps of a user
+	 * @param $user user
+	 * @returns array with app ids
+	 *
+	 * This function returns a list of all apps of the userthat have at least
+	 * one entry in the preferences table.
+	 */
+	public static function getApps( $user ){
+		// No need for more comments
+		$query = OC_DB::prepare( 'SELECT DISTINCT( appid ) FROM *PREFIX*preferences WHERE userid = ?' );
+		$result = $query->execute( array( $user ));
+
+		$apps = array();
+		while( $row = $result->fetchRow()){
+			$apps[] = $row["appid"];
+		}
+
+		return $apps;
+	}
+
+	/**
+	 * @brief Get the available keys for an app
+	 * @param $user user
+	 * @param $app the app we are looking for
+	 * @returns array with key names
+	 *
+	 * This function gets all keys of an app of an user. Please note that the
+	 * values are not returned.
+	 */
+	public static function getKeys( $user, $app ){
+		// No need for more comments
+		$query = OC_DB::prepare( 'SELECT configkey FROM *PREFIX*preferences WHERE userid = ? AND appid = ?' );
+		$result = $query->execute( array( $user, $app ));
+
+		$keys = array();
+		while( $row = $result->fetchRow()){
+			$keys[] = $row["configkey"];
+		}
+
+		return $keys;
+	}
+
+	/**
+	 * @brief Gets the preference
+	 * @param $user user
+	 * @param $app app
+	 * @param $key key
+	 * @param $default = null, default value if the key does not exist
+	 * @returns the value or $default
+	 *
+	 * This function gets a value from the prefernces table. If the key does
+	 * not exist the default value will be returnes
+	 */
+	public static function getValue( $user, $app, $key, $default = null ){
+		// Try to fetch the value, return default if not exists.
+		$query = OC_DB::prepare( 'SELECT configvalue FROM *PREFIX*preferences WHERE userid = ? AND appid = ? AND configkey = ?' );
+		$result = $query->execute( array( $user, $app, $key ));
+
+		if( !$result->numRows()){
+			return $default;
+		}
+
+		$row = $result->fetchRow();
+
+		return $row["configvalue"];
+	}
+
+	/**
+	 * @brief sets a value in the preferences
+	 * @param $user user
+	 * @param $app app
+	 * @param $key key
+	 * @param $value value
+	 * @returns true/false
+	 *
+	 * Adds a value to the preferences. If the key did not exist before, it
+	 * will be added automagically.
+	 */
+	public static function setValue( $user, $app, $key, $value ){
+		// Check if the key does exist
+		$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);
+
+		if( !$exists ){
+			$query = OC_DB::prepare( 'INSERT INTO *PREFIX*preferences ( userid, appid, configkey, configvalue ) VALUES( ?, ?, ?, ? )' );
+			$query->execute( array( $user, $app, $key, $value ));
+		}
+		else{
+			$query = OC_DB::prepare( 'UPDATE *PREFIX*preferences SET configvalue = ? WHERE userid = ? AND appid = ? AND configkey = ?' );
+			$query->execute( array( $value, $user, $app, $key ));
+		}
+	}
+
+	/**
+	 * @brief Deletes a key
+	 * @param $user user
+	 * @param $app app
+	 * @param $key key
+	 * @returns true/false
+	 *
+	 * Deletes a key.
+	 */
+	public static function deleteKey( $user, $app, $key ){
+		// No need for more comments
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE userid = ? AND appid = ? AND configkey = ?' );
+		$result = $query->execute( array( $user, $app, $key ));
+
+		return true;
+	}
+
+	/**
+	 * @brief Remove app of user from preferences
+	 * @param $user user
+	 * @param $app app
+	 * @returns true/false
+	 *
+	 * Removes all keys in appconfig belonging to the app and the user.
+	 */
+	public static function deleteApp( $user, $app ){
+		// No need for more comments
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE userid = ? AND appid = ?' );
+		$result = $query->execute( array( $user, $app ));
+
+		return true;
+	}
+
+	/**
+	 * @brief Remove user from preferences
+	 * @param $user user
+	 * @returns true/false
+	 *
+	 * Removes all keys in appconfig belonging to the user.
+	 */
+	public static function deleteUser( $user ){
+		// No need for more comments
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE userid = ?' );
+		$result = $query->execute( array( $user ));
+
+		return true;
+	}
+
+	/**
+	 * @brief Remove app from all users
+	 * @param $app app
+	 * @returns true/false
+	 *
+	 * Removes all keys in preferences belonging to the app.
+	 */
+	public static function deleteAppFromAllUsers( $app ){
+		// No need for more comments
+		$query = OC_DB::prepare( 'DELETE FROM *PREFIX*preferences WHERE appid = ?' );
+		$result = $query->execute( array( $app ));
+
+		return true;
+	}
+}
diff --git a/lib/remote/cloud.php b/lib/remote/cloud.php
new file mode 100644
index 0000000000000000000000000000000000000000..2d3dee47b1ce5c76fb2cb47701a78018f6cf4b23
--- /dev/null
+++ b/lib/remote/cloud.php
@@ -0,0 +1,206 @@
+<?php
+/**
+ * Class for connection to a remote owncloud installation
+ *
+ */
+class OC_REMOTE_CLOUD{
+	private $path;
+	private $connected=false;
+	private $cookiefile=false;
+
+	/**
+	* make an api call to the remote cloud
+	* @param string $action
+	* @param array parameters
+	* @param bool assoc   when set to true, the result will be parsed as associative array
+	*
+	*/
+	private function apiCall($action,$parameters=false,$assoc=false){
+		if(!$this->cookiefile){
+			$this->cookiefile=sys_get_temp_dir().'/remoteCloudCookie'.uniqid();
+		}
+		$url=$this->path.='/files/api.php';
+		$fields_string="action=$action&";
+		if(is_array($parameters)){
+			foreach($parameters as $key=>$value){
+				$fields_string.=$key.'='.$value.'&';
+			}
+			rtrim($fields_string,'&');
+		}
+		$ch=curl_init();
+		curl_setopt($ch,CURLOPT_URL,$url);
+		curl_setopt($ch,CURLOPT_POST,count($parameters));
+		curl_setopt($ch,CURLOPT_POSTFIELDS,$fields_string);
+		curl_setopt($ch, CURLOPT_COOKIEFILE,$this->cookiefile);
+		curl_setopt($ch, CURLOPT_COOKIEJAR,$this->cookiefile);
+		curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
+		$result=curl_exec($ch);
+		$result=trim($result);
+		$info=curl_getinfo($ch);
+		$httpCode=$info['http_code'];
+		curl_close($ch);
+		if($httpCode==200 or $httpCode==0){
+			return json_decode($result,$assoc);
+		}else{
+			return false;
+		}
+	}
+
+	public function __construct($path,$user,$password){
+		$this->path=$path;
+		$this->connected=$this->apiCall('login',array('username'=>$user,'password'=>$password));
+	}
+
+	/**
+	* check if we are stull logged in on the remote cloud
+	*
+	*/
+	public function isLoggedIn(){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('checklogin');
+	}
+
+	public function __get($name){
+		switch($name){
+			case 'connected':
+				return $this->connected;
+		}
+	}
+
+	/**
+	* disconnect from the remote cloud
+	*
+	*/
+	public function disconnect(){
+		$this->connected=false;
+		if(is_file($this->cookiefile)){
+			unlink($this->cookiefile);
+		}
+		$this->cookiefile=false;
+	}
+
+	/**
+	* create a new file or directory
+	* @param string $dir
+	* @param string $name
+	* @param string $type
+	*/
+	public function newFile($dir,$name,$type){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('new',array('dir'=>$dir,'name'=>$name,'type'=>$type),true);
+	}
+
+	/**
+	* deletes a file or directory
+	* @param string $dir
+	* @param string $file
+	*/
+	public function delete($dir,$name){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('delete',array('dir'=>$dir,'file'=>$name),true);
+	}
+
+	/**
+	* moves a file or directory
+	* @param string $sorceDir
+	* @param string $sorceFile
+	* @param string $targetDir
+	* @param string $targetFile
+	*/
+	public function move($sourceDir,$sourceFile,$targetDir,$targetFile){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('move',array('sourcedir'=>$sourceDir,'source'=>$sourceFile,'targetdir'=>$targetDir,'target'=>$targetFile),true);
+	}
+
+	/**
+	* copies a file or directory
+	* @param string $sorceDir
+	* @param string $sorceFile
+	* @param string $targetDir
+	* @param string $targetFile
+	*/
+	public function copy($sourceDir,$sourceFile,$targetDir,$targetFile){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('copy',array('sourcedir'=>$sourceDir,'source'=>$sourceFile,'targetdir'=>$targetDir,'target'=>$targetFile),true);
+	}
+
+	/**
+	* get a file tree
+	* @param string $dir
+	*/
+	public function getTree($dir){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('gettree',array('dir'=>$dir),true);
+	}
+
+	/**
+	* get the files inside a directory of the remote cloud
+	* @param string $dir
+	*/
+	public function getFiles($dir){
+		if(!$this->connected){
+			return false;
+		}
+		return $this->apiCall('getfiles',array('dir'=>$dir),true);
+	}
+
+	/**
+	* get a remove file and save it in a temporary file and return the path of the temporary file
+	* @param string $dir
+	* @param string $file
+	* @return string
+	*/
+	public function getFile($dir, $file){
+		if(!$this->connected){
+			return false;
+		}
+		$ch=curl_init();
+		if(!$this->cookiefile){
+			$this->cookiefile=sys_get_temp_dir().'/remoteCloudCookie'.uniqid();
+		}
+		$tmpfile=tempnam(sys_get_temp_dir(),'remoteCloudFile');
+		$fp=fopen($tmpfile,'w+');
+		$url=$this->path.="/files/api.php?action=get&dir=$dir&file=$file";
+		curl_setopt($ch,CURLOPT_URL,$url);
+		curl_setopt($ch, CURLOPT_COOKIEFILE,$this->cookiefile);
+		curl_setopt($ch, CURLOPT_COOKIEJAR,$this->cookiefile);
+		curl_setopt($ch, CURLOPT_FILE, $fp);
+		curl_exec($ch);
+		fclose($fp);
+		curl_close($ch);
+		return $tmpfile;
+	}
+
+	public function sendFile($sourceDir,$sourceFile,$targetDir,$targetFile){
+		global $WEBROOT;
+		$source=$sourceDir.'/'.$sourceFile;
+		$tmp=OC_Filesystem::toTmpFile($source);
+		return $this->sendTmpFile($tmp,$targetDir,$targetFile);
+	}
+
+	public function sendTmpFile($tmp,$targetDir,$targetFile){
+		$token=sha1(uniqid().$tmp);
+		global $WEBROOT;
+		$file=sys_get_temp_dir().'/'.'remoteCloudFile'.$token;
+		rename($tmp,$file);
+		if( OC_Config::getValue( "forcessl", false ) or isset($_SERVER['HTTPS']) and $_SERVER['HTTPS'] == 'on') {
+			$url = "https://". $_SERVER['SERVER_NAME'] . $WEBROOT;
+		}else{
+			$url = "http://". $_SERVER['SERVER_NAME'] . $WEBROOT;
+		}
+		return $this->apiCall('pull',array('dir'=>$targetDir,'file'=>$targetFile,'token'=>$token,'source'=>$url),true);
+	}
+}
+ 
\ No newline at end of file
diff --git a/lib/search.php b/lib/search.php
new file mode 100644
index 0000000000000000000000000000000000000000..f6f805bfe6587e2dc76ac25a91aad504f0e045bc
--- /dev/null
+++ b/lib/search.php
@@ -0,0 +1,50 @@
+<?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/>.
+ *
+ */
+
+
+/**
+ * provides an interface to all search providers
+ */
+class OC_Search{
+	static private $providers=array();
+	
+	/**
+	 * register a new search provider to be used
+	 * @param OC_Search_Provider $provider
+	 */
+	public static function registerProvider($provider){
+		self::$providers[]=$provider;
+	}
+	
+	/**
+	 * search all provider for $query
+	 * @param string query
+	 * @return array An array of OC_Search_Result's
+	 */
+	public static function search($query){
+		$results=array();
+		foreach(self::$providers as $provider){
+			$results=array_merge($results,$provider->search($query));
+		}
+		return $results;
+	}
+}
diff --git a/lib/search/provider.php b/lib/search/provider.php
new file mode 100644
index 0000000000000000000000000000000000000000..cceed8b04a38f7353003650a54b61dcddc3994e2
--- /dev/null
+++ b/lib/search/provider.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * provides search functionalty
+ */
+abstract class OC_Search_Provider{
+	public function __construct(){
+		OC_Search::registerProvider($this);
+	}
+
+	/**
+	 * search for $query
+	 * @param string $query
+	 * @return array An array of OC_Search_Result's
+	 */
+	abstract function search($query);
+}
diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php
new file mode 100644
index 0000000000000000000000000000000000000000..5fd35fa3e523416bba7d2eb647297b2845a185b5
--- /dev/null
+++ b/lib/search/provider/file.php
@@ -0,0 +1,33 @@
+<?php
+
+class OC_Search_Provider_File extends OC_Search_Provider{
+	function search($query){
+		$files=OC_Filesystem::search($query);
+		$results=array();
+		foreach($files as $file){
+			if(OC_Filesystem::is_dir($file)){
+				$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'index.php?dir='.$file ),'Files');
+			}else{
+				$mime=OC_Filesystem::getMimeType($file);
+				$mimeBase=substr($mime,0,strpos($mime,'/'));
+				switch($mimeBase){
+					case 'audio':
+						break;
+					case 'text':
+						$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'download.php?file='.$file ),'Text');
+						break;
+					case 'image':
+						$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'download.php?file='.$file ),'Images');
+						break;
+					default:
+						if($mime=='application/xml'){
+							$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'download.php?file='.$file ),'Text');
+						}else{
+							$results[]=new OC_Search_Result(basename($file),'',OC_Helper::linkTo( 'files', 'download.php?file='.$file ),'Files');
+						}
+				}
+			}
+		}
+		return $results;
+	}
+}
diff --git a/lib/search/result.php b/lib/search/result.php
new file mode 100644
index 0000000000000000000000000000000000000000..cd78a5cf25390e380bc1606009cfbdbf69cabe78
--- /dev/null
+++ b/lib/search/result.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * a result of a search
+ */
+class OC_Search_Result{
+	public $name;
+	public $text;
+	public $link;
+	public $type;
+
+	/**
+	 * create a new search result
+	 * @param string $name short name for the result
+	 * @param string $text some more information about the result
+	 * @param string $link link for the result
+	 * @param string $type the type of result as human readable string ('File', 'Music', etc)
+	 */
+	public function __construct($name,$text,$link,$type){
+		$this->name=$name;
+		$this->text=$text;
+		$this->link=$link;
+		$this->type=$type;
+	}
+}
diff --git a/lib/setup.php b/lib/setup.php
new file mode 100644
index 0000000000000000000000000000000000000000..9bb6e2df513422994b2ab16c43746e0481476e64
--- /dev/null
+++ b/lib/setup.php
@@ -0,0 +1,294 @@
+<?php
+
+$hasSQLite = (is_callable('sqlite_open') or class_exists('SQLite3'));
+$hasMySQL = is_callable('mysql_connect');
+$hasPostgreSQL = is_callable('pg_connect');
+$datadir = OC_Config::getValue('datadir', $SERVERROOT.'/data');
+$opts = array(
+	'hasSQLite' => $hasSQLite,
+	'hasMySQL' => $hasMySQL,
+	'hasPostgreSQL' => $hasPostgreSQL,
+	'directory' => $datadir,
+	'errors' => array(),
+);
+
+if(isset($_POST['install']) AND $_POST['install']=='true') {
+	// We have to launch the installation process :
+	$e = OC_Setup::install($_POST);
+	$errors = array('errors' => $e);
+	
+	if(count($e) > 0) {
+		//OC_Template::printGuestPage("", "error", array("errors" => $errors));
+		$options = array_merge($_POST, $opts, $errors);
+		OC_Template::printGuestPage("", "installation", $options);
+	}
+	else {
+		header("Location: ".$WEBROOT.'/');
+		exit();
+	}
+}
+else {
+	OC_Template::printGuestPage("", "installation", $opts);
+}
+
+class OC_Setup {
+	public static function install($options) {
+		$error = array();
+		$dbtype = $options['dbtype'];
+		
+		if(empty($options['adminlogin'])) {
+			$error[] = 'Set an admin username.';
+		}
+		if(empty($options['adminpass'])) {
+			$error[] = 'Set an admin password.';
+		}
+		if(empty($options['directory'])) {
+			$error[] = 'Specify a data folder.';
+		}
+
+		if($dbtype=='mysql' or $dbtype=='pgsql') { //mysql and postgresql needs more config options
+			if($dbtype=='mysql')
+				$dbprettyname = 'MySQL';
+			else
+				$dbprettyname = 'PostgreSQL';
+
+			if(empty($options['dbuser'])) {
+				$error[] = "$dbprettyname enter the database username.";
+			}
+			if(empty($options['dbpass'])) {
+				$error[] = "$dbprettyname enter the database password.";
+			}
+			if(empty($options['dbname'])) {
+				$error[] = "$dbprettyname enter the database name.";
+			}
+			if(empty($options['dbhost'])) {
+				$error[] = "$dbprettyname set the database host.";
+			}
+			if(!isset($options['dbtableprefix'])) {
+				$error[] = "$dbprettyname set a table prefix.";
+			}
+		}
+
+		if(count($error) == 0) { //no errors, good
+			$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('version',implode('.',OC_Util::getVersion()));
+			if($dbtype == 'mysql') {
+				$dbuser = $options['dbuser'];
+				$dbpass = $options['dbpass'];
+				$dbname = $options['dbname'];
+				$dbhost = $options['dbhost'];
+				$dbtableprefix = $options['dbtableprefix'];
+				OC_Config::setValue('dbname', $dbname);
+				OC_Config::setValue('dbhost', $dbhost);
+				OC_Config::setValue('dbtableprefix', $dbtableprefix);
+
+				//check if the database user has admin right
+				$connection = @mysql_connect($dbhost, $dbuser, $dbpass);
+				if(!$connection) {
+					$error[] = array(
+						'error' => 'MySQL username and/or password not valid',
+						'hint' => 'You need to enter either an existing account or the administrator.'
+					);
+				}
+				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)) {
+						//use the admin login data for the new database user
+
+						//add prefix to the mysql user name to prevent collissions
+						$dbusername=substr('oc_mysql_'.$username,0,16);
+						//hash the password so we don't need to store the admin config in the config file
+						$dbpassword=md5(time().$password);
+						
+						self::createDBUser($dbusername, $dbpassword, $connection);
+						
+						OC_Config::setValue('dbuser', $dbusername);
+						OC_Config::setValue('dbpassword', $dbpassword);
+
+						//create the database
+						self::createDatabase($dbname, $dbusername, $connection);
+					}
+					else {
+						OC_Config::setValue('dbuser', $dbuser);
+						OC_Config::setValue('dbpassword', $dbpass);
+
+						//create the database
+						self::createDatabase($dbname, $dbuser, $connection);
+					}
+
+					//fill the database if needed
+					$query="SELECT * FROM $dbname.{$dbtableprefix}users";
+					$result = mysql_query($query,$connection);
+					if(!$result) {
+						OC_DB::createDbFromStructure('db_structure.xml');
+					}
+					mysql_close($connection);
+				}
+			}
+			elseif($dbtype == 'pgsql') {
+				$dbuser = $options['dbuser'];
+				$dbpass = $options['dbpass'];
+				$dbname = $options['dbname'];
+				$dbhost = $options['dbhost'];
+				$dbtableprefix = $options['dbtableprefix'];
+				OC_CONFIG::setValue('dbname', $dbname);
+				OC_CONFIG::setValue('dbhost', $dbhost);
+				OC_CONFIG::setValue('dbtableprefix', $dbtableprefix);
+
+				//check if the database user has admin right
+				$connection_string = "host=$dbhost dbname=postgres user=$dbuser password=$dbpass";
+				$connection = @pg_connect($connection_string);
+				if(!$connection) {
+					$error[] = array(
+						'error' => 'PostgreSQL username and/or password not valid',
+						'hint' => 'You need to enter either an existing account or the administrator.'
+					);
+				}
+				else {
+					//check for roles creation rights in postgresql
+					$query="SELECT 1 FROM pg_roles WHERE rolcreaterole=TRUE AND rolname='$dbuser'";
+					$result = pg_query($connection, $query);
+					if($result and pg_num_rows($result) > 0) {
+						//use the admin login data for the new database user
+
+						//add prefix to the postgresql user name to prevent collissions
+						$dbusername='oc_'.$username;
+						//hash the password so we don't need to store the admin config in the config file
+						$dbpassword=md5(time().$password);
+						
+						self::pg_createDBUser($dbusername, $dbpassword, $connection);
+						
+						OC_CONFIG::setValue('dbuser', $dbusername);
+						OC_CONFIG::setValue('dbpassword', $dbpassword);
+
+						//create the database
+						self::pg_createDatabase($dbname, $dbusername, $connection);
+					}
+					else {
+						OC_CONFIG::setValue('dbuser', $dbuser);
+						OC_CONFIG::setValue('dbpassword', $dbpass);
+
+						//create the database
+						self::pg_createDatabase($dbname, $dbuser, $connection);
+					}
+
+					//fill the database if needed
+					$query="SELECT * FROM {$dbtableprefix}users";
+					$result = pg_query($connection, $query);
+					if(!$result) {
+						OC_DB::createDbFromStructure('db_structure.xml');
+					}
+					pg_close($connection);
+				}
+			}
+			else {
+				//delete the old sqlite database first, might cause infinte loops otherwise
+				if(file_exists("$datadir/owncloud.db")){
+					unlink("$datadir/owncloud.db");
+				}
+				//in case of sqlite, we can always fill the database
+				OC_DB::createDbFromStructure('db_structure.xml');
+			}
+
+			if(count($error) == 0) {
+				//create the user and group
+				OC_User::createUser($username, $password);
+				OC_Group::createGroup('admin');
+				OC_Group::addToGroup($username, 'admin');
+				OC_User::login($username, $password);
+
+				//guess what this does
+				OC_Installer::installShippedApps(true);
+
+				//create htaccess files for apache hosts
+				if (strstr($_SERVER['SERVER_SOFTWARE'], 'Apache')) {
+					self::createHtaccess();
+				}
+
+				//and we are done
+				OC_Config::setValue('installed', true);
+			}
+		}
+
+		return $error;
+	}
+
+	public static function createDatabase($name,$user,$connection) {
+		//we cant user OC_BD functions here because we need to connect as the administrative user.
+		$query = "CREATE DATABASE IF NOT EXISTS  `$name`";
+		$result = mysql_query($query, $connection);
+		if(!$result) {
+			$entry='DB Error: "'.mysql_error($connection).'"<br />';
+			$entry.='Offending command was: '.$query.'<br />';
+			echo($entry);
+		}
+		$query="GRANT ALL PRIVILEGES ON  `$name` . * TO  '$user'";
+		$result = mysql_query($query, $connection); //this query will fail if there aren't the right permissons, ignore the error
+	}
+
+	private static function createDBUser($name,$password,$connection) {
+		// we need to create 2 accounts, one for global use and one for local user. if we don't speccify the local one,
+		// the anonymous user would take precedence when there is one.
+		$query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'";
+		$result = mysql_query($query, $connection);
+		$query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'";
+		$result = mysql_query($query, $connection);
+	}
+
+	public static function pg_createDatabase($name,$user,$connection) {
+		//we cant user OC_BD functions here because we need to connect as the administrative user.
+		$query = "CREATE DATABASE $name OWNER $user";
+		$result = pg_query($connection, $query);
+		if(!$result) {
+			$entry='DB Error: "'.pg_last_error($connection).'"<br />';
+			$entry.='Offending command was: '.$query.'<br />';
+			echo($entry);
+		}
+		$query = "REVOKE ALL PRIVILEGES ON DATABASE $name FROM PUBLIC";
+		$result = pg_query($connection, $query);		
+	}
+
+	private static function pg_createDBUser($name,$password,$connection) {
+		$query = "CREATE USER $name CREATEDB PASSWORD '$password';";
+		$result = pg_query($connection, $query);
+		if(!$result) {
+			$entry='DB Error: "'.pg_last_error($connection).'"<br />';
+			$entry.='Offending command was: '.$query.'<br />';
+			echo($entry);
+		}
+	}
+
+	/**
+	 * create .htaccess files for apache hosts
+	 */
+	private static function createHtaccess() {
+		$SERVERROOT=OC::$SERVERROOT;
+		$WEBROOT=OC::$WEBROOT;
+		$content = "ErrorDocument 404 $WEBROOT/core/templates/404.php\n";//custom 404 error page
+		$content.= "<IfModule mod_php5.c>\n";
+		$content.= "php_value upload_max_filesize 512M\n";//upload limit
+		$content.= "php_value post_max_size 512M\n";
+		$content.= "SetEnv htaccessWorking true\n";
+		$content.= "</IfModule>\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\n";
+		$content.= "IndexIgnore *";
+		file_put_contents(OC_Config::getValue('datadirectory', $SERVERROOT.'/data').'/.htaccess', $content);
+		file_put_contents(OC_Config::getValue('datadirectory', $SERVERROOT.'/data').'/index.html', '');
+	}
+}
+
+?>
diff --git a/lib/template.php b/lib/template.php
new file mode 100644
index 0000000000000000000000000000000000000000..adedd4f901a3025e8a28ed8ef49e6dfb1b6eefed
--- /dev/null
+++ b/lib/template.php
@@ -0,0 +1,378 @@
+<?php
+/**
+ * ownCloud
+ *
+ * @author Frank Karlitschek
+ * @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/>.
+ *
+ */
+
+/**
+ * @brief make OC_Helper::linkTo available as a simple function
+ * @param $app app
+ * @param $file file
+ * @returns link to the file
+ *
+ * For further information have a look at OC_Helper::linkTo
+ */
+function link_to( $app, $file ){
+	return OC_Helper::linkTo( $app, $file );
+}
+
+/**
+ * @brief make OC_Helper::imagePath available as a simple function
+ * @param $app app
+ * @param $image image
+ * @returns link to the image
+ *
+ * For further information have a look at OC_Helper::imagePath
+ */
+function image_path( $app, $image ){
+	return OC_Helper::imagePath( $app, $image );
+}
+
+/**
+ * @brief make OC_Helper::mimetypeIcon available as a simple function
+ * @param $mimetype mimetype
+ * @returns link to the image
+ *
+ * For further information have a look at OC_Helper::mimetypeIcon
+ */
+function mimetype_icon( $mimetype ){
+	return OC_Helper::mimetypeIcon( $mimetype );
+}
+
+/**
+ * @brief make OC_Helper::humanFileSize available as a simple function
+ * @param $bytes size in bytes
+ * @returns size as string
+ *
+ * For further information have a look at OC_Helper::humanFileSize
+ */
+function human_file_size( $bytes ){
+	return OC_Helper::humanFileSize( $bytes );
+}
+
+function simple_file_size($bytes) {
+	$mbytes = round($bytes/(1024*1024),1);
+	if($bytes == 0) { return '0'; }
+	else if($mbytes < 0.1) { return '&lt; 0.1'; }
+	else if($mbytes > 1000) { return '&gt; 1000'; }
+	else { return number_format($mbytes, 1); }
+}
+
+function relative_modified_date($timestamp) {
+    $l=new OC_L10N('template');
+	$timediff = time() - $timestamp;
+	$diffminutes = round($timediff/60);
+	$diffhours = round($diffminutes/60);
+	$diffdays = round($diffhours/24);
+	$diffmonths = round($diffdays/31);
+	$diffyears = round($diffdays/365);
+
+	if($timediff < 60) { return $l->t('seconds ago'); }
+	else if($timediff < 120) { return $l->t('1 minute ago'); }
+	else if($timediff < 3600) { return $l->t('%d minutes ago',$diffminutes); }
+	//else if($timediff < 7200) { return '1 hour ago'; }
+	//else if($timediff < 86400) { return $diffhours.' hours ago'; }
+	else if((date('G')-$diffhours) > 0) { return $l->t('today'); }
+	else if((date('G')-$diffhours) > -24) { return $l->t('yesterday'); }
+	else if($timediff < 2678400) { return $l->t('%d days ago',$diffdays); }
+	else if($timediff < 5184000) { return $l->t('last month'); }
+	else if((date('n')-$diffmonths) > 0) { return $l->t('months ago'); }
+	else if($timediff < 63113852) { return $l->t('last year'); }
+	else { return $l->t('years ago'); }
+}
+
+
+/**
+ * This class provides the templates for owncloud.
+ */
+class OC_Template{
+	private $renderas; // Create a full page?
+	private $application; // template Application
+	private $vars; // Vars
+	private $template; // The path to the template
+	private $l10n; // The l10n-Object
+	private $headers=array(); //custom headers
+
+	/**
+	 * @brief Constructor
+	 * @param $app app providing the template
+	 * @param $file name of the tempalte file (without suffix)
+	 * @param $renderas = ""; produce a full page
+	 * @returns OC_Template object
+	 *
+	 * This function creates an OC_Template object.
+	 *
+	 * If $renderas is set, OC_Template will try to produce a full page in the
+	 * according layout. For now, renderas can be set to "guest", "user" or
+	 * "admin".
+	 */
+	public function __construct( $app, $name, $renderas = "" ){
+		// Global vars we need
+		global $SERVERROOT;
+
+		// Get the right template folder
+		$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/";
+			}
+			else{
+				$template = "$SERVERROOT/$app/templates/";
+			}
+		}
+
+		// Templates have the ending .php
+		$path = $template;
+		$template .= "$name.php";
+
+		// Set the private data
+		$this->renderas = $renderas;
+		$this->application = $app;
+		$this->template = $template;
+		$this->path = $path;
+		$this->vars = array();
+		$this->l10n = new OC_L10N($app);
+	}
+
+	/**
+	 * @brief Assign variables
+	 * @param $key key
+	 * @param $value value
+	 * @returns true
+	 *
+	 * This function assigns a variable. It can be accessed via $_[$key] in
+	 * the template.
+	 *
+	 * If the key existed before, it will be overwritten
+	 */
+	public function assign( $key, $value ){
+		$this->vars[$key] = $value;
+		return true;
+	}
+
+	/**
+	 * @brief Appends a variable
+	 * @param $key key
+	 * @param $value value
+	 * @returns true
+	 *
+	 * This function assigns a variable in an array context. If the key already
+	 * exists, the value will be appended. It can be accessed via
+	 * $_[$key][$position] in the template.
+	 */
+	public function append( $key, $value ){
+		if( array_key_exists( $key, $this->vars )){
+			$this->vars[$key][] = $value;
+		}
+		else{
+			$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
+	 * @returns true/false
+	 *
+	 * This function proceeds the template and prints its output.
+	 */
+	public function printPage(){
+		$data = $this->fetchPage();
+		if( $data === false ){
+			return false;
+		}
+		else{
+			print $data;
+			return true;
+		}
+	}
+
+	/**
+	 * @brief Proceeds the template
+	 * @returns content
+	 *
+	 * This function proceeds the template. If $this->renderas is set, it will
+	 * will produce a full page.
+	 */
+	public function fetchPage(){
+		// global Data we need
+		global $WEBROOT;
+		global $SERVERROOT;
+		$data = $this->_fetch();
+
+		if( $this->renderas ){
+			// Decide which page we show
+			if( $this->renderas == "user" ){
+				$page = new OC_Template( "core", "layout.user" );
+				$page->assign('searchurl',OC_Helper::linkTo( 'search', 'index.php' ));
+				if(array_search(OC_APP::getCurrentApp(),array('settings','admin','help'))!==false){
+					$page->assign('bodyid','body-settings');
+				}else{
+					$page->assign('bodyid','body-user');
+				}
+
+				// Add navigation entry
+				$page->assign( "navigation", OC_App::getNavigation());
+				$page->assign( "settingsnavigation", OC_App::getSettingsNavigation());
+			}else{
+				$page = new OC_Template( "core", "layout.guest" );
+			}
+
+			// 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" );
+				}
+				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" );
+				}
+				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();
+		}
+		else{
+			return $data;
+		}
+	}
+
+	/**
+	 * @brief doing the actual work
+	 * @returns content
+	 *
+	 * Includes the template file, fetches its output
+	 */
+	private function _fetch(){
+		// Register the variables
+		$_ = $this->vars;
+		$l = $this->l10n;
+
+		// Execute the template
+		ob_start();
+		include( $this->template ); // <-- we have to use include because we pass $_!
+		$data = ob_get_contents();
+		ob_end_clean();
+
+		// return the data
+		return $data;
+	}
+
+	/**
+	 * @brief Include template
+	 * @returns returns content of included template
+	 *
+	 * Includes another template. use <?php echo $this->inc('template'); ?> to
+	 * do this.
+	 */
+	public function inc( $file, $additionalparams = null ){
+		// $_ erstellen
+		$_ = $this->vars;
+		$l = $this->l10n;
+
+		if( !is_null($additionalparams)){
+			$_ = array_merge( $additionalparams, $this->vars );
+		}
+
+		// Einbinden
+		ob_start();
+		include( $this->path.$file.'.php' );
+		$data = ob_get_contents();
+		ob_end_clean();
+
+		// Daten zurückgeben
+		return $data;
+	}
+
+	/**
+	 * @brief Shortcut to print a simple page for users
+	 * @param $application The application we render the template for
+	 * @param $name Name of the template
+	 * @param $parameters Parameters for the template
+	 * @returns true/false
+	 */
+	public static function printUserPage( $application, $name, $parameters = array() ){
+		$content = new OC_Template( $application, $name, "user" );
+		foreach( $parameters as $key => $value ){
+			$content->assign( $key, $value );
+		}
+		print $content->printPage();
+	}
+
+	/**
+	 * @brief Shortcut to print a simple page for admins
+	 * @param $application The application we render the template for
+	 * @param $name Name of the template
+	 * @param $parameters Parameters for the template
+	 * @returns true/false
+	 */
+	public static function printAdminPage( $application, $name, $parameters = array() ){
+		$content = new OC_Template( $application, $name, "admin" );
+		foreach( $parameters as $key => $value ){
+			$content->assign( $key, $value );
+		}
+		return $content->printPage();
+	}
+
+	/**
+	 * @brief Shortcut to print a simple page for guests
+	 * @param $application The application we render the template for
+	 * @param $name Name of the template
+	 * @param $parameters Parameters for the template
+	 * @returns true/false
+	 */
+	public static function printGuestPage( $application, $name, $parameters = array() ){
+		$content = new OC_Template( $application, $name, "guest" );
+		foreach( $parameters as $key => $value ){
+			$content->assign( $key, $value );
+		}
+		return $content->printPage();
+	}
+}
diff --git a/lib/testcase.php b/lib/testcase.php
new file mode 100644
index 0000000000000000000000000000000000000000..19494dc2f193f302918f5542ce5da0b8d70f1f44
--- /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
new file mode 100644
index 0000000000000000000000000000000000000000..0630ebb93848d233a6266530559b8246d9bcaf25
--- /dev/null
+++ b/lib/user.php
@@ -0,0 +1,354 @@
+<?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/>.
+ *
+ */
+
+/**
+ * This class provides all methods for user management.
+ *
+ * Hooks provided:
+ *   pre_createUser(&run, uid, password)
+ *   post_createUser(uid, password)
+ *   pre_deleteUser(&run, uid)
+ *   post_deleteUser(uid)
+ *   pre_setPassword(&run, uid, password)
+ *   post_setPassword(uid, password)
+ *   pre_login(&run, uid)
+ *   post_login(uid)
+ *   logout()
+ */
+class OC_User {
+	// The backend used for user management
+	private static $_usedBackends = array();
+
+	// Backends available (except database)
+	private static $_backends = array();
+
+	/**
+	 * @brief registers backend
+	 * @param $name name of the backend
+	 * @returns true/false
+	 *
+	 * Makes a list of backends that can be used by other modules
+	 */
+	public static function registerBackend( $name ){
+		self::$_backends[] = $name;
+		return true;
+	}
+
+	/**
+	 * @brief gets available backends
+	 * @returns array of backends
+	 *
+	 * Returns the names of all backends.
+	 */
+	public static function getBackends(){
+		return self::$_backends;
+	}
+
+	/**
+	 * @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 useBackend( $backend = 'database' ){
+		// You'll never know what happens
+		if( null === $backend OR !is_string( $backend )){
+			$backend = 'database';
+		}
+
+		// Load backend
+		switch( $backend ){
+			case 'database':
+			case 'mysql':
+			case 'sqlite':
+				self::$_usedBackends[$backend] = new OC_User_Database();
+				break;
+			default:
+				$className = 'OC_USER_' . strToUpper($backend);
+				self::$_usedBackends[$backend] = new $className();
+				break;
+		}
+
+		true;
+	}
+
+	/**
+	 * @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.
+	 *
+	 * Allowed characters in the username are: "a-z", "A-Z", "0-9" and "_.@-"
+	 */
+	public static function createUser( $uid, $password ){
+		// Check the name for bad characters
+		// Allowed are: "a-z", "A-Z", "0-9" and "_.@-"
+		if( preg_match( '/[^a-zA-Z0-9 _\.@\-]/', $uid )){
+			return false;
+		}
+		// No empty username
+		if( !$uid ){
+			return false;
+		}
+		// Check if user already exists
+		if( self::userExists($uid) ){
+			return false;
+		}
+
+
+		$run = true;
+		OC_Hook::emit( "OC_User", "pre_createUser", array( "run" => &$run, "uid" => $uid, "password" => $password ));
+
+		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;
+	}
+
+	/**
+	 * @brief delete a user
+	 * @param $uid The username of the user to delete
+	 * @returns true/false
+	 *
+	 * Deletes a user
+	 */
+	public static function deleteUser( $uid ){
+		$run = true;
+		OC_Hook::emit( "OC_User", "pre_deleteUser", array( "run" => &$run, "uid" => $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 );
+			}
+
+			// Emit and exit
+			OC_Hook::emit( "OC_User", "post_deleteUser", array( "uid" => $uid ));
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @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 ){
+		$run = true;
+		OC_Hook::emit( "OC_User", "pre_login", array( "run" => &$run, "uid" => $uid ));
+
+		if( $run ){
+			$uid=self::checkPassword( $uid, $password );
+			if($uid){
+				$_SESSION['user_id'] = $uid;
+				OC_Crypt::init($uid,$password);
+				OC_Hook::emit( "OC_User", "post_login", array( "uid" => $uid ));
+				return true;
+			}else{
+				return false;
+			}
+		}else{
+			return false;
+		}
+	}
+
+	/**
+	 * @brief Kick the user
+	 * @returns true
+	 *
+	 * Logout, destroys session
+	 */
+	public static function logout(){
+		OC_Hook::emit( "OC_User", "logout", array());
+		$_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 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;
+		}
+	}
+
+	/**
+	 * @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
+	 * @returns true/false
+	 *
+	 * Change the password of a user
+	 */
+	public static function setPassword( $uid, $password ){
+		$run = true;
+		OC_Hook::emit( "OC_User", "pre_setPassword", array( "run" => &$run, "uid" => $uid, "password" => $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;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @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 ){
+		foreach(self::$_usedBackends as $backend){
+			if($backend->implementsActions(OC_USER_BACKEND_CHECK_PASSWORD)){
+				$result=$backend->checkPassword( $uid, $password );
+				if($result){
+					return $result;
+				}
+			}
+		}
+	}
+
+	/**
+	 * @brief Get a list of all users
+	 * @returns array with all uids
+	 *
+	 * Get a list of all users.
+	 */
+	public static function 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;
+	}
+
+	/**
+	 * @brief Set cookie value to use in next page load
+	 * @param string $username username to be set
+	 */
+	public static function setUsernameInCookie($username){
+		setcookie("username", $username, mktime().time()+60*60*24*15);
+	}
+
+	/**
+	 * @brief Remove cookie for "remember username"
+	 */
+	public static function unsetUsernameInCookie(){
+		unset($_COOKIE["username"]);
+		setcookie("username", NULL, -1);
+	}
+}
diff --git a/lib/user/backend.php b/lib/user/backend.php
new file mode 100644
index 0000000000000000000000000000000000000000..4afdf152150f2015af6b135d7651c177f2a492cb
--- /dev/null
+++ b/lib/user/backend.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * 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
+ * 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/>.
+ *
+ */
+
+/**
+ * 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 {
+
+	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 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;
+			}
+		}
+
+		return $actions;
+	}
+
+	/**
+	* @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
new file mode 100644
index 0000000000000000000000000000000000000000..f29aaf00f05ec84926532029055360162af67a52
--- /dev/null
+++ b/lib/user/database.php
@@ -0,0 +1,145 @@
+<?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/>.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE `users` (
+ *   `uid` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
+ *   `password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
+ *   PRIMARY KEY (`uid`)
+ * ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
+ *
+ */
+
+/**
+ * Class for user management in a SQL Database (e.g. MySQL, SQLite)
+ */
+class OC_User_Database extends OC_User_Backend {
+	static private $userGroupCache=array();
+
+	/**
+	 * @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 ){
+		if( $this->userExists($uid) ){
+			return false;
+		}
+		else{
+			$query = OC_DB::prepare( "INSERT INTO `*PREFIX*users` ( `uid`, `password` ) VALUES( ?, ? )" );
+			$result = $query->execute( array( $uid, sha1( $password )));
+
+			return $result ? true : false;
+		}
+	}
+
+	/**
+	 * @brief delete a user
+	 * @param $uid The username of the user to delete
+	 * @returns true/false
+	 *
+	 * Deletes a user
+	 */
+	public function deleteUser( $uid ){
+		// Delete user-group-relation
+		$query = OC_DB::prepare( "DELETE FROM `*PREFIX*users` WHERE uid = ?" );
+		$result = $query->execute( array( $uid ));
+		return true;
+	}
+
+	/**
+	 * @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 ){
+		if( $this->userExists($uid) ){
+			$query = OC_DB::prepare( "UPDATE *PREFIX*users SET password = ? WHERE uid = ?" );
+			$result = $query->execute( array( sha1( $password ), $uid ));
+
+			return true;
+		}
+		else{
+			return false;
+		}
+	}
+
+	/**
+	 * @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 ){
+		$query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users WHERE uid LIKE ? AND password = ?" );
+		$result = $query->execute( array( $uid, sha1( $password )));
+
+		if( $result->numRows() > 0 ){
+			$row=$result->fetchRow();
+			return $row['uid'];
+		}else{
+			return false;
+		}
+	}
+
+	/**
+	 * @brief Get a list of all users
+	 * @returns array with all uids
+	 *
+	 * Get a list of all users.
+	 */
+	public function getUsers(){
+		$query = OC_DB::prepare( "SELECT uid FROM *PREFIX*users" );
+		$result = $query->execute();
+
+		$users=array();
+		while( $row = $result->fetchRow()){
+			$users[] = $row["uid"];
+		}
+		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 0000000000000000000000000000000000000000..7481014de77c5fb54ff8183abfdd529886e10573
--- /dev/null
+++ b/lib/user/example.php
@@ -0,0 +1,95 @@
+<?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/>.
+ *
+ */
+
+/**
+ * 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/util.php b/lib/util.php
new file mode 100644
index 0000000000000000000000000000000000000000..f4ca879a9bc589f6af9486ab447c4918a3fad650
--- /dev/null
+++ b/lib/util.php
@@ -0,0 +1,256 @@
+<?php
+
+/**
+ * Class for utility functions
+ *
+ */
+class OC_Util {
+	public static $scripts=array();
+	public static $styles=array();
+	public static $headers=array();
+	private static $fsSetup=false;
+
+	// Can be set up
+	public static function setupFS( $user = "", $root = "files" ){// configure the initial filesystem based on the configuration
+		if(self::$fsSetup){//setting up the filesystem twice can only lead to trouble
+			return false;
+		}
+
+		// Global Variables
+		global $SERVERROOT;
+		global $CONFIG_DATADIRECTORY;
+
+		$CONFIG_DATADIRECTORY_ROOT = OC_Config::getValue( "datadirectory", "$SERVERROOT/data" );
+		$CONFIG_BACKUPDIRECTORY = OC_Config::getValue( "backupdirectory", "$SERVERROOT/backup" );
+
+		// Create root dir
+		if(!is_dir($CONFIG_DATADIRECTORY_ROOT)){
+			$success=@mkdir($CONFIG_DATADIRECTORY_ROOT);
+                        if(!$success) {
+				$tmpl = new OC_Template( '', 'error', 'guest' );
+				$tmpl->assign('errors',array(1=>array('error'=>"Can't create data directory ($CONFIG_DATADIRECTORY_ROOT)",'hint'=>"You can usually fix this by setting the owner of '$SERVERROOT' to the user that the web server uses (".exec('whoami').")")));
+				$tmpl->printPage();
+				exit;
+  			}
+		}
+
+		// If we are not forced to load a specific user we load the one that is logged in
+		if( $user == "" && OC_User::isLoggedIn()){
+			$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_ROOT));
+// 			if( OC_Config::getValue( "enablebackup", false )){
+// 				// This creates the Directorys recursively
+// 				if(!is_dir( "$CONFIG_BACKUPDIRECTORY/$user/$root" )){
+// 					mkdir( "$CONFIG_BACKUPDIRECTORY/$user/$root", 0755, true );
+// 				}
+// 				$backupStorage=OC_Filesystem::createStorage('local',array('datadir'=>$CONFIG_BACKUPDIRECTORY));
+// 				$backup=new OC_FILEOBSERVER_BACKUP(array('storage'=>$backupStorage));
+// 				$rootStorage->addObserver($backup);
+// 			}
+			OC_Filesystem::mount($rootStorage,'/');
+
+			// TODO add this storage provider in a proper way
+			$sharedStorage = OC_Filesystem::createStorage('shared',array('datadir'=>'/'.OC_User::getUser().'/files/Shared'));
+			OC_Filesystem::mount($sharedStorage,'/'.OC_User::getUser().'/files/Shared/');
+
+			$CONFIG_DATADIRECTORY = "$CONFIG_DATADIRECTORY_ROOT/$user/$root";
+			if( !is_dir( $CONFIG_DATADIRECTORY )){
+				mkdir( $CONFIG_DATADIRECTORY, 0755, true );
+			}
+
+// TODO: find a cool way for doing this
+// 			//set up the other storages according to the system settings
+// 			foreach($CONFIG_FILESYSTEM as $storageConfig){
+// 				if(OC_Filesystem::hasStorageType($storageConfig['type'])){
+// 					$arguments=$storageConfig;
+// 					unset($arguments['type']);
+// 					unset($arguments['mountpoint']);
+// 					$storage=OC_Filesystem::createStorage($storageConfig['type'],$arguments);
+// 					if($storage){
+// 						OC_Filesystem::mount($storage,$storageConfig['mountpoint']);
+// 					}
+// 				}
+// 			}
+
+			//jail the user into his "home" directory
+			OC_Filesystem::chroot("/$user/$root");
+			$quotaProxy=new OC_FileProxy_Quota();
+			OC_FileProxy::register($quotaProxy);
+			self::$fsSetup=true;
+		}
+	}
+
+	public static function tearDownFS(){
+		OC_Filesystem::tearDown();
+		self::$fsSetup=false;
+	}
+
+	/**
+	 * get the current installed version of ownCloud
+	 * @return array
+	 */
+	public static function getVersion(){
+		return array(1,90,0);
+	}
+
+	/**
+	 * add a javascript file
+	 *
+	 * @param url  $url
+	 */
+	public static function addScript( $application, $file = null ){
+		if( is_null( $file )){
+			$file = $application;
+			$application = "";
+		}
+		if( !empty( $application )){
+			self::$scripts[] = "$application/js/$file";
+		}else{
+			self::$scripts[] = "js/$file";
+		}
+	}
+
+	/**
+	 * add a css file
+	 *
+	 * @param url  $url
+	 */
+	public static function addStyle( $application, $file = null ){
+		if( is_null( $file )){
+			$file = $application;
+			$application = "";
+		}
+		if( !empty( $application )){
+			self::$styles[] = "$application/css/$file";
+		}else{
+			self::$styles[] = "css/$file";
+		}
+	}
+
+	/**
+	 * @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
+         *
+         * @param int timestamp $timestamp
+         * @param bool dateOnly option to ommit time from the result
+         */
+        public static function formatDate( $timestamp,$dateOnly=false){
+			if(isset($_SESSION['timezone'])){//adjust to clients timezone if we know it
+				$systemTimeZone = intval(exec('date +%z'));
+				$systemTimeZone=(round($systemTimeZone/100,0)*60)+($systemTimeZone%100);
+				$clientTimeZone=$_SESSION['timezone']*60;
+				$offset=$clientTimeZone-$systemTimeZone;
+				$timestamp=$timestamp+$offset*60;
+			}
+			$timeformat=$dateOnly?'F j, Y':'F j, Y, H:i';
+			return date($timeformat,$timestamp);
+        }
+
+	/**
+	 * Shows a pagenavi widget where you can jump to different pages.
+	 *
+	 * @param int $pagecount
+	 * @param int $page
+	 * @param string $url
+	 * @return OC_Template
+	 */
+	public static function getPageNavi($pagecount,$page,$url) {
+
+		$pagelinkcount=8;
+		if ($pagecount>1) {
+			$pagestart=$page-$pagelinkcount;
+			if($pagestart<0) $pagestart=0;
+			$pagestop=$page+$pagelinkcount;
+			if($pagestop>$pagecount) $pagestop=$pagecount;
+
+			$tmpl = new OC_Template( '', 'part.pagenavi', '' );
+			$tmpl->assign('page',$page);
+			$tmpl->assign('pagecount',$pagecount);
+			$tmpl->assign('pagestart',$pagestart);
+			$tmpl->assign('pagestop',$pagestop);
+			$tmpl->assign('url',$url);
+			return $tmpl;
+		}
+	}
+
+
+
+	/**
+	 * check if the current server configuration is suitable for ownCloud
+	 * @return array arrays with error messages and hints
+	 */
+	public static function checkServer(){
+		global $SERVERROOT;
+		global $CONFIG_DATADIRECTORY;
+
+		$CONFIG_DATADIRECTORY_ROOT = OC_Config::getValue( "datadirectory", "$SERVERROOT/data" );
+		$CONFIG_BACKUPDIRECTORY = OC_Config::getValue( "backupdirectory", "$SERVERROOT/backup" );
+		$CONFIG_INSTALLED = OC_Config::getValue( "installed", false );
+		$errors=array();
+
+		//check for database drivers
+		if(!is_callable('sqlite_open') and !is_callable('mysql_connect')){
+			$errors[]=array('error'=>'No database drivers (sqlite or mysql) installed.<br/>','hint'=>'');//TODO: sane hint
+		}
+		$CONFIG_DBTYPE = OC_Config::getValue( "dbtype", "sqlite" );
+		$CONFIG_DBNAME = OC_Config::getValue( "dbname", "owncloud" );
+
+		//try to get the username the httpd server runs on, used in hints
+		$stat=stat($_SERVER['DOCUMENT_ROOT']);
+		if(is_callable('posix_getpwuid')){
+			$serverUser=posix_getpwuid($stat['uid']);
+			$serverUser='\''.$serverUser['name'].'\'';
+		}else{
+			$serverUser='\'www-data\' for ubuntu/debian';//TODO: try to detect the distro and give a guess based on that
+		}
+
+		//common hint for all file permissons error messages
+		$permissionsHint="Permissions can usually be fixed by setting the owner of the file or directory to the user the web server runs as ($serverUser)";
+
+		//check for correct file permissions
+		if(!stristr(PHP_OS, 'WIN')){
+			$prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY_ROOT)),-3);
+			if(substr($prems,-1)!='0'){
+				OC_Helper::chmodr($CONFIG_DATADIRECTORY_ROOT,0770);
+				clearstatcache();
+				$prems=substr(decoct(@fileperms($CONFIG_DATADIRECTORY_ROOT)),-3);
+				if(substr($prems,2,1)!='0'){
+					$errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY_ROOT.') is readable from the web<br/>','hint'=>$permissionsHint);
+				}
+			}
+			if( OC_Config::getValue( "enablebackup", false )){
+				$prems=substr(decoct(@fileperms($CONFIG_BACKUPDIRECTORY)),-3);
+				if(substr($prems,-1)!='0'){
+					OC_Helper::chmodr($CONFIG_BACKUPDIRECTORY,0770);
+					clearstatcache();
+					$prems=substr(decoct(@fileperms($CONFIG_BACKUPDIRECTORY)),-3);
+					if(substr($prems,2,1)!='0'){
+						$errors[]=array('error'=>'Data directory ('.$CONFIG_BACKUPDIRECTORY.') is readable from the web<br/>','hint'=>$permissionsHint);
+					}
+				}
+			}
+		}else{
+			//TODO: premisions checks for windows hosts
+		}
+		if(is_dir($CONFIG_DATADIRECTORY_ROOT) and !is_writable($CONFIG_DATADIRECTORY_ROOT)){
+			$errors[]=array('error'=>'Data directory ('.$CONFIG_DATADIRECTORY_ROOT.') not writable by ownCloud<br/>','hint'=>$permissionsHint);
+		}
+
+		//TODO: check for php modules
+
+		return $errors;
+	}
+}
diff --git a/ocs/providers.php b/ocs/providers.php
new file mode 100644
index 0000000000000000000000000000000000000000..68cece3277a0118b7e4480a6de1c8258ddafc50f
--- /dev/null
+++ b/ocs/providers.php
@@ -0,0 +1,45 @@
+<?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('../inc/lib_base.php');
+
+$url='http://'.substr($_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'],0,-17).'ocs/v1.php/';
+
+echo('
+<providers>
+<provider>
+ <id>ownCloud</id>
+ <location>'.$url.'</location>
+ <name>ownCloud</name>
+ <icon></icon>
+ <termsofuse></termsofuse>
+ <register></register>
+ <services>
+   <activity ocsversion="1.5" />
+ </services>
+</provider>
+</providers>
+');
+
+
+?>
diff --git a/ocs/v1.php b/ocs/v1.php
new file mode 100644
index 0000000000000000000000000000000000000000..1b8b479f4550aa5c0b2fc928cc438d81207ed6d8
--- /dev/null
+++ b/ocs/v1.php
@@ -0,0 +1,28 @@
+<?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('../inc/lib_base.php');
+ob_clean();
+OC_OCS::handle();
+
+?>
diff --git a/search/ajax/search.php b/search/ajax/search.php
new file mode 100644
index 0000000000000000000000000000000000000000..c65fbbc63fa9ba456cb6dc10012f48fc0e3b9f8e
--- /dev/null
+++ b/search/ajax/search.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+* ownCloud
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+$query=(isset($_GET['query']))?$_GET['query']:'';
+if($query){
+	$result=OC_Search::search($query);
+	echo json_encode($result);
+}else{
+	echo 'false';
+}
+
+?>
\ No newline at end of file
diff --git a/search/appinfo/app.php b/search/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..b91341643f25dd9a59c26d45fe58910c6b6ce403
--- /dev/null
+++ b/search/appinfo/app.php
@@ -0,0 +1,5 @@
+<?php
+
+OC_App::register( array( 'order' => 2, "id" => 'search', 'name' => 'Search' ));
+
+?>
diff --git a/search/css/results.css b/search/css/results.css
new file mode 100644
index 0000000000000000000000000000000000000000..440e68ee48f74e02bdb5c62c73a9a5275dabd30f
--- /dev/null
+++ b/search/css/results.css
@@ -0,0 +1,9 @@
+#searchresults { list-style:none; position:fixed; top:3.5em; right:0; z-index:75; background-color:#fff; overflow:hidden; text-overflow:ellipsis; max-height:80%; width:26.5em; padding-bottom:1em; -moz-box-shadow:0 0 10px #000; -webkit-box-shadow:0 0 10px #000; box-shadow:0 0 10px #000; -moz-border-radius-bottomleft:1em; -webkit-border-bottom-left-radius:1em; border-bottom-left-radius:1em; }
+#searchresults li.resultHeader { font-size:1.2em; font-weight:bold; border-bottom:solid 1px #CCC; padding:.2em; background-color:#eee; }
+#searchresults li.result { margin-left:2em; }
+#searchresults table { width:100%; table-layout:fixed; top:0; border-spacing:0; }
+#searchresults td { padding:0 .3em; vertical-align:top; }
+#searchresults td.result div.text { padding-left:1em; white-space:nowrap; }
+#searchresults td.result * { cursor:pointer; }
+#searchresults td.type { width:3.5em; text-align:right; border-right:1px solid #aaa; border-bottom:none; font-weight:bold; }
+#searchresults tr.current { background-color:#ddd; }
diff --git a/search/index.php b/search/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..7369a6d81ce5aede7e2483e4f9290feb457158b9
--- /dev/null
+++ b/search/index.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+* ownCloud - ajax frontend
+*
+* @author Robin Appelman
+* @copyright 2010 Robin Appelman icewind1991@gmail.com
+*
+* 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/>.
+*
+*/
+
+
+// Init owncloud
+require_once('../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	header( "Location: ".OC_Helper::linkTo( '', 'index.php' ));
+	exit();
+}
+
+// Load the files we need
+OC_Util::addStyle( 'search', 'search' );
+
+$query=(isset($_POST['query']))?$_POST['query']:'';
+if($query){
+	$results=OC_Search::search($query);
+}else{
+	header("Location: ".$WEBROOT.'/'.OC_Appconfig::getValue("core", "defaultpage", "files/index.php"));
+	exit();
+}
+
+$resultTypes=array();
+foreach($results as $result){
+	if(!isset($resultTypes[$result->type])){
+		$resultTypes[$result->type]=array();
+	}
+	$resultTypes[$result->type][]=$result;
+}
+
+$tmpl = new OC_Template( 'search', 'index', 'user' );
+$tmpl->assign('resultTypes',$resultTypes);
+$tmpl->printPage();
+
+?>
diff --git a/search/js/result.js b/search/js/result.js
new file mode 100644
index 0000000000000000000000000000000000000000..1087f9684b29663cb91973ad28a19a701cbdacdc
--- /dev/null
+++ b/search/js/result.js
@@ -0,0 +1,83 @@
+OC.search.catagorizeResults=function(results){
+	var types={};
+	for(var i=0;i<results.length;i++){
+		var type=results[i].type;
+		if(!types[type]){
+			types[type]=[];
+		}
+		types[type].push(results[i]);
+	}
+	return types;
+}
+OC.search.hide=function(){
+	$('#searchresults').hide();
+	if($('#searchbox').val().length>2){
+		$('#searchbox').val('');
+	};
+}
+OC.search.showResults=function(results){
+	if(results.length==0){
+		return;
+	}
+	if(!OC.search.showResults.loaded){
+		var parent=$('<div/>');
+		$('body').append(parent);
+		parent.load(OC.filePath('search','templates','part.results.php'),function(){
+			OC.search.showResults.loaded=true;
+			$('#searchresults').click(function(event){
+				event.stopPropagation();
+			});
+			$(window).click(function(event){
+				OC.search.hide();
+			});
+			OC.search.lastResults=results;
+			OC.search.showResults(results);
+		});
+	}else{
+		var types=OC.search.catagorizeResults(results);
+		$('#searchresults').show();
+		$('#searchresults tr.result').remove();
+		var index=0;
+		for(var name in types){
+			var type=types[name];
+			if(type.length>0){
+				var row=$('#searchresults tr.template').clone();
+				row.removeClass('template');
+				row.addClass('result');
+				row.children('td.type').text(name);
+				row.find('td.result a').attr('href',type[0].link);
+				row.find('td.result div.name').text(type[0].name);
+				row.find('td.result div.text').text(type[0].text);
+				row.data('index',index);
+				index++;
+				if(OC.search.customResults[name]){//give plugins the ability to customize the entries in here
+					OC.search.customResults[name](row,type[0]);
+				}
+				$('#searchresults tbody').append(row);
+				for(var i=1;i<type.length;i++){
+					var row=$('#searchresults tr.template').clone();
+					row.removeClass('template');
+					row.addClass('result');
+					row.find('td.result a').attr('href',type[i].link);
+					row.find('td.result div.name').text(type[i].name);
+					row.find('td.result div.text').text(type[i].text);
+					row.data('index',index);
+					index++;
+					if(OC.search.customResults[name]){//give plugins the ability to customize the entries in here
+						OC.search.customResults[name](row,type[i]);
+					}
+					$('#searchresults tbody').append(row);
+				}
+			}
+		}
+	}
+}
+OC.search.showResults.loaded=false;
+
+OC.search.renderCurrent=function(){
+	if($('#searchresults tr.result')[OC.search.currentResult]){
+		var result=$('#searchresults tr.result')[OC.search.currentResult];
+		$('#searchresults tr.result').removeClass('current');
+		$(result).addClass('current');
+	}
+}
diff --git a/search/templates/index.php b/search/templates/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..7241553e7fc085e166fcdce4eb01a88fbb5463b5
--- /dev/null
+++ b/search/templates/index.php
@@ -0,0 +1,17 @@
+<ul id='searchresults'>
+	<?php foreach($_['resultTypes'] as $resultType):?>
+		<li class='resultHeader'>
+			<p><?php echo $resultType[0]->type?></p>
+		</li>
+		<?php foreach($resultType as $result):?>
+			<li class='result'>
+				<p>
+					<a href='<?php echo $result->link?>' title='<?php echo $result->name?>'><?php echo $result->name?></a>
+				</p>
+				<p>
+					<?php echo $result->text?>
+				</p>
+			</li>
+		<?php endforeach;?>
+	<?php endforeach;?>
+</ul>
diff --git a/search/templates/part.results.php b/search/templates/part.results.php
new file mode 100644
index 0000000000000000000000000000000000000000..9e39a1c2c8ba07dd5f44c61fb89411e246607557
--- /dev/null
+++ b/search/templates/part.results.php
@@ -0,0 +1,15 @@
+<div id='searchresults'>
+	<table>
+		<tbody>
+			<tr class='template '>
+				<td class='type'></td>
+				<td class='result'>
+					<a>
+						<div class='name'></div>
+						<div class='text'></div>
+					</a>
+				</td>
+			</tr>
+		</tbody>
+	</table>
+</div>
diff --git a/settings/admin.php b/settings/admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..15559a150e5ea80077053ec600891b4cbbb4bdfa
--- /dev/null
+++ b/settings/admin.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once('../lib/base.php');
+if( !OC_User::isLoggedIn()){
+    header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+    exit();
+}
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	header( "Location: ".OC_Helper::linkTo( '', "index.php" ));
+	exit();
+}
+
+OC_Util::addStyle( "settings", "settings" );
+OC_App::setActiveNavigationEntry( "admin" );
+
+$tmpl = new OC_Template( 'settings', 'admin', 'user');
+$forms=OC_App::getForms('admin');
+$tmpl->assign('forms',array());
+foreach($forms as $form){
+	$tmpl->append('forms',$form);
+}
+$tmpl->printPage();
\ No newline at end of file
diff --git a/settings/ajax/changepassword.php b/settings/ajax/changepassword.php
new file mode 100644
index 0000000000000000000000000000000000000000..c5cdbcef05610bb4a32559284d01c6eb116824b2
--- /dev/null
+++ b/settings/ajax/changepassword.php
@@ -0,0 +1,26 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+$username = isset($_POST["username"]) ? $_POST["username"] : OC_User::getUser();
+$password = $_POST["password"];
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || (!OC_Group::inGroup( OC_User::getUser(), 'admin' )&& $username!=OC_User::getUser())) {
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+// Return Success story
+if( OC_User::setPassword( $username, $password )){
+	echo json_encode( array( "status" => "success", "data" => array( "username" => $username )));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to change password" )));
+}
+
+?>
diff --git a/settings/ajax/creategroup.php b/settings/ajax/creategroup.php
new file mode 100644
index 0000000000000000000000000000000000000000..2631937b14dbec9c86c6876435d6f08eee581bc8
--- /dev/null
+++ b/settings/ajax/creategroup.php
@@ -0,0 +1,31 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$groupname = $_POST["groupname"];
+
+// Does the group exist?
+if( in_array( $groupname, OC_Group::getGroups())){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Group already exists" )));
+	exit();
+}
+
+// Return Success story
+if( OC_Group::createGroup( $groupname )){
+	echo json_encode( array( "status" => "success", "data" => array( "groupname" => $groupname )));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to add group" )));
+}
+
+?>
diff --git a/settings/ajax/createuser.php b/settings/ajax/createuser.php
new file mode 100644
index 0000000000000000000000000000000000000000..de52f90d4f3c730d321c322935406a42205d82b8
--- /dev/null
+++ b/settings/ajax/createuser.php
@@ -0,0 +1,42 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$groups = array();
+if( isset( $_POST["groups"] )){
+	$groups = $_POST["groups"];
+}
+$username = $_POST["username"];
+$password = $_POST["password"];
+
+// Does the group exist?
+if( in_array( $username, OC_User::getUsers())){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "User already exists" )));
+	exit();
+}
+
+// Return Success story
+if( OC_User::createUser( $username, $password )){
+	foreach( $groups as $i ){
+		if(!OC_Group::groupExists($i)){
+			OC_Group::createGroup($i);
+		}
+		OC_Group::addToGroup( $username, $i );
+	}
+	echo json_encode( array( "status" => "success", "data" => array( "username" => $username, "groups" => implode( ", ", OC_Group::getUserGroups( $username )))));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to add user" )));
+}
+
+?>
diff --git a/settings/ajax/disableapp.php b/settings/ajax/disableapp.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cf66a553f8b715a4a59fd727be195a77ae79b13
--- /dev/null
+++ b/settings/ajax/disableapp.php
@@ -0,0 +1,8 @@
+<?php
+// Init owncloud
+require_once('../../lib/base.php');
+header( "Content-Type: application/jsonrequest" );
+
+OC_App::disable($_POST['appid']);
+
+?>
diff --git a/settings/ajax/enableapp.php b/settings/ajax/enableapp.php
new file mode 100644
index 0000000000000000000000000000000000000000..eb1bfc54a0449c9f40e17ac7f18d32afb91d2a1e
--- /dev/null
+++ b/settings/ajax/enableapp.php
@@ -0,0 +1,9 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+header( "Content-Type: application/jsonrequest" );
+
+OC_App::enable($_POST['appid']);
+
+?>
diff --git a/settings/ajax/openid.php b/settings/ajax/openid.php
new file mode 100644
index 0000000000000000000000000000000000000000..021fe35d8ec23e24637cc020a1d1808365af62d8
--- /dev/null
+++ b/settings/ajax/openid.php
@@ -0,0 +1,26 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+$l=new OC_L10N('settings');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Authentication error") )));
+	exit();
+}
+
+// Get data
+if( isset( $_POST['identity'] ) ){
+	$identity=$_POST['identity'];
+	OC_Preferences::setValue(OC_User::getUser(),'user_openid','identity',$identity);
+	echo json_encode( array( "status" => "success", "data" => array( "message" => $l->t("OpenID Changed") )));
+}else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Invalid request") )));
+}
+
+?>
diff --git a/settings/ajax/removegroup.php b/settings/ajax/removegroup.php
new file mode 100644
index 0000000000000000000000000000000000000000..bf80da741c702d4a3a4f0e69dc91aa564561e356
--- /dev/null
+++ b/settings/ajax/removegroup.php
@@ -0,0 +1,25 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$name = $_POST["groupname"];
+
+// Return Success story
+if( OC_Group::deleteGroup( $name )){
+	echo json_encode( array( "status" => "success", "data" => array( "groupname" => $name )));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to delete group" )));
+}
+
+?>
diff --git a/settings/ajax/removeuser.php b/settings/ajax/removeuser.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a94884cb96f4b192c59c64fd94cea128dd4cf82
--- /dev/null
+++ b/settings/ajax/removeuser.php
@@ -0,0 +1,25 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$username = $_POST["username"];
+
+// Return Success story
+if( OC_User::deleteUser( $username )){
+	echo json_encode( array( "status" => "success", "data" => array( "username" => $username )));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to delete user" )));
+}
+
+?>
diff --git a/settings/ajax/setlanguage.php b/settings/ajax/setlanguage.php
new file mode 100644
index 0000000000000000000000000000000000000000..a5ba3d81ba37a9086cfba8f59b8e75002ffd429e
--- /dev/null
+++ b/settings/ajax/setlanguage.php
@@ -0,0 +1,26 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+$l=new OC_L10N('settings');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Authentication error") )));
+	exit();
+}
+
+// Get data
+if( isset( $_POST['lang'] ) ){
+	$lang=$_POST['lang'];
+	OC_Preferences::setValue( OC_User::getUser(), 'core', 'lang', $lang );
+	echo json_encode( array( "status" => "success", "data" => array( "message" => $l->t("Language changed") )));
+}else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => $l->t("Invalid request") )));
+}
+
+?>
diff --git a/settings/ajax/setquota.php b/settings/ajax/setquota.php
new file mode 100644
index 0000000000000000000000000000000000000000..244a85e3d9c39187720962865b29ce67f00109a3
--- /dev/null
+++ b/settings/ajax/setquota.php
@@ -0,0 +1,22 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$username = $_POST["username"];
+$quota= OC_Helper::computerFileSize($_POST["quota"]);
+
+// Return Success story
+OC_Preferences::setValue($username,'files','quota',$quota);
+echo json_encode( array( "status" => "success", "data" => array( "username" => $username ,'quota'=>$quota)));
+
+?>
diff --git a/settings/ajax/togglegroups.php b/settings/ajax/togglegroups.php
new file mode 100644
index 0000000000000000000000000000000000000000..3210252af02878839dd0725ccb4660945d1c5f3d
--- /dev/null
+++ b/settings/ajax/togglegroups.php
@@ -0,0 +1,48 @@
+<?php
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// We send json data
+header( "Content-Type: application/jsonrequest" );
+
+// Check if we are a user
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Authentication error" )));
+	exit();
+}
+
+$success = true;
+$error = "add user to";
+$action = "add";
+
+$username = $_POST["username"];
+$group = $_POST["group"];
+
+if(!OC_Group::groupExists($group)){
+	OC_Group::createGroup($group);
+}
+
+// Toggle group
+if( OC_Group::inGroup( $username, $group )){
+	$action = "remove";
+	$error = "remove user from";
+	$success = OC_Group::removeFromGroup( $username, $group );
+	$usersInGroup=OC_Group::usersInGroup($group);
+	if(count($usersInGroup)==0){
+		OC_Group::deleteGroup($group);
+	}
+}
+else{
+	$success = OC_Group::addToGroup( $username, $group );
+}
+
+// Return Success story
+if( $success ){
+	echo json_encode( array( "status" => "success", "data" => array( "username" => $username, "action" => $action, "groupname" => $group )));
+}
+else{
+	echo json_encode( array( "status" => "error", "data" => array( "message" => "Unable to $error group $group" )));
+}
+
+?>
diff --git a/settings/appinfo/app.php b/settings/appinfo/app.php
new file mode 100644
index 0000000000000000000000000000000000000000..d18bcdbff0dd1e6082645328456906af5a552393
--- /dev/null
+++ b/settings/appinfo/app.php
@@ -0,0 +1,7 @@
+<?php
+
+OC_App::register( array( "id" => "settings", "name" => "Settings" ));
+OC_App::register( array( "order" => 1, "id" => "admin", "name" => "Administration" ));
+OC_App::register( array( "order" => 1, "id" => "help", "name" => "Help" ));
+
+?>
diff --git a/settings/apps.php b/settings/apps.php
new file mode 100644
index 0000000000000000000000000000000000000000..337a52fd41630932cc30fff8361c6cf72a3292f2
--- /dev/null
+++ b/settings/apps.php
@@ -0,0 +1,72 @@
+<?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('../lib/base.php');
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+	exit();
+}
+
+// Load the files we need
+OC_Util::addStyle( "settings", "settings" );
+OC_Util::addScript( "settings", "apps" );
+OC_App::setActiveNavigationEntry( "core_apps" );
+
+$registeredApps=OC_App::getAllApps();
+$apps=array();
+
+$blacklist=array('files_imageviewer','files_textviewer');//we dont want to show configuration for these
+
+foreach($registeredApps as $app){
+	if(array_search($app,$blacklist)===false){
+		$info=OC_App::getAppInfo($app);
+		$active=(OC_Appconfig::getValue($app,'enabled','no')=='yes')?true:false;
+		$info['active']=$active;
+		$apps[]=$info;
+	}
+}
+
+$catagoryNames=OC_OCSClient::getCategories();
+if(is_array($catagoryNames)){
+	$categories=array_keys($catagoryNames);
+	$externalApps=OC_OCSClient::getApplications($categories);
+	foreach($externalApps as $app){
+		$apps[]=array(
+			'name'=>$app['name'],
+			'id'=>$app['id'],
+			'active'=>false,
+			'description'=>$app['description'],
+			'author'=>$app['personid'],
+			'license'=>$app['license'],
+		);
+	}
+}
+
+
+
+$tmpl = new OC_Template( "settings", "apps", "user" );
+$tmpl->assign('apps',$apps);
+
+$tmpl->printPage();
+
+?>
diff --git a/settings/css/settings.css b/settings/css/settings.css
new file mode 100644
index 0000000000000000000000000000000000000000..429e96ddb9a0f9177fb8e6c4e2e767d794f67783
--- /dev/null
+++ b/settings/css/settings.css
@@ -0,0 +1,28 @@
+input#openid, input#webdav { width:20em; }
+
+/* PERSONAL */
+#passworderror { display:none; }
+#passwordchanged { display:none; }
+input#identity { width:20em; }
+
+
+/* USERS */
+form { display:inline; }
+td.name, td.password { padding-left:.8em; }
+td.password>img, td.remove>img { display:none; cursor:pointer; }
+td.password>span { margin-right:1.2em; }
+td.password { width:12em; cursor:pointer; }
+
+td.remove { width:1em; padding-right:1em; }
+tr:hover>td.password>span { margin:0; cursor:pointer; }
+tr:hover>td.remove>img, tr:hover>td.password>img { display:inline;  cursor:pointer; }
+tr:hover>td.remove>img { float:right; }
+li.selected { background-color:#ddd; }
+#content>table { margin-top:6.5em; }
+table { width:100%; }
+
+
+/* APPS */
+li { color:#888; }
+li.active { color:#000; }
+span.version { margin-left:3em; color:#ddd; }
diff --git a/settings/help.php b/settings/help.php
new file mode 100644
index 0000000000000000000000000000000000000000..f8a2f9b8bf959d2eea9183dc1efcc8b420f080cc
--- /dev/null
+++ b/settings/help.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Copyright (c) 2011, Frank Karlitschek karlitschek@kde.org
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once('../lib/base.php');
+if( !OC_User::isLoggedIn()){
+    header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+    exit();
+}
+
+
+// Load the files we need
+OC_Util::addStyle( "settings", "settings" );
+OC_App::setActiveNavigationEntry( "help" );
+
+$pagesize=7;
+if(isset($_GET['page'])) $page=$_GET['page']; else $page=0;
+$kbe=OC_OCSClient::getKnownledgebaseEntries($page,$pagesize);
+$totalitems=$kbe['totalitems'];
+unset($kbe['totalitems']);
+$pagecount=ceil($totalitems/$pagesize);
+
+$tmpl = new OC_Template( "settings", "help", "user" );
+$tmpl->assign( "kbe", $kbe );
+$tmpl->assign( "pagecount", $pagecount );
+$tmpl->assign( "page", $page );
+$tmpl->printPage();
+
+?>
diff --git a/settings/img/admin.png b/settings/img/admin.png
new file mode 100644
index 0000000000000000000000000000000000000000..c1e6d6b8a7fe53c1f217667ec32498bb6f3e3da0
Binary files /dev/null and b/settings/img/admin.png differ
diff --git a/settings/img/admin.svg b/settings/img/admin.svg
new file mode 100644
index 0000000000000000000000000000000000000000..b3c4a32451dcb2f7d9bab06328e98d7f86a1c674
--- /dev/null
+++ b/settings/img/admin.svg
@@ -0,0 +1,2214 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="apps.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/apps.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="32.000001"
+     inkscape:cx="8.0537858"
+     inkscape:cy="6.4773881"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-4"
+       id="linearGradient4357-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       id="linearGradient3587-6-5-4">
+      <stop
+         id="stop3589-9-2-1"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-04"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-26"
+       id="linearGradient4566-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       id="linearGradient3587-6-5-26">
+      <stop
+         id="stop3589-9-2-45"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-20"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       id="linearGradient4580-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6-3" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-2-1">
+      <stop
+         id="stop3589-9-2-8-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="1013.451"
+       x2="209.34245"
+       y1="998.45801"
+       x1="209.34245"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3528"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-4"
+       id="linearGradient3335-2"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-4">
+      <stop
+         id="stop3589-9-2-8-7-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-6"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-8-3">
+      <stop
+         id="stop3589-9-2-67-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3584">
+      <stop
+         id="stop3586"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3588"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3591">
+      <stop
+         id="stop3593"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3595"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3598">
+      <stop
+         id="stop3600"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3602"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-8-3-9">
+      <stop
+         id="stop3589-9-2-67-4-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-9-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4682">
+      <stop
+         id="stop4684"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4686"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4689">
+      <stop
+         id="stop4691"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4693"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3-9"
+       id="linearGradient4665-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4696">
+      <stop
+         id="stop4698"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4700"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4823"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4825"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4827"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,6.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4829"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1-5"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86-0">
+      <stop
+         id="stop3589-9-2-65-9"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427-9"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3630">
+      <stop
+         id="stop3632"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3634"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436-6"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3637">
+      <stop
+         id="stop3639"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3641"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442-8"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3644">
+      <stop
+         id="stop3646"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3648"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       id="linearGradient4907"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)"
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       id="linearGradient4911"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)"
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       id="linearGradient4915"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)"
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       id="linearGradient4919"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)"
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0-6"
+       id="linearGradient4911-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)"
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17" />
+    <linearGradient
+       id="linearGradient3587-6-5-86-0-6">
+      <stop
+         id="stop3589-9-2-65-9-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17"
+       x2="11"
+       y1="6"
+       x1="11"
+       gradientTransform="matrix(0.15503877,0,0,0.11235953,3.7984496,11.52809)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4936"
+       xlink:href="#linearGradient3587-6-5-86-0-6"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0"
+       id="linearGradient5057"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)"
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332" />
+    <linearGradient
+       id="linearGradient3587-6-5-86-0-9">
+      <stop
+         id="stop3589-9-2-65-9-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5080">
+      <stop
+         id="stop5082"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop5084"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient5087">
+      <stop
+         id="stop5089"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop5091"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-86-0-9"
+       id="linearGradient4907-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)"
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17" />
+    <linearGradient
+       id="linearGradient5094">
+      <stop
+         id="stop5096"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop5098"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       transform="translate(0,1)"
+       id="g5059-3"
+       style="fill:#ffffff;fill-opacity:1;opacity:0.6">
+      <path
+         inkscape:connector-curvature="0"
+         id="path2407-9-6"
+         d="m 6,4 0,1 8,0 0,-1 -8,0 z m 0,4 0,1 8,0 0,-1 -8,0 z m 0,4 0,1 8,0 0,-1 -8,0 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.69999999999999996;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.99999994000000003;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+      <rect
+         style="opacity:0.69999999999999996;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="rect3728-0-5"
+         y="3"
+         x="2"
+         height="3"
+         width="3" />
+      <path
+         inkscape:connector-curvature="0"
+         id="rect2434-2"
+         d="m 2,11 0,3 3,0 0,-3 -3,0 z m 1,1 1,0 0,1 -1,0 0,-1 z"
+         style="opacity:0.69999999999999996;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <rect
+         style="opacity:0.69999999999999996;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="rect2440-8"
+         y="7"
+         x="2"
+         height="3"
+         width="3" />
+    </g>
+    <g
+       id="g5059">
+      <path
+         inkscape:connector-curvature="0"
+         id="path2407-9"
+         d="m 6,4 0,1 8,0 0,-1 -8,0 z m 0,4 0,1 8,0 0,-1 -8,0 z m 0,4 0,1 8,0 0,-1 -8,0 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient5057);fill-opacity:1;stroke:none;stroke-width:0.99999994;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+      <rect
+         style="opacity:0.7;fill:url(#linearGradient4915);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="rect3728-0"
+         y="3"
+         x="2"
+         height="3"
+         width="3" />
+      <path
+         id="rect2434"
+         d="M 2 11 L 2 14 L 5 14 L 5 11 L 2 11 z M 3 12 L 4 12 L 4 13 L 3 13 L 3 12 z "
+         style="opacity:0.69999999999999996;fill:url(#linearGradient4911);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+      <rect
+         style="opacity:0.7;fill:url(#linearGradient4907);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         id="rect2440"
+         y="7"
+         x="2"
+         height="3"
+         width="3" />
+    </g>
+  </g>
+</svg>
diff --git a/settings/img/apps.png b/settings/img/apps.png
new file mode 100644
index 0000000000000000000000000000000000000000..17f47d632b9fa9726f393fc6a4504b69a493d70f
Binary files /dev/null and b/settings/img/apps.png differ
diff --git a/settings/img/apps.svg b/settings/img/apps.svg
new file mode 100644
index 0000000000000000000000000000000000000000..cda246bc4a872d6167ad001ffc1f5ceebfd33735
--- /dev/null
+++ b/settings/img/apps.svg
@@ -0,0 +1,2164 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="file.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/file.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="16.000001"
+     inkscape:cx="-1.1375545"
+     inkscape:cy="5.0070539"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-4"
+       id="linearGradient4357-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       id="linearGradient3587-6-5-4">
+      <stop
+         id="stop3589-9-2-1"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-04"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-26"
+       id="linearGradient4566-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       id="linearGradient3587-6-5-26">
+      <stop
+         id="stop3589-9-2-45"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-20"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       id="linearGradient4580-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6-3" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-2-1">
+      <stop
+         id="stop3589-9-2-8-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="1013.451"
+       x2="209.34245"
+       y1="998.45801"
+       x1="209.34245"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3528"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-4"
+       id="linearGradient3335-2"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-4">
+      <stop
+         id="stop3589-9-2-8-7-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-6"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3567"
+       xlink:href="#linearGradient3587-6-5-2-4-4"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient5021-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-3">
+      <stop
+         id="stop3589-9-2-67-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient5018-2"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3584">
+      <stop
+         id="stop3586"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3588"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient5015-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3591">
+      <stop
+         id="stop3593"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3595"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient5012-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3598">
+      <stop
+         id="stop3600"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3602"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4638"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4640"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4642"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4644"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4656"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,6.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4659"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4661"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4663"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,6.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4665"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-3-9">
+      <stop
+         id="stop3589-9-2-67-4-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-9-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3-9"
+       id="linearGradient4661-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4682">
+      <stop
+         id="stop4684"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4686"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4689">
+      <stop
+         id="stop4691"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4693"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3-9"
+       id="linearGradient4665-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4696">
+      <stop
+         id="stop4698"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4700"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="19.490837"
+       x2="26.045763"
+       y1="9.6223383"
+       x1="26.045763"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4707"
+       xlink:href="#linearGradient3587-6-5-8-3-9"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4823"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4825"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4827"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,6.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-3"
+       id="linearGradient4829"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       id="g4811"
+       transform="translate(0,1)">
+      <g
+         style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:none"
+         id="g5023-6">
+        <path
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           d="m 10,3 0,0.5 0,3 0,0.5 0.5,0 3,0 0.5,0 0,-0.5 0,-3 0,-0.5 -0.5,0 -3,0 z"
+           id="rect3187-0"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="ccccccccccccc" />
+        <path
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           d="M 2,3 2,3.5 2,6.5 2,7 2.5,7 5.5,7 6,7 6,6.5 6,3.5 6,3 5.5,3 2.5,3 z"
+           id="rect3201-0"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="ccccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           id="rect3205-6"
+           transform="translate(0,1)"
+           d="m 10,9 0,0.5 0,3 0,0.5 0.5,0 3,0 0.5,0 0,-0.5 0,-3 0,-0.5 -0.5,0 -3,0 -0.5,0 z m 1,1 2,0 0,2 -2,0 0,-2 z"
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+        <path
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           d="m 2,10 0,0.5 0,3 0,0.5 0.5,0 3,0 0.5,0 0,-0.5 0,-3 0,-0.5 -0.5,0 -3,0 z"
+           id="rect3209-4"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="ccccccccccccc" />
+      </g>
+      <g
+         transform="translate(0,-1)"
+         id="g5023">
+        <path
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient4823);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           d="m 10,3 0,0.5 0,3 0,0.5 0.5,0 3,0 0.5,0 0,-0.5 0,-3 0,-0.5 -0.5,0 -3,0 z"
+           id="rect3187"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="ccccccccccccc" />
+        <path
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient4825);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           d="M 2,3 2,3.5 2,6.5 2,7 2.5,7 5.5,7 6,7 6,6.5 6,3.5 6,3 5.5,3 2.5,3 z"
+           id="rect3201"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="ccccccccccccc" />
+        <path
+           inkscape:connector-curvature="0"
+           id="rect3205"
+           transform="translate(0,1)"
+           d="m 10,9 0,0.5 0,3 0,0.5 0.5,0 3,0 0.5,0 0,-0.5 0,-3 0,-0.5 -0.5,0 -3,0 -0.5,0 z m 1,1 2,0 0,2 -2,0 0,-2 z"
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient4827);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans" />
+        <path
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient4829);fill-opacity:1;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           d="m 2,10 0,0.5 0,3 0,0.5 0.5,0 3,0 0.5,0 0,-0.5 0,-3 0,-0.5 -0.5,0 -3,0 z"
+           id="rect3209"
+           inkscape:connector-curvature="0"
+           sodipodi:nodetypes="ccccccccccccc" />
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/settings/img/help.png b/settings/img/help.png
new file mode 100644
index 0000000000000000000000000000000000000000..2257d144d11ad33a429bead6bc1fde7222e66aae
Binary files /dev/null and b/settings/img/help.png differ
diff --git a/settings/img/help.svg b/settings/img/help.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1e07aed85271c6ae91ce3fa360127ff1e0404f23
--- /dev/null
+++ b/settings/img/help.svg
@@ -0,0 +1,1758 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="users.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/users.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="20.074369"
+       x2="14.152531"
+       y1="-1.4095211"
+       x1="14.501121"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3437"
+       xlink:href="#linearGradient3587-6-5-19"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-4"
+       id="linearGradient4357-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       id="linearGradient3587-6-5-4">
+      <stop
+         id="stop3589-9-2-1"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-04"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="28.776533"
+       x2="0.44923753"
+       y1="13.895414"
+       x1="0.86849999"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3456"
+       xlink:href="#linearGradient3587-6-5-4"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       id="g4314"
+       transform="matrix(1.0000288,0,0,1,-0.14973031,7.5119235e-7)">
+      <path
+         d="M 5.149582,8.4745445 C 5.3049331,8.8555559 5.4749951,9.1626038 5.7940692,8.7203676 6.2006678,8.4518873 7.5528003,7.2925123 7.4556978,8.3783304 7.0875579,10.395217 6.6215241,12.395026 6.2845924,14.416813 5.892954,15.532242 6.9195772,16.485144 7.9224991,15.729405 9.0003636,15.226276 9.9139828,14.440939 10.850418,13.716521 10.706075,13.39458 10.599944,12.928009 10.253582,13.370754 9.7853152,13.60987 8.7844663,14.688222 8.5572925,13.841548 8.8726653,11.661003 9.5328233,9.5467073 9.9227187,7.3804227 10.320459,6.3755023 9.5582449,5.1570833 8.5229975,6.0170334 7.266481,6.6343485 6.2334577,7.6013759 5.149582,8.4745445 z M 9.6088759,1.0026758 C 8.3013694,0.98534052 7.7033019,3.148247 8.9661979,3.6822119 9.9886006,4.0601787 11.042606,2.968368 10.755649,1.9317924 10.657925,1.3899396 10.158361,0.96201137 9.6088769,1.0026758 l -1e-6,0 z"
+         id="path3536-8"
+         style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:none;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L Bold"
+         inkscape:connector-curvature="0" />
+      <path
+         d="M 5.149582,7.4745451 C 5.3049334,7.8555565 5.4749951,8.1626045 5.7940689,7.7203682 6.2006672,7.4518879 7.5527994,6.292513 7.4556976,7.378331 7.0875583,9.3952173 6.6215238,11.395026 6.2845927,13.416813 5.8929541,14.532242 6.9195769,15.485145 7.9224987,14.729405 9.0003636,14.226276 9.9139826,13.440939 10.850418,12.716521 10.706076,12.394581 10.599945,11.928009 10.253583,12.370754 9.7853157,12.60987 8.784467,13.688222 8.5572925,12.841549 8.872666,10.661003 9.5328233,8.5467079 9.9227188,6.3804233 10.32046,5.3755029 9.5582457,4.1570839 8.5229973,5.017034 7.2664804,5.6343492 6.2334583,6.6013765 5.149582,7.4745451 z M 9.6088764,0.00267653 C 8.3013697,-0.01465929 7.7033021,2.1482476 8.9661977,2.6822125 9.9886009,3.0601794 11.042605,1.9683686 10.755649,0.93179313 10.657922,0.38994033 10.158361,-0.03798791 9.6088774,0.00267653 l -1e-6,0 z"
+         id="path3536"
+         style="font-size:40px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;opacity:0.7;fill:url(#linearGradient3456);fill-opacity:1;stroke:none;font-family:URW Palladio L;-inkscape-font-specification:URW Palladio L Bold"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/settings/img/personal.png b/settings/img/personal.png
new file mode 100644
index 0000000000000000000000000000000000000000..8204028f70e6fbc1e21d3b5bf593eac2b6b0839d
Binary files /dev/null and b/settings/img/personal.png differ
diff --git a/settings/img/personal.svg b/settings/img/personal.svg
new file mode 100644
index 0000000000000000000000000000000000000000..613186199939396330b78714a16389a492d7b5f8
--- /dev/null
+++ b/settings/img/personal.svg
@@ -0,0 +1,1724 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="image.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/image.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="47.208389"
+       x2="-39.421574"
+       y1="-5.2547116"
+       x1="-39.421574"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3397"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3417"
+       xlink:href="#linearGradient3587-6-5-10"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       id="g4131"
+       transform="translate(2,40)">
+      <g
+         style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1"
+         id="g3743-9"
+         transform="matrix(0.99998873,0,0,0.99998873,-3.9960435,-40.001608)">
+        <path
+           sodipodi:nodetypes="sccccsscscsscccccccsccscscsscccss"
+           inkscape:connector-curvature="0"
+           d="M 8,1 C 6.3769202,1 5,2.1869577 5,3.71875 5.011517,4.2029062 5.05482,4.7999354 5.34375,6.0625 l 0,0.03125 L 5.375,6.125 C 5.467756,6.3906733 5.6027279,6.5426488 5.78125,6.75 5.9597721,6.9573512 6.1726069,7.2014001 6.375,7.40625 6.398811,7.43035 6.414077,7.445288 6.4375,7.46875 6.47764,7.6434131 6.52626,7.8313866 6.5625,8 6.65892,8.4486073 6.64903,8.7662912 6.625,8.875 5.9275445,9.1198852 5.0598384,9.4115215 4.28125,9.75 3.8441326,9.9400296 3.4485889,10.109721 3.125,10.3125 c -0.3235889,0.202779 -0.6454015,0.355982 -0.75,0.8125 -0.0013,0.02081 -0.0013,0.04169 0,0.0625 -0.1022115,0.938479 -0.2568187,2.318515 -0.375,3.25 -0.025515,0.196074 0.077832,0.402768 0.25,0.5 C 3.6636791,15.701111 5.8352555,16.008445 8,16 c 2.164744,-0.0084 4.319026,-0.333835 5.6875,-1.0625 0.172168,-0.09723 0.275515,-0.303926 0.25,-0.5 -0.03773,-0.291166 -0.08408,-0.947729 -0.125,-1.59375 -0.04092,-0.646021 -0.07644,-1.281501 -0.125,-1.65625 -0.01694,-0.09289 -0.06085,-0.180708 -0.125,-0.25 C 13.127785,10.418403 12.478302,10.101073 11.71875,9.78125 11.025324,9.4892706 10.212392,9.1860601 9.40625,8.84375 9.36113,8.7432405 9.316313,8.4508168 9.40625,8 9.4304,7.8789473 9.4682151,7.7492945 9.5,7.625 9.575755,7.5401485 9.6348046,7.4708101 9.71875,7.375 9.897787,7.1706581 10.090163,6.9562971 10.25,6.75 10.409837,6.5437029 10.540606,6.3667247 10.625,6.125 L 10.65625,6.09375 C 10.98289,4.7754556 10.983061,4.2253548 11,3.75 L 11,3.71875 C 11,2.1869583 9.623082,1 8,1 z"
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.6;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00012147;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           transform="translate(1.9999393,0.00161961)"
+           id="path2880-7" />
+      </g>
+      <path
+         sodipodi:nodetypes="sccccsscscsscccccccsccscscsscccss"
+         inkscape:connector-curvature="0"
+         d="m 6.0037806,-40.000016 c -1.6230605,0 -2.9999647,1.186944 -2.9999647,2.718719 0.011519,0.48415 0.054822,1.081172 0.3437465,2.343722 l 0,0.03125 0.031247,0.03125 c 0.092751,0.265671 0.2277248,0.417644 0.406245,0.624993 0.1785202,0.207349 0.3913525,0.451395 0.5937433,0.656242 0.023812,0.0241 0.039074,0.03903 0.062494,0.06251 0.040137,0.174662 0.088761,0.362633 0.1249979,0.531244 0.096423,0.448603 0.086533,0.766283 0.062505,0.874989 -0.6974467,0.244883 -1.565143,0.536516 -2.343722,0.874989 -0.4371126,0.190028 -0.832652,0.359718 -1.1562374,0.562494 -0.32358434,0.202777 -0.64539391,0.355978 -0.7499904,0.812491 -0.001341,0.02081 -0.001341,0.04169 0,0.06251 -0.10220809,0.938467 -0.25681652,2.318487 -0.37499572,3.249962 -0.02551335,0.196072 0.07782913,0.402763 0.24999681,0.499994 1.41366241,0.763602 3.58521361,1.070932 5.74993271,1.062488 2.1647181,-0.0084 4.3189754,-0.333832 5.6874324,-1.062488 0.172166,-0.09723 0.275513,-0.303922 0.249997,-0.499994 -0.03773,-0.291163 -0.08408,-0.947718 -0.124999,-1.593732 -0.04092,-0.646014 -0.07644,-1.281486 -0.124994,-1.656231 -0.01694,-0.09289 -0.06086,-0.180706 -0.125,-0.249997 -0.43471,-0.51909 -1.084186,-0.836417 -1.8437284,-1.156236 -0.6934176,-0.291975 -1.5063396,-0.595182 -2.3124717,-0.937489 -0.045118,-0.100507 -0.089936,-0.392929 0,-0.84374 0.024152,-0.121051 0.061968,-0.250702 0.093752,-0.374996 0.075756,-0.08485 0.1348031,-0.154188 0.2187476,-0.249997 0.1790349,-0.20434 0.371408,-0.418698 0.5312428,-0.624992 0.1598359,-0.206295 0.2906037,-0.383272 0.3749958,-0.624994 l 0.031247,-0.03125 c 0.3266369,-1.318279 0.3268071,-1.868373 0.3437465,-2.343723 l 0,-0.03125 c 0,-1.531774 -1.3769011,-2.718718 -2.9999647,-2.718718 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient3417);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00012147;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+         id="path2880-5-3" />
+    </g>
+  </g>
+</svg>
diff --git a/settings/img/users.png b/settings/img/users.png
new file mode 100644
index 0000000000000000000000000000000000000000..f56e2442c9e7bc923031e2c846d6dd712760307f
Binary files /dev/null and b/settings/img/users.png differ
diff --git a/settings/img/users.svg b/settings/img/users.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1c8c8955693c1406ff5cca9a37960007eb2ffe85
--- /dev/null
+++ b/settings/img/users.svg
@@ -0,0 +1,1743 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="16"
+   height="16"
+   id="svg11300"
+   inkscape:version="0.48.1 r9760"
+   sodipodi:docname="personal.svg"
+   inkscape:export-filename="/home/jancborchardt/jancborchardt/ownCloud/icons/personal.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata26">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#cccccc"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1280"
+     inkscape:window-height="776"
+     id="namedview24"
+     showgrid="true"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:zoom="22.627418"
+     inkscape:cx="14.025105"
+     inkscape:cy="9.2202448"
+     inkscape:window-x="0"
+     inkscape:window-y="24"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="g4146">
+    <inkscape:grid
+       type="xygrid"
+       id="grid4330"
+       empspacing="5"
+       dotted="true"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true" />
+  </sodipodi:namedview>
+  <defs
+     id="defs3">
+    <linearGradient
+       id="linearGradient4136">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4303">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop4305" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4307" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4297">
+      <stop
+         id="stop4299"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4301"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4115">
+      <stop
+         id="stop4117"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3785">
+      <stop
+         id="stop3787"
+         style="stop-color:#b8b8b8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3789"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient6954">
+      <stop
+         id="stop6960"
+         style="stop-color:#f5f5f5;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop6962"
+         style="stop-color:#d2d2d2;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3341">
+      <stop
+         id="stop3343"
+         style="stop-color:white;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3345"
+         style="stop-color:white;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="24.999998"
+       cy="28.659998"
+       r="16"
+       fx="24.999998"
+       fy="28.659998"
+       id="radialGradient2856"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.56186795,0,0,0.15787922,-6.1682604,5.3385209)" />
+    <linearGradient
+       x1="30"
+       y1="25.084745"
+       x2="30"
+       y2="45"
+       id="linearGradient2858"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <radialGradient
+       cx="26.375898"
+       cy="12.31301"
+       r="8"
+       fx="26.375898"
+       fy="12.31301"
+       id="radialGradient2860"
+       xlink:href="#linearGradient6954"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55250164,-0.0426402,0.04315608,0.50971914,-6.3026675,-1.9765067)" />
+    <linearGradient
+       x1="30"
+       y1="5"
+       x2="30"
+       y2="44.678879"
+       id="linearGradient2862"
+       xlink:href="#linearGradient3785"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="30"
+       y1="0.91818392"
+       x2="30"
+       y2="25.792814"
+       id="linearGradient2864"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.33685737,0,0,0.32161283,-0.10572085,-0.29529973)" />
+    <linearGradient
+       x1="29.955881"
+       y1="21.86607"
+       x2="29.955881"
+       y2="43.144382"
+       id="linearGradient2866"
+       xlink:href="#linearGradient3341"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.42808986,0,0,0.42296591,-2.823809,-3.2486024)" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient7308"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.54681372,0,0,0.39376081,3.7325729,-0.29182867)"
+       x1="34.992828"
+       y1="0.94087797"
+       x2="34.992828"
+       y2="33.955856" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3796"
+       x1="8.3635759"
+       y1="15.028702"
+       x2="15.937561"
+       y2="11.00073"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3798"
+       x1="6.9951797"
+       y1="4.7478018"
+       x2="13.00482"
+       y2="4.7478018"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3815"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3815-3"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-0" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-5"
+       id="linearGradient3831"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3833">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3835" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3837" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3874"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       id="linearGradient3892-2"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.96967712,0,0,0.96967712,0.26437941,-0.96950812)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient3984"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6" />
+    </linearGradient>
+    <linearGradient
+       gradientTransform="matrix(0.78786264,0,0,0.78786264,-1.5726929,-0.7389112)"
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3909-3"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-2"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-2">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-7" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4115-9"
+       id="linearGradient4113-3"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4115-9">
+      <stop
+         id="stop4117-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4119-6"
+         style="stop-color:#363636;stop-opacity:0.698"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3104"
+       id="linearGradient3815-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,0.35950872)"
+       x1="-51.786404"
+       y1="50.786446"
+       x2="-51.786404"
+       y2="2.9062471" />
+    <linearGradient
+       id="linearGradient3104">
+      <stop
+         id="stop3106"
+         style="stop-color:#aaaaaa;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3108"
+         style="stop-color:#c8c8c8;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <radialGradient
+       cx="13.138569"
+       cy="25.625349"
+       r="13.931416"
+       fx="13.138569"
+       fy="25.625349"
+       id="radialGradient2965"
+       xlink:href="#linearGradient3690-451"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.92614711,-1.0546317,0,32.402583,-9.3345932)" />
+    <linearGradient
+       id="linearGradient3690-451">
+      <stop
+         id="stop2857"
+         style="stop-color:#e8e8e8;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2859"
+         style="stop-color:#d8d8d8;stop-opacity:1"
+         offset="0.26238" />
+      <stop
+         id="stop2861"
+         style="stop-color:#c2c2c2;stop-opacity:1"
+         offset="0.66093999" />
+      <stop
+         id="stop2863"
+         style="stop-color:#a5a5a5;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="21.483376"
+       y1="36.255058"
+       x2="21.483376"
+       y2="9.5799999"
+       id="linearGradient2967"
+       xlink:href="#linearGradient3603-84"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762279)" />
+    <linearGradient
+       id="linearGradient3603-84">
+      <stop
+         id="stop2867"
+         style="stop-color:#707070;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2869"
+         style="stop-color:#9e9e9e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11.566265"
+       y1="22.292103"
+       x2="15.214532"
+       y2="33.95525"
+       id="linearGradient3674-262"
+       xlink:href="#linearGradient8265-821-176-38-919-66-249-529"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4893617,0,0,0.4893617,1.7131795,22.728095)" />
+    <linearGradient
+       id="linearGradient8265-821-176-38-919-66-249-529">
+      <stop
+         id="stop2873"
+         style="stop-color:#ffffff;stop-opacity:0.27450982"
+         offset="0" />
+      <stop
+         id="stop2875"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="24.046366"
+       y1="11.673002"
+       x2="24.046366"
+       y2="34.713669"
+       id="linearGradient3677-116"
+       xlink:href="#linearGradient3642-81"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)" />
+    <linearGradient
+       id="linearGradient3642-81">
+      <stop
+         id="stop2879"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop2881"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="34.713669"
+       x2="24.046366"
+       y1="11.673002"
+       x1="24.046366"
+       gradientTransform="matrix(0.55048262,0,0,0.57815823,-3.8262247,-5.2762276)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3037"
+       xlink:href="#linearGradient3642-81"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3155-40"
+       id="linearGradient8639"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.415777,-0.4174938,0.518983,0.5146192,-15.747227,2.6503673)"
+       spreadMethod="pad"
+       x1="23.575972"
+       y1="25.356892"
+       x2="23.575972"
+       y2="31.210939" />
+    <linearGradient
+       id="linearGradient3155-40">
+      <stop
+         id="stop2541"
+         offset="0"
+         style="stop-color:#181818;stop-opacity:1;" />
+      <stop
+         style="stop-color:#dbdbdb;stop-opacity:1;"
+         offset="0.13482948"
+         id="stop2543" />
+      <stop
+         id="stop2545"
+         offset="0.20224422"
+         style="stop-color:#a4a4a4;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.26965895"
+         id="stop2547" />
+      <stop
+         id="stop2549"
+         offset="0.44650277"
+         style="stop-color:#8d8d8d;stop-opacity:1;" />
+      <stop
+         style="stop-color:#959595;stop-opacity:1;"
+         offset="0.57114136"
+         id="stop2551" />
+      <stop
+         id="stop2553"
+         offset="0.72038066"
+         style="stop-color:#cecece;stop-opacity:1;" />
+      <stop
+         id="stop2555"
+         offset="1"
+         style="stop-color:#181818;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-279"
+       id="linearGradient8641"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.867764,0.6930272)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-279">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2559" />
+      <stop
+         id="stop2561"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2563" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-789"
+       id="linearGradient8643"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.983472,0.8092126)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-789">
+      <stop
+         id="stop2567"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2569" />
+      <stop
+         id="stop2571"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-686"
+       id="linearGradient8645"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.465684,0.2892868)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-686">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2575" />
+      <stop
+         id="stop2577"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2579" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-768"
+       id="linearGradient8647"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.581392,0.4054707)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-768">
+      <stop
+         id="stop2583"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2585" />
+      <stop
+         id="stop2587"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3240-907"
+       id="linearGradient8649"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.061661,-0.1164056)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3240-907">
+      <stop
+         style="stop-color:#565656;stop-opacity:1;"
+         offset="0"
+         id="stop2591" />
+      <stop
+         id="stop2593"
+         offset="0.5"
+         style="stop-color:#9a9a9a;stop-opacity:1;" />
+      <stop
+         style="stop-color:#545454;stop-opacity:1;"
+         offset="1"
+         id="stop2595" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3223-699"
+       id="linearGradient8651"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.6022679,-17.177369,-2.1969969e-4)"
+       x1="30.037716"
+       y1="24.989594"
+       x2="30.037716"
+       y2="30.000141" />
+    <linearGradient
+       id="linearGradient3223-699">
+      <stop
+         id="stop2599"
+         offset="0"
+         style="stop-color:#b1b1b1;stop-opacity:1;" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:1;"
+         offset="0.5"
+         id="stop2601" />
+      <stop
+         id="stop2603"
+         offset="1"
+         style="stop-color:#8f8f8f;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3290-678"
+       id="linearGradient8653"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4040235,-0.4056919,0.6073752,0.602268,-17.636692,0.462492)"
+       x1="9"
+       y1="29.056757"
+       x2="9"
+       y2="26.02973" />
+    <linearGradient
+       id="linearGradient3290-678">
+      <stop
+         id="stop2607"
+         offset="0"
+         style="stop-color:#ece5a5;stop-opacity:1;" />
+      <stop
+         id="stop2609"
+         offset="1"
+         style="stop-color:#fcfbf2;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3191-577"
+       id="linearGradient8655"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.3763801,0.03615261,0.03669995,0.374874,-2.2182805,-1.1331002)"
+       x1="5.5178981"
+       y1="37.371799"
+       x2="9.5220556"
+       y2="41.391716" />
+    <linearGradient
+       id="linearGradient3191-577">
+      <stop
+         id="stop2613"
+         offset="0"
+         style="stop-color:#dbce48;stop-opacity:1;" />
+      <stop
+         id="stop2615"
+         offset="1"
+         style="stop-color:#c5b625;stop-opacity:1;" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0"
+       id="linearGradient3934-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4154-8"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-9-0">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-8-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-4-8" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-2-1-0-3">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-1-4-9-3" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-4-6-0-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4326"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4328"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093"
+       id="linearGradient3878"
+       xlink:href="#linearGradient3587-6-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,-3.4266134)" />
+    <linearGradient
+       id="linearGradient3587-6-5">
+      <stop
+         id="stop3589-9-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4357"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0344828,0,0,1.0344828,8.0707628,-14.513825)"
+       x1="0.86849999"
+       y1="13.895414"
+       x2="0.44923753"
+       y2="28.776533" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1"
+       id="linearGradient4405"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4413-7"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-4-5-4-0-1-55">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-3-2-53-4-3-95" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-7-9-86-9-3-6" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4411-3"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       id="linearGradient3587-6-5-2">
+      <stop
+         id="stop3589-9-2-8"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient4466-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.23426255,0,0,0.2859159,18.734419,60.359508)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4">
+      <stop
+         id="stop3589-9-2-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="54.703121"
+       x2="-41.553459"
+       y1="2.2401412"
+       x1="-41.553459"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,17.618755,60.402242)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4483-3"
+       xlink:href="#linearGradient3587-6-5-2-4-9"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-9">
+      <stop
+         id="stop3589-9-2-8-7-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-8"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4564"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5"
+       id="linearGradient4566"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(13.927091,16.573387)"
+       x1="-2.4040222"
+       y1="4.4573336"
+       x2="-2.4040222"
+       y2="18.967093" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2"
+       id="linearGradient4578"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-4-5-4-0-1-55"
+       id="linearGradient4580"
+       gradientUnits="userSpaceOnUse"
+       x1="209.34245"
+       y1="998.45801"
+       x2="209.34245"
+       y2="1013.451" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4359-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,29.038238,-21.358617)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-3">
+      <stop
+         id="stop3589-9-2-6"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3"
+       id="linearGradient4361-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient4597">
+      <stop
+         id="stop4599"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4601"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,25.66524,-19.333318)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient4610"
+       xlink:href="#linearGradient3587-6-5-3"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422"
+       xlink:href="#linearGradient3587-6-5-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4090909,0,0,0.375,7.4545459,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-5">
+      <stop
+         id="stop3589-9-2-4"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3189"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-8">
+      <stop
+         id="stop3589-9-2-67"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3203"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)" />
+    <linearGradient
+       id="linearGradient3120">
+      <stop
+         id="stop3122"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3124"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3207"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)" />
+    <linearGradient
+       id="linearGradient3127">
+      <stop
+         id="stop3129"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3131"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837"
+       id="linearGradient3211"
+       xlink:href="#linearGradient3587-6-5-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)" />
+    <linearGradient
+       id="linearGradient3134">
+      <stop
+         id="stop3136"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3138"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2409"
+       xlink:href="#linearGradient3587-6-5-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.2403101,0,0,0.8988764,10.387597,0.2247191)" />
+    <linearGradient
+       id="linearGradient3587-6-5-1">
+      <stop
+         id="stop3589-9-2-0"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-21"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="40.805084"
+       y1="5.6271191"
+       x2="40.805084"
+       y2="17.627119"
+       id="linearGradient3206"
+       xlink:href="#linearGradient3587-8-5"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="translate(-32.805085,-3.6271193)" />
+    <linearGradient
+       id="linearGradient3587-8-5">
+      <stop
+         id="stop3589-2-7"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-3-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="17.627119"
+       x2="40.805084"
+       y1="5.6271191"
+       x1="40.805084"
+       gradientTransform="translate(-32.805085,-3.6271193)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3180"
+       xlink:href="#linearGradient3587-8-5"
+       inkscape:collect="always" />
+    <linearGradient
+       x1="1.3333321"
+       y1="6.6666665"
+       x2="1.3333321"
+       y2="33.333332"
+       id="linearGradient2422-1"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2727273,0,0,0.375,9.636365,1.5)" />
+    <linearGradient
+       id="linearGradient3587-6-5-86">
+      <stop
+         id="stop3589-9-2-65"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2427"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,1.5842703)" />
+    <linearGradient
+       id="linearGradient3207-3">
+      <stop
+         id="stop3209"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3211"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2436"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,9.58427)" />
+    <linearGradient
+       id="linearGradient3214">
+      <stop
+         id="stop3216"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3218"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="11"
+       y1="6"
+       x2="11"
+       y2="17"
+       id="linearGradient2442"
+       xlink:href="#linearGradient3587-6-5-86"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.4651163,0,0,0.3370786,4.3953488,5.5842706)" />
+    <linearGradient
+       id="linearGradient3221">
+      <stop
+         id="stop3223"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3225"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="1.3333321"
+       y1="4.9755898"
+       x2="1.3333321"
+       y2="37.373981"
+       id="linearGradient2422-1-0"
+       xlink:href="#linearGradient3587-6-5-0"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.39871888,0,0,0.3091132,71.812715,15.470662)" />
+    <linearGradient
+       id="linearGradient3587-6-5-0">
+      <stop
+         id="stop3589-9-2-5"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="46.395508"
+       y1="12.707516"
+       x2="46.395508"
+       y2="38.409042"
+       id="linearGradient3795-2"
+       xlink:href="#linearGradient3587-6-5-3-5-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,28.02322,-5.9219706)" />
+    <linearGradient
+       id="linearGradient3587-6-5-3-5-7">
+      <stop
+         id="stop3589-9-2-2-6-2"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-73-5-1"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3587-6-5-3-5">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1"
+         id="stop3589-9-2-2-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop3591-7-4-73-5" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-3-5"
+       id="linearGradient4872"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(-0.4100229,0,0,0.5447147,19.329265,-26.729116)"
+       x1="100.77747"
+       y1="17.859186"
+       x2="100.77747"
+       y2="38.055252" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4894"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4900"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4906"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient4912"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient3587-6-5-8-6">
+      <stop
+         id="stop3589-9-2-67-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-2-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4935">
+      <stop
+         id="stop4937"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4939"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4942">
+      <stop
+         id="stop4944"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4946"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8-6"
+       id="linearGradient4912-4"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       id="linearGradient4949">
+      <stop
+         id="stop4951"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop4953"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5012"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5,7.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5015"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,7.499999)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5018"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,-0.5000001,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-8"
+       id="linearGradient5021"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.375,0,0,0.375,7.5,0.5)"
+       x1="26.045763"
+       y1="9.6223383"
+       x2="26.045763"
+       y2="19.490837" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4"
+       id="linearGradient3335"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.21864454,0,0,0.26685422,18.618755,-19.597758)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4134"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136"
+       id="linearGradient4150"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6"
+       id="linearGradient3335-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,76.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6">
+      <stop
+         id="stop3589-9-2-8-7-8"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-0"
+       id="linearGradient3335-7-8"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-0">
+      <stop
+         id="stop3589-9-2-8-7-8-7"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-7"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-4"
+       id="linearGradient3335-7-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,0.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.553459"
+       y2="54.703121" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-4">
+      <stop
+         id="stop3589-9-2-8-7-8-2"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-2"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-7"
+       id="linearGradient3335-7-3"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,16.619476,1.402242)"
+       x1="-41.553459"
+       y1="2.2401412"
+       x2="-41.755585"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-7">
+      <stop
+         id="stop3589-9-2-8-7-8-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-5"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2"
+       id="linearGradient3335-7-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2">
+      <stop
+         id="stop3589-9-2-8-7-8-77"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4136-9"
+       id="linearGradient4150-0"
+       gradientUnits="userSpaceOnUse"
+       spreadMethod="pad"
+       x1="9"
+       y1="0"
+       x2="9"
+       y2="15" />
+    <linearGradient
+       id="linearGradient4136-9">
+      <stop
+         offset="0"
+         style="stop-color:#000000;stop-opacity:1;"
+         id="stop4138-6" />
+      <stop
+         offset="1"
+         style="stop-color:#363636;stop-opacity:1"
+         id="stop4140-3" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-2-4-6-2-6"
+       id="linearGradient3335-7-1-7"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.2186487,0,0,0.26685422,56.619476,1.4022422)"
+       x1="-39.421574"
+       y1="-5.2547116"
+       x2="-39.421574"
+       y2="47.208389" />
+    <linearGradient
+       id="linearGradient3587-6-5-2-4-6-2-6">
+      <stop
+         id="stop3589-9-2-8-7-8-77-4"
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-0-3-4-9-3"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-10"
+       id="linearGradient4328-1"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       x1="8.7094374"
+       y1="1.0035814"
+       x2="8.6826077"
+       y2="16.052532" />
+    <linearGradient
+       id="linearGradient3587-6-5-10">
+      <stop
+         id="stop3589-9-2-3"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-4"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="16.052532"
+       x2="8.6826077"
+       y1="1.0035814"
+       x1="8.7094374"
+       gradientTransform="matrix(0.99998838,0,0,0.99998838,-1.9961264,-41.000004)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3417"
+       xlink:href="#linearGradient3587-6-5-10"
+       inkscape:collect="always" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3587-6-5-19"
+       id="linearGradient4326-9"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       x1="14.501121"
+       y1="-1.4095211"
+       x2="14.152531"
+       y2="20.074369" />
+    <linearGradient
+       id="linearGradient3587-6-5-19">
+      <stop
+         id="stop3589-9-2-62"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop3591-7-4-54"
+         style="stop-color:#363636;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       y2="20.074369"
+       x2="14.152531"
+       y1="-1.4095211"
+       x1="14.501121"
+       gradientTransform="matrix(0.6858824,0,0,0.68591053,-5.3691237,-18.974705)"
+       gradientUnits="userSpaceOnUse"
+       id="linearGradient3437"
+       xlink:href="#linearGradient3587-6-5-19"
+       inkscape:collect="always" />
+  </defs>
+  <g
+     transform="matrix(0.78786264,0,0,0.78786264,-3.1483699,0.44173984)"
+     id="g3743-3"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <rect
+     style="color:#000000;fill:#ccc000;fill-opacity:0;fill-rule:nonzero;stroke:none;stroke-width:1px;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+     id="rect3136"
+     width="163.31035"
+     height="97.986206"
+     x="-62.896553"
+     y="-32.993103" />
+  <g
+     transform="matrix(0.99998873,0,0,0.99998873,-3.996044,-20.001608)"
+     id="g3743-9-4"
+     style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1" />
+  <g
+     id="g4146">
+    <g
+       id="g4309"
+       transform="translate(4.0000002,19.999999)">
+      <g
+         style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-opacity:1"
+         id="g3743-9-4-9"
+         transform="matrix(0.68591077,0,0,0.68591077,-6.7409611,-17.975799)">
+        <path
+           d="M 8,1 C 6.3769202,1 5,2.1869577 5,3.71875 5.011517,4.2029062 5.05482,4.7999354 5.34375,6.0625 l 0,0.03125 L 5.375,6.125 C 5.467756,6.3906733 5.6027279,6.5426488 5.78125,6.75 5.9597721,6.9573512 6.1726069,7.2014001 6.375,7.40625 6.398811,7.43035 6.414077,7.445288 6.4375,7.46875 6.47764,7.6434131 6.52626,7.8313866 6.5625,8 6.65892,8.4486073 6.64903,8.7662912 6.625,8.875 5.9275445,9.1198852 5.0598384,9.4115215 4.28125,9.75 3.8441326,9.9400296 3.4485889,10.109721 3.125,10.3125 c -0.3235889,0.202779 -0.6454015,0.355982 -0.75,0.8125 -0.0013,0.02081 -0.0013,0.04169 0,0.0625 -0.1022115,0.938479 -0.2568187,2.318515 -0.375,3.25 -0.025515,0.196074 0.077832,0.402768 0.25,0.5 C 3.6636791,15.701111 5.8352555,16.008445 8,16 c 2.164744,-0.0084 4.319026,-0.333835 5.6875,-1.0625 0.172168,-0.09723 0.275515,-0.303926 0.25,-0.5 -0.03773,-0.291166 -0.08408,-0.947729 -0.125,-1.59375 -0.04092,-0.646021 -0.07644,-1.281501 -0.125,-1.65625 -0.01694,-0.09289 -0.06085,-0.180708 -0.125,-0.25 C 13.127785,10.418403 12.478302,10.101073 11.71875,9.78125 11.025324,9.4892706 10.212392,9.1860601 9.40625,8.84375 9.36113,8.7432405 9.316313,8.4508168 9.40625,8 9.4304,7.8789473 9.4682151,7.7492945 9.5,7.625 9.575755,7.5401485 9.6348046,7.4708101 9.71875,7.375 9.897787,7.1706581 10.090163,6.9562971 10.25,6.75 10.409837,6.5437029 10.540606,6.3667247 10.625,6.125 L 10.65625,6.09375 C 10.98289,4.7754556 10.983061,4.2253548 11,3.75 L 11,3.71875 C 11,2.1869583 9.623082,1 8,1 z m 8.580821,-2.4948177 c -2.366287,0 -4.373697,1.73046459 -4.373697,3.9636633 0.01679,0.7058509 0.07992,1.5762593 0.501153,3.4169511 l 0,0.045559 0.04556,0.045559 c 0.135229,0.3873249 0.332004,0.6088898 0.592271,0.911187 0.260268,0.3022971 0.570559,0.6580958 0.865628,0.9567463 0.03471,0.035135 0.05697,0.056914 0.09112,0.091119 0.05852,0.2546412 0.129403,0.5286876 0.182237,0.7745089 0.140571,0.6540242 0.126152,1.1171753 0.09112,1.2756618 -1.01682,0.3570183 -2.281848,0.7821943 -3.416951,1.2756623 -0.637273,0.277044 -1.2139362,0.524437 -1.6856962,0.820068 -0.47176,0.295631 -0.9409303,0.518986 -1.0934244,1.184543 -0.0019,0.03034 -0.0019,0.06078 0,0.09112 -0.149014,1.368208 -0.3744158,3.380161 -0.5467122,4.738172 -0.037198,0.285856 0.1134712,0.587195 0.3644748,0.72895 2.061002,1.113268 5.226941,1.56133 8.38292,1.549018 3.155979,-0.01225 6.296704,-0.486698 8.291802,-1.549018 0.251003,-0.141752 0.401673,-0.443094 0.364474,-0.72895 -0.05501,-0.424491 -0.12258,-1.381693 -0.182237,-2.323527 -0.05966,-0.941833 -0.111442,-1.868299 -0.182237,-2.414645 -0.0247,-0.135424 -0.08871,-0.263454 -0.182238,-0.364475 -0.63377,-0.756791 -1.580651,-1.219426 -2.688001,-1.685696 -1.010946,-0.425676 -2.196118,-0.867727 -3.371392,-1.3667807 -0.06578,-0.1465327 -0.131119,-0.5728569 0,-1.2301024 0.03521,-0.1764826 0.09034,-0.3655033 0.136678,-0.5467122 0.110443,-0.1237049 0.196531,-0.2247933 0.318915,-0.3644747 0.261018,-0.2979099 0.541483,-0.6104266 0.774509,-0.911187 0.233026,-0.3007604 0.423674,-0.5587767 0.546712,-0.911187 l 0.04556,-0.045559 c 0.476208,-1.9219403 0.476457,-2.7239318 0.501153,-3.4169512 l 0,-0.045559 c 0,-2.23319784 -2.007408,-3.9636633 -4.373698,-3.9636633 z"
+           style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.6;color:#000000;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00012147;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+           transform="translate(1.9999393,0.00161961)"
+           id="path2880-7-3-9"
+           inkscape:connector-curvature="0" />
+      </g>
+      <path
+         d="m 0.1179355,-18.288795 c -1.1132415,0 -2.0576469,0.814147 -2.0576469,1.86482 0.0079,0.332088 0.037602,0.741596 0.2357724,1.607602 l 0,0.02144 0.021432,0.02143 c 0.063617,0.182229 0.1561942,0.28647 0.2786394,0.428695 0.1224453,0.142224 0.268425,0.30962 0.4072429,0.450128 0.016332,0.01653 0.0268,0.02677 0.042864,0.04288 0.02753,0.119804 0.060881,0.248737 0.085735,0.36439 0.066135,0.307705 0.059353,0.525608 0.042871,0.600171 -0.4783719,0.16797 -1.0735165,0.368006 -1.6075363,0.600171 -0.2998113,0.130344 -0.5711079,0.246737 -0.7930521,0.385825 -0.2219433,0.139089 -0.4426695,0.244172 -0.5144111,0.557303 -9.199e-4,0.01427 -9.199e-4,0.0286 0,0.04288 -0.070103,0.643711 -0.176148,1.5902928 -0.257206,2.2292088 -0.017499,0.134489 0.053382,0.276262 0.1714704,0.342955 0.9696174,0.523769 2.4590634,0.734572 3.9438235,0.72878 1.4847593,-0.0058 2.9623436,-0.228982 3.9009551,-0.72878 0.118087,-0.06669 0.1889717,-0.208466 0.1714705,-0.342955 -0.025879,-0.199714 -0.05767,-0.650058 -0.085735,-1.0931708 -0.028067,-0.443113 -0.05243,-0.878995 -0.085733,-1.136039 -0.01162,-0.06372 -0.041743,-0.12395 -0.085737,-0.171478 -0.2981634,-0.356053 -0.7436327,-0.573714 -1.2645956,-0.793083 -0.4756083,-0.200271 -1.0331837,-0.408247 -1.5861019,-0.643041 -0.030946,-0.06894 -0.061686,-0.269518 0,-0.578737 0.016565,-0.08303 0.042503,-0.171961 0.064303,-0.257217 0.05196,-0.0582 0.09246,-0.10576 0.1500368,-0.171477 0.1227983,-0.140161 0.2547452,-0.287193 0.3643743,-0.428694 0.1096299,-0.141502 0.1993223,-0.262893 0.257206,-0.428695 l 0.021432,-0.02144 c 0.2240371,-0.904232 0.2241538,-1.281552 0.2357725,-1.607603 l 0,-0.02143 c 0,-1.050672 -0.9444033,-1.864819 -2.057647,-1.864819 z m 5.8858451,-1.711221 c -1.6230605,0 -2.9999647,1.186944 -2.9999647,2.718719 0.011519,0.48415 0.054822,1.081172 0.3437465,2.343722 l 0,0.03125 0.031247,0.03125 c 0.092751,0.265671 0.2277248,0.417644 0.406245,0.624993 0.1785202,0.207349 0.3913525,0.451395 0.5937433,0.656242 0.023812,0.0241 0.039074,0.03903 0.062494,0.06251 0.040137,0.174662 0.088761,0.362633 0.1249979,0.531244 0.096423,0.448603 0.086533,0.766283 0.062505,0.874989 -0.6974467,0.244883 -1.565143,0.536516 -2.343722,0.874989 -0.4371126,0.190028 -0.832652,0.359718 -1.1562374,0.562494 -0.32358434,0.202777 -0.64539391,0.355978 -0.7499904,0.812491 -0.001341,0.02081 -0.001341,0.04169 0,0.06251 -0.10220809,0.938467 -0.25681652,2.318487 -0.37499572,3.249962 -0.02551335,0.196072 0.07782913,0.402763 0.24999681,0.499994 1.41366241,0.763602 3.58521361,1.070932 5.74993271,1.062488 2.1647181,-0.0084 4.3189754,-0.333832 5.6874324,-1.062488 0.172166,-0.09723 0.275513,-0.303922 0.249997,-0.499994 -0.03773,-0.291163 -0.08408,-0.947718 -0.124999,-1.593732 -0.04092,-0.646014 -0.07644,-1.281486 -0.124994,-1.656231 -0.01694,-0.09289 -0.06086,-0.180706 -0.125,-0.249997 -0.43471,-0.51909 -1.084186,-0.836417 -1.8437284,-1.156236 -0.6934176,-0.291975 -1.5063396,-0.595182 -2.3124717,-0.937489 -0.045118,-0.100507 -0.089936,-0.392929 0,-0.84374 0.024152,-0.121051 0.061968,-0.250702 0.093752,-0.374996 0.075756,-0.08485 0.1348031,-0.154188 0.2187476,-0.249997 0.1790349,-0.20434 0.371408,-0.418698 0.5312428,-0.624992 0.1598359,-0.206295 0.2906037,-0.383272 0.3749958,-0.624994 l 0.031247,-0.03125 c 0.3266369,-1.318279 0.3268071,-1.868373 0.3437465,-2.343723 l 0,-0.03125 c 0,-1.531774 -1.3769011,-2.718718 -2.9999647,-2.718718 z"
+         style="font-size:medium;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-indent:0;text-align:start;text-decoration:none;line-height:normal;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;text-anchor:start;baseline-shift:baseline;opacity:0.7;color:#000000;fill:url(#linearGradient3437);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00012147;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;font-family:Sans;-inkscape-font-specification:Sans"
+         id="path2880-5-3-9-2"
+         inkscape:connector-curvature="0" />
+    </g>
+  </g>
+</svg>
diff --git a/settings/js/apps.js b/settings/js/apps.js
new file mode 100644
index 0000000000000000000000000000000000000000..e2f882c6fec7f1760861812888dacb207bbea2e7
--- /dev/null
+++ b/settings/js/apps.js
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+$(document).ready(function(){
+	$('#leftcontent li').each(function(index,li){
+		var app=$.parseJSON($(this).children('span').text());
+		$(li).data('app',app);
+	});
+	$('#leftcontent li').click(function(){
+		var app=$(this).data('app');
+		$('#rightcontent p').show();
+		$('#rightcontent span.name').text(app.name);
+		$('#rightcontent span.version').text(app.version);
+		$('#rightcontent p.description').text(app.description);
+		$('#rightcontent span.author').text(app.author);
+		$('#rightcontent span.licence').text(app.licence);
+		
+		$('#rightcontent input.enable').show();
+		$('#rightcontent input.enable').val((app.active)?t('settings','Disable'):t('settings','Enable'));
+		$('#rightcontent input.enable').data('appid',app.id);
+		$('#rightcontent input.enable').data('active',app.active);
+	});
+	$('#rightcontent input.enable').click(function(){
+		var app=$(this).data('appid');
+		var active=$(this).data('active');
+		if(app){
+			if(active){
+				$.post(OC.filePath('settings','ajax','disableapp.php'),{appid:app});
+				$('#leftcontent li[data-id="'+app+'"]').removeClass('active');
+			}else{
+				$.post(OC.filePath('settings','ajax','enableapp.php'),{appid:app});
+				$('#leftcontent li[data-id="'+app+'"]').addClass('active');
+			}
+			active=!active;
+			$(this).data('active',active);
+			$(this).val((active)?t('settings','Disable'):t('settings','Enable'));
+			var appData=$('#leftcontent li[data-id="'+app+'"]');
+			appData.active=active;
+		}
+	});
+});
diff --git a/settings/js/personal.js b/settings/js/personal.js
new file mode 100644
index 0000000000000000000000000000000000000000..65bb81b0f04e7e112cfbe3e53060719201b0e467
--- /dev/null
+++ b/settings/js/personal.js
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+$(document).ready(function(){
+	$("#passwordbutton").click( function(){
+		if ($('#pass1').val() != '' && $('#pass2').val() != '') {
+			// Serialize the data
+			var post = $( "#passwordform" ).serialize();
+			$('#passwordchanged').hide();
+			$('#passworderror').hide();
+			// Ajax foo
+			$.post( 'ajax/changepassword.php', post, function(data){
+				if( data.status == "success" ){
+					$('#pass1').val('');
+					$('#pass2').val('');
+					$('#passwordchanged').show();
+				}
+				else{
+					$('#passworderror').html( data.data.message );
+					$('#passworderror').show();
+				}
+			});
+			return false;
+		} else {
+			$('#passwordchanged').hide();
+			$('#passworderror').show();
+			return false;
+		}
+
+	});
+
+	$("#languageinput").chosen();
+
+	$("#languageinput").change( function(){
+		// Serialize the data
+		var post = $( "#languageinput" ).serialize();
+		// Ajax foo
+		$.post( 'ajax/setlanguage.php', post, function(data){
+			if( data.status == "success" ){
+				location.reload();
+			}
+			else{
+				$('#passworderror').html( data.data.message );
+			}
+		});
+		return false;
+	});
+
+	// reset value when edited, workaround because of .select() not working with disabled inputs
+	$('#webdav').focus(function(event){
+		openidValue = $('#webdav').val();
+	});
+	$('#webdav').blur(function(event){
+		$('#webdav').val(openidValue);
+	});
+} );
diff --git a/settings/js/users.js b/settings/js/users.js
new file mode 100644
index 0000000000000000000000000000000000000000..c60fb32c40e7fbf9eb267f49d1e76b9e0f582213
--- /dev/null
+++ b/settings/js/users.js
@@ -0,0 +1,157 @@
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+$(document).ready(function(){
+	function applyMultiplySelect(element){
+		var checked=[];
+		var user=element.data('username')
+		if(element.data('userGroups')){
+			checked=element.data('userGroups').split(', ');
+		}
+		if(user){
+			var checkHandeler=function(group){
+				if(user==OC.currentUser && group=='admin'){
+					return false;
+				}
+				$.post(
+					OC.filePath('settings','ajax','togglegroups.php'),
+					{
+						username:user,
+						group:group
+					},
+					function(){}
+				);
+			}
+		}else{
+			checkHandeler=false;
+		}
+		element.multiSelect({
+			createText:'add group',
+			 checked:checked,
+			 oncheck:checkHandeler,
+			 onuncheck:checkHandeler
+		});
+	}
+	$('select[multiple]').each(function(index,element){
+		applyMultiplySelect($(element));
+	});
+	
+	$('td.remove>img').live('click',function(event){
+		var uid=$(this).parent().parent().data('uid');
+		$.post(
+			OC.filePath('settings','ajax','removeuser.php'),
+			{username:uid},
+			function(result){
+			
+			}
+		);
+		$(this).parent().parent().remove();
+	});
+	
+	$('td.password>img').live('click',function(event){
+		event.stopPropagation();
+		var img=$(this);
+		var uid=img.parent().parent().data('uid');
+		var input=$('<input type="password">');
+		img.css('display','none');
+		img.parent().children('span').replaceWith(input);
+		input.focus();
+		input.keypress(function(event) {
+			if(event.keyCode == 13) {
+				if($(this).val().length>0){
+					$.post(
+						OC.filePath('settings','ajax','changepassword.php'),
+						{username:uid,password:$(this).val()},
+						function(result){}
+					);
+					input.blur();
+				}else{
+					input.blur();
+				}
+			}
+		});
+		input.blur(function(){
+			$(this).replaceWith($('<span>●●●●●●●</span>'));
+			img.css('display','');
+		});
+	});
+	$('td.password').live('click',function(event){
+		$(this).children('img').click();
+	});
+
+	$('td.quota>img').live('click',function(event){
+		event.stopPropagation();
+		var img=$(this);
+		var uid=img.parent().parent().data('uid');
+		var input=$('<input>');
+		var quota=img.parent().children('span').text();
+		img
+		if(quota=='None'){
+			quota='';
+		}
+		input.val(quota);
+		img.css('display','none');
+		img.parent().children('span').replaceWith(input);
+		input.focus();
+		input.keypress(function(event) {
+			if(event.keyCode == 13) {
+				$(this).parent().attr('data-quota',$(this).val());
+				if($(this).val().length>0){
+					$.post(
+						OC.filePath('settings','ajax','setquota.php'),
+						   {username:uid,quota:$(this).val()},
+						   function(result){}
+					);
+					input.blur();
+				}else{
+					input.blur();
+				}
+			}
+		});
+		input.blur(function(){
+			var quota=$(this).parent().data('quota');
+			$(this).replaceWith($('<span>'+quota+'</span>'));
+			img.css('display','');
+		});
+	});
+	$('td.quota').live('click',function(event){
+		$(this).children('img').click();
+	});
+	
+	$('#newuser').submit(function(event){
+		event.preventDefault();
+		var username=$('#newusername').val();
+		var password=$('#newuserpassword').val();
+		var groups=$('#newusergroups').prev().children('div').data('settings').checked;
+		$.post(
+			OC.filePath('settings','ajax','createuser.php'),
+			{
+				username:username,
+				password:password,
+				groups:groups,
+			},
+			function(result){
+				
+			}
+		);
+		var tr=$('#content table tbody tr').first().clone();
+		tr.attr('data-uid',username);
+		tr.find('td.name').text(username);
+		var select=$('<select multiple="multiple" data-placehoder="Groups" title="Groups">');
+		select.data('username',username);
+		select.data('userGroups',groups.join(', '));
+		tr.find('td.groups').empty();
+		$.each($('#content table').data('groups').split(', '),function(i,group){
+			select.append($('<option value="'+group+'">'+group+'</option>'));
+		});
+		tr.find('td.groups').append(select);
+		if(tr.find('td.remove img').length==0){
+			tr.find('td.remove').append($('<img alt="Delete" title="'+t('settings','Delete')+'" class="svg action" src="'+OC.imagePath('core','actions/delete')+'"/>'));
+		}
+		applyMultiplySelect(select);
+		$('#content table tbody').last().after(tr);
+	});
+});
diff --git a/settings/l10n/bg_BG.php b/settings/l10n/bg_BG.php
new file mode 100644
index 0000000000000000000000000000000000000000..004166252bb2bf1de374ec1a5b2732686f7dbcef
--- /dev/null
+++ b/settings/l10n/bg_BG.php
@@ -0,0 +1,8 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Проблем с индентификацията",
+"Invalid request" => "Невалидна заявка",
+"Language changed" => "Езика е сменен",
+"Your password got changed" => "Вашата парола е сменена",
+"New password" => "Нова парола",
+"Language" => "Език"
+);
diff --git a/settings/l10n/ca.php b/settings/l10n/ca.php
new file mode 100644
index 0000000000000000000000000000000000000000..95d84eeed9f872cb620b81380e2676007742f33f
--- /dev/null
+++ b/settings/l10n/ca.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Error d'autenticació",
+"OpenID Changed" => "OpenID ha canviat",
+"Invalid request" => "Sol.licitud no vàlida",
+"Language changed" => "S'ha canviat l'idioma",
+"Add your application" => "Afegir una aplicació",
+"Select an App" => "Sel·leccioneu una aplicació",
+"-licensed" => "- amb llicència",
+"by" => "per",
+"Ask a question" => "Feu una pregunta",
+"Problems connecting to help database." => "Problemes per connectar-se a la base de dades d'ajuda.",
+"Go there manually." => "Vés-hi manualment.",
+"Answer" => "Resposta",
+"You use" => "Esteu usant",
+"of the available" => "del disponible",
+"Your password got changed" => "La contrasenya ha canviat",
+"Current password" => "contrasenya actual",
+"New password" => "Contrasenya nova",
+"show" => "mostra",
+"Change password" => "canvia la contrasenya",
+"Language" => "Idioma",
+"Help translating" => "Ajudeu amb la traducció",
+"use this address to connect to your ownCloud in your file manager" => "useu aquesta adreça per connectar-vos a ownCloud des del gestor de fitxers",
+"Name" => "Nom",
+"Password" => "Contrasenya",
+"Groups" => "Grups",
+"Create" => "Crea",
+"Delete" => "Esborra"
+);
diff --git a/settings/l10n/da.php b/settings/l10n/da.php
new file mode 100644
index 0000000000000000000000000000000000000000..cb51f709a47fe22e69e7702cad1607cfc5ebd491
--- /dev/null
+++ b/settings/l10n/da.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Godkendelsesfejl",
+"OpenID Changed" => "OpenID ændret",
+"Invalid request" => "Ugyldig forespørgsel",
+"Language changed" => "Sprog ændret",
+"Add your application" => "Tilføj dit program",
+"Select an App" => "Vælg en App",
+"-licensed" => "-licenseret",
+"by" => "af",
+"Ask a question" => "Stil et spørgsmål",
+"Problems connecting to help database." => "Problemer med at forbinde til hjælpe-databasen",
+"Go there manually." => "GÃ¥ derhen manuelt.",
+"Answer" => "Svar",
+"You use" => "Du benytter",
+"of the available" => "af det tilgængelige",
+"Your password got changed" => "Din adgangskode er blevet ændret",
+"Current password" => "Nuværende adgangskode",
+"New password" => "Ny adgangskode",
+"show" => "vis",
+"Change password" => "Skift password",
+"Language" => "Sprog",
+"Help translating" => "Hjælp med at oversætte",
+"use this address to connect to your ownCloud in your file manager" => "benyt denne adresse til at forbinde til din ownCloud i din filbrowser",
+"Name" => "Navn",
+"Password" => "Kodeord",
+"Groups" => "Grupper",
+"Create" => "Ny",
+"Delete" => "Slet"
+);
diff --git a/settings/l10n/de.php b/settings/l10n/de.php
new file mode 100644
index 0000000000000000000000000000000000000000..21f3a8f8aa0c788ef9c3a0f674ea28fd236f8e6a
--- /dev/null
+++ b/settings/l10n/de.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Berechtigungsfehler",
+"OpenID Changed" => "OpenID geändert",
+"Invalid request" => "Ungültige Anfrage",
+"Language changed" => "Sprache geändert",
+"Add your application" => "Eigene Anwendung hinzufügen",
+"Select an App" => "Wähle eine Anwendung aus",
+"-licensed" => "-lizenziert",
+"by" => "von",
+"Ask a question" => "Stell eine Frage",
+"Problems connecting to help database." => "Probleme bei der Verbindung zur Hilfe-Datenbank.",
+"Go there manually." => "Datenbank direkt besuchen.",
+"Answer" => "Antwort",
+"You use" => "Du nutzt",
+"of the available" => "der verfügbaren",
+"Your password got changed" => "Dein Passwort wurde geändert",
+"Current password" => "Aktuelles Passwort",
+"New password" => "Neues Passwort",
+"show" => "zeigen",
+"Change password" => "Passwort ändern",
+"Language" => "Sprache",
+"Help translating" => "Hilf bei der Ãœbersetzung",
+"use this address to connect to your ownCloud in your file manager" => "benutze diese Adresse, um deine ownCloud mit deinem Dateiverwalter zu verbinden",
+"Name" => "Name",
+"Password" => "Passwort",
+"Groups" => "Gruppen",
+"Create" => "Anlegen",
+"Delete" => "Löschen"
+);
diff --git a/settings/l10n/el.php b/settings/l10n/el.php
new file mode 100644
index 0000000000000000000000000000000000000000..8d55698e0aa2e45ba50844f3c3582b768a664719
--- /dev/null
+++ b/settings/l10n/el.php
@@ -0,0 +1,28 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Σφάλμα ταυτοποίησης",
+"OpenID Changed" => "Το OpenID άλλαξε",
+"Invalid request" => "Άκυρα αίτημα",
+"Language changed" => "Η γλώσσα άλλαξε",
+"Select an App" => "Επιλέξτε μια εφαρμογή",
+"-licensed" => "-με άδεια",
+"by" => "με",
+"Ask a question" => "Κάντε μια ερώτηση",
+"Problems connecting to help database." => "Προβλήματα κατά τη σύνδεση με τη βάση δεδομένων βοήθειας.",
+"Go there manually." => "Χειροκίνητη μετάβαση.",
+"Answer" => "Απάντηση",
+"You use" => "Χρησιμοποιείτε",
+"of the available" => "από τα διαθέσιμα",
+"Your password got changed" => "Ο κωδικός πρόσβασής σας άλαλαξε",
+"Current password" => "Τρέχοντα κωδικό πρόσβασης",
+"New password" => "Νέος κωδικός",
+"show" => "Εμφάνιση",
+"Change password" => "Αλλαγή κωδικού πρόσβασης",
+"Language" => "Γλώσσα",
+"Help translating" => "Βοηθήστε στη μετάφραση",
+"use this address to connect to your ownCloud in your file manager" => "χρησιμοποιήστε αυτή τη διεύθυνση για να συνδεθείτε στο ownCloud σας από το διαχειριστή αρχείων σας",
+"Name" => "Όνομα",
+"Password" => "Κωδικός",
+"Groups" => "Ομάδες",
+"Create" => "Δημιουργία",
+"Delete" => "Διαγραφή"
+);
diff --git a/settings/l10n/es.php b/settings/l10n/es.php
new file mode 100644
index 0000000000000000000000000000000000000000..0459ae453669f7c136f2c5f3adc0c2b551454107
--- /dev/null
+++ b/settings/l10n/es.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Error de autentificación",
+"OpenID Changed" => "OpenID Cambiado",
+"Invalid request" => "Solicitud no válida",
+"Language changed" => "Idioma cambiado",
+"Add your application" => "Añadir tu aplicación",
+"Select an App" => "Seleccionar una aplicación",
+"-licensed" => "-autorizado",
+"by" => "por",
+"Ask a question" => "Hacer una pregunta",
+"Problems connecting to help database." => "Problemas al conectar con la base de datos de ayuda.",
+"Go there manually." => "Ir manualmente",
+"Answer" => "Respuesta",
+"You use" => "Estás utilizando",
+"of the available" => "del total disponible de",
+"Your password got changed" => "Tu contraseña ha sido cambiada",
+"Current password" => "Contraseña actual",
+"New password" => "Nueva contraseña:",
+"show" => "mostrar",
+"Change password" => "Cambiar contraseña",
+"Language" => "Idioma",
+"Help translating" => "Ayuda a traducir",
+"use this address to connect to your ownCloud in your file manager" => "usar esta dirección para conectar tu ownCloud en tu explorador de archivos",
+"Name" => "Nombre",
+"Password" => "Contraseña",
+"Groups" => "Grupos",
+"Create" => "Crear",
+"Delete" => "Eliminar"
+);
diff --git a/settings/l10n/fr.php b/settings/l10n/fr.php
new file mode 100644
index 0000000000000000000000000000000000000000..769d1e9e6acc06550f1cf03b3df77dde92d215d5
--- /dev/null
+++ b/settings/l10n/fr.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Erreur d'authentification",
+"OpenID Changed" => "Identifiant OpenID changé",
+"Invalid request" => "Requète invalide",
+"Language changed" => "Langue changée",
+"Add your application" => "Ajoutez votre application",
+"Select an App" => "Sélectionner une Application",
+"-licensed" => "sous licence",
+"by" => "par",
+"Ask a question" => "Poser une question",
+"Problems connecting to help database." => "Problème de connexion à la base de données d'aide.",
+"Go there manually." => "S'y rendre manuellement.",
+"Answer" => "Réponse",
+"You use" => "Vous utilisez",
+"of the available" => "sur un total de",
+"Your password got changed" => "Votre mot de passe a été changé",
+"Current password" => "Mot de passe actuel",
+"New password" => "Nouveau mot de passe",
+"show" => "Afficher",
+"Change password" => "Changer de mot de passe",
+"Language" => "Langue",
+"Help translating" => "Aider à traduire",
+"use this address to connect to your ownCloud in your file manager" => "utilisez cette adresse pour vous connecter à votre ownCloud depuis votre explorateur de fichiers",
+"Name" => "Nom",
+"Password" => "Mot de passe",
+"Groups" => "Groupes",
+"Create" => "Créer",
+"Delete" => "Supprimer"
+);
diff --git a/settings/l10n/id.php b/settings/l10n/id.php
new file mode 100644
index 0000000000000000000000000000000000000000..b6909f914f914aa4963a5f04064aa3bcd275ebe6
--- /dev/null
+++ b/settings/l10n/id.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Otentikasi bermasalah",
+"OpenID Changed" => "OpenID telah dirubah",
+"Invalid request" => "Permintaan tidak valid",
+"Language changed" => "Bahasa telah diganti",
+"Add your application" => "Tambahkan aplikasi anda",
+"Select an App" => "Pilih satu aplikasi",
+"-licensed" => "-terlisensi",
+"by" => "oleh",
+"Ask a question" => "Ajukan pertanyaan",
+"Problems connecting to help database." => "Bermasalah saat menghubungi database bantuan.",
+"Go there manually." => "Pergi kesana secara manual.",
+"Answer" => "Jawab",
+"You use" => "Anda menggunakan",
+"of the available" => "dari yang tersedia",
+"Your password got changed" => "Password anda telah dirubah",
+"Current password" => "Password saat ini",
+"New password" => "Password baru",
+"show" => "perlihatkan",
+"Change password" => "Rubah password",
+"Language" => "Bahasa",
+"Help translating" => "Bantu terjemahkan",
+"use this address to connect to your ownCloud in your file manager" => "gunakan alamat ini untuk terhubung dengan ownCloud anda dalam file manager anda",
+"Name" => "Nama",
+"Password" => "Password",
+"Groups" => "Group",
+"Create" => "Buat",
+"Delete" => "Hapus"
+);
diff --git a/settings/l10n/it.php b/settings/l10n/it.php
new file mode 100644
index 0000000000000000000000000000000000000000..fd1593717b561b90c0888694091d5b656b8aab97
--- /dev/null
+++ b/settings/l10n/it.php
@@ -0,0 +1,28 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Errore nell'autenticazione",
+"OpenID Changed" => "OpenID Modificato",
+"Invalid request" => "Richiesta non valida",
+"Language changed" => "Lingua modificata",
+"Select an App" => "Seleziona un applicazione",
+"-licensed" => "-licensed",
+"by" => "da",
+"Ask a question" => "Fai una domanda",
+"Problems connecting to help database." => "Problemi di connessione al database di aiutare.",
+"Go there manually." => "Vai lì manualmente.",
+"Answer" => "Risposta",
+"You use" => "Si utilizza",
+"of the available" => "su un totale di",
+"Your password got changed" => "La tua password è stata cambiata",
+"Current password" => "Password attuale",
+"New password" => "Nuova password",
+"show" => "mostra",
+"Change password" => "Modifica password",
+"Language" => "Lingua",
+"Help translating" => "Aiuta nella traduzione",
+"use this address to connect to your ownCloud in your file manager" => "usa questo indirizzo per connettersi al proprio ownCloud nel tuo file manager",
+"Name" => "Nome",
+"Password" => "Password",
+"Groups" => "Gruppi",
+"Create" => "Crea",
+"Delete" => "Cancella"
+);
diff --git a/settings/l10n/nl.php b/settings/l10n/nl.php
new file mode 100644
index 0000000000000000000000000000000000000000..36b37ca720be199eb9bb8bbbff00ca3d67d72301
--- /dev/null
+++ b/settings/l10n/nl.php
@@ -0,0 +1,26 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Authenticatiefout.",
+"OpenID Changed" => "OpenID is aangepast",
+"Invalid request" => "Ongeldig verzoek",
+"Language changed" => "Taal aangepast",
+"Select an App" => "Selecteer een App",
+"-licensed" => "-gelicenseerd",
+"by" => "door",
+"Ask a question" => "Stel een vraag",
+"Answer" => "Beantwoord",
+"You use" => "U gebruikt",
+"of the available" => "van de beschikbare",
+"Your password got changed" => "Uw wachtwoord is aangepast",
+"Current password" => "Huidig wachtwoord",
+"New password" => "Nieuw wachtwoord",
+"show" => "weergeven",
+"Change password" => "Verander wachtwoord",
+"Language" => "Taal",
+"Help translating" => "Help met vertalen",
+"use this address to connect to your ownCloud in your file manager" => "gebruik dit adres om verbinding te maken met ownCloud in uw bestandsbeheer programa",
+"Name" => "Naam",
+"Password" => "Wachtwoord",
+"Groups" => "Groepen",
+"Create" => "Creëer",
+"Delete" => "verwijderen"
+);
diff --git a/settings/l10n/pl.php b/settings/l10n/pl.php
new file mode 100644
index 0000000000000000000000000000000000000000..cfd52cb3475c7b59f013785f761eff77b78bf300
--- /dev/null
+++ b/settings/l10n/pl.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "BÅ‚Ä…d uwierzytelniania",
+"OpenID Changed" => "Zmieniono OpenID",
+"Invalid request" => "Nieprawidłowe żądanie",
+"Language changed" => "Język zmieniony",
+"Add your application" => "Dodaj własną aplikacje",
+"Select an App" => "Zaznacz aplikacje",
+"-licensed" => "-licencjonowany",
+"by" => "przez",
+"Ask a question" => "Zadaj pytanie",
+"Problems connecting to help database." => "Problem z połączeniem z bazą danych.",
+"Go there manually." => "Przejdź na stronę ręcznie.",
+"Answer" => "Odpowiedź",
+"You use" => "Używasz",
+"of the available" => "z dostępnych",
+"Your password got changed" => "Twoje hasło zostało zmienione",
+"Current password" => "Bieżące hasło",
+"New password" => "Nowe hasło",
+"show" => "pokaż",
+"Change password" => "Zmień hasło",
+"Language" => "Język",
+"Help translating" => "Pomóż w tłumaczeniu",
+"use this address to connect to your ownCloud in your file manager" => "użyj tego adresu żeby połączyć się z twoim kontem ownCloud w menedżerze plików.",
+"Name" => "Nazwa",
+"Password" => "Hasło",
+"Groups" => "Grupy",
+"Create" => "Stwórz",
+"Delete" => "Skasuj"
+);
diff --git a/settings/l10n/pt_BR.php b/settings/l10n/pt_BR.php
new file mode 100644
index 0000000000000000000000000000000000000000..d890918452a6c8611a867ddaba9f4276090f7db3
--- /dev/null
+++ b/settings/l10n/pt_BR.php
@@ -0,0 +1,29 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Erro de autenticação",
+"OpenID Changed" => "Mudou OpenID",
+"Invalid request" => "Pedido inválido",
+"Language changed" => "Mudou Idioma",
+"Add your application" => "Adicionar o seu aplicativo",
+"Select an App" => "Selecione uma Aplicação",
+"-licensed" => "-licenciados",
+"by" => "por",
+"Ask a question" => "Faça uma pergunta",
+"Problems connecting to help database." => "Problemas ao conectar na base de dados.",
+"Go there manually." => "Ir manualmente.",
+"Answer" => "Resposta",
+"You use" => "Você usa",
+"of the available" => "do disponível",
+"Your password got changed" => "Sua senha foi modificada",
+"Current password" => "Senha atual",
+"New password" => "Nova senha",
+"show" => "mostrar",
+"Change password" => "Alterar senha",
+"Language" => "Idioma",
+"Help translating" => "Ajuda na Tradução",
+"use this address to connect to your ownCloud in your file manager" => "use este endereço para se conectar ao seu ownCloud no seu gerenciador de arquvos",
+"Name" => "Nome",
+"Password" => "Senha",
+"Groups" => "Grupos",
+"Create" => "Criar",
+"Delete" => "Apagar"
+);
diff --git a/settings/l10n/sv.php b/settings/l10n/sv.php
new file mode 100644
index 0000000000000000000000000000000000000000..f721e9a98916fb2e9da793168aef09ba60ed64db
--- /dev/null
+++ b/settings/l10n/sv.php
@@ -0,0 +1,28 @@
+<?php $TRANSLATIONS = array(
+"Authentication error" => "Autentiseringsfel",
+"OpenID Changed" => "OpenID ändrat",
+"Invalid request" => "Ogiltig begäran",
+"Language changed" => "Språk ändrades",
+"Select an App" => "Välj en App",
+"-licensed" => "-licensierat",
+"by" => "av",
+"Ask a question" => "Ställ en fråga",
+"Problems connecting to help database." => "Problem med att ansluta till hjälp-databasen.",
+"Go there manually." => "GÃ¥ dit manuellt",
+"Answer" => "Svar",
+"You use" => "Du använder",
+"of the available" => "av tillgängliga",
+"Your password got changed" => "Ditt lösenord ändrades",
+"Current password" => "Nuvarande lösenord",
+"New password" => "Nytt lösenord",
+"show" => "visa",
+"Change password" => "Ändra lösenord",
+"Language" => "Språk",
+"Help translating" => "Hjälp till att översätta",
+"use this address to connect to your ownCloud in your file manager" => "använd denna adress för att ansluta till ditt ownCloud i din filhanterare",
+"Name" => "Namn",
+"Password" => "Lösenord",
+"Groups" => "Grupper",
+"Create" => "Skapa",
+"Delete" => "Ta bort"
+);
diff --git a/settings/languageCodes.php b/settings/languageCodes.php
new file mode 100644
index 0000000000000000000000000000000000000000..088ba5f29c6886adf1ea948839de08496ff291a6
--- /dev/null
+++ b/settings/languageCodes.php
@@ -0,0 +1,153 @@
+<?php /**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+ 
+return array(
+'ab'=>'Abkhazian',
+'aa'=>'Afar',
+'af'=>'Afrikaans',
+'sq'=>'Shqip',
+'am'=>'Amharic',
+'ar'=>'Arabic',
+'hy'=>'Armenian',
+'as'=>'Assamese',
+'ay'=>'Aymara',
+'az'=>'Azerbaijani',
+'ba'=>'Bashkir',
+'eu'=>'Euskara',
+'bn'=>'Bengali (Bangla)',
+'dz'=>'Bhutani',
+'bh'=>'Bihari',
+'bi'=>'Bislama',
+'br'=>'Breton',
+'bg'=>'Balgarski ezik',
+'my'=>'Burmese',
+'be'=>'Byelorussian (Belarusian)',
+'km'=>'Cambodian',
+'ca'=>'Catala',
+'zh'=>'Chinese (Simplified)',
+'zh'=>'Chinese (Traditional)',
+'co'=>'Corsican',
+'hr'=>'Hrvatski',
+'cs'=>'Cestina',
+'da'=>'Dansk',
+'nl'=>'Nederlands',
+'en'=>'English',
+'eo'=>'Esperanto',
+'et'=>'Eesti',
+'fo'=>'Faeroese',
+'fa'=>'Farsi',
+'fj'=>'Fiji',
+'fi'=>'Finnish',
+'nl-be'=>'Flemish',
+'fr'=>'Francais',
+'fy'=>'Frisian',
+'gl'=>'Galician',
+'gd'=>'Gaelic (Scottish)',
+'gv'=>'Gaelic (Manx)',
+'ka'=>'Georgian',
+'de'=>'Deutsch',
+'el'=>'Ellinika',
+'kl'=>'Greenlandic',
+'gn'=>'Guarani',
+'gu'=>'Gujarati',
+'ha'=>'Hausa',
+'he'=>'Hebrew',
+'iw'=>'Hebrew',
+'hi'=>'Hindi',
+'hu'=>'Hungarian',
+'is'=>'Icelandic',
+'id'=>'Bahasa Indonesia',
+'in'=>'Bahasa Indonesia',
+'ia'=>'Interlingua',
+'ie'=>'Interlingue',
+'iu'=>'Inuktitut',
+'ik'=>'Inupiak',
+'ga'=>'Irish',
+'it'=>'Italiano',
+'ja'=>'Japanese',
+'jv'=>'Javanese',
+'kn'=>'Kannada',
+'ks'=>'Kashmiri',
+'kk'=>'Kazakh',
+'rw'=>'Kinyarwanda (Ruanda)',
+'ky'=>'Kirghiz',
+'rn'=>'Kirundi (Rundi)',
+'ko'=>'Korean',
+'ku'=>'Kurdish',
+'lo'=>'Laothian',
+'la'=>'Latin',
+'lv'=>'Latvian (Lettish)',
+'li'=>'Limburgish ( Limburger)',
+'ln'=>'Lingala',
+'lt'=>'Lithuanian',
+'mk'=>'Macedonian',
+'mg'=>'Malagasy',
+'ms'=>'Malay',
+'ml'=>'Malayalam',
+'mt'=>'Maltese',
+'mi'=>'Maori',
+'mr'=>'Marathi',
+'mo'=>'Moldavian',
+'mn'=>'Mongolian',
+'na'=>'Nauru',
+'ne'=>'Nepali',
+'no'=>'Norsk',
+'oc'=>'Occitan',
+'or'=>'Oriya',
+'om'=>'Oromo (Afan, Galla)',
+'ps'=>'Pashto (Pushto)',
+'pl'=>'Polski',
+'pt'=>'Portugues',
+'pa'=>'Punjabi',
+'qu'=>'Quechua',
+'rm'=>'Rhaeto-Romance',
+'ro'=>'Romanian',
+'ru'=>'Russian',
+'sm'=>'Samoan',
+'sg'=>'Sangro',
+'sa'=>'Sanskrit',
+'sr'=>'Serbian',
+'sh'=>'Serbo-Croatian',
+'st'=>'Sesotho',
+'tn'=>'Setswana',
+'sn'=>'Shona',
+'sd'=>'Sindhi',
+'si'=>'Sinhalese',
+'ss'=>'Siswati',
+'sk'=>'Slovak',
+'sl'=>'Slovenian',
+'so'=>'Somali',
+'es'=>'Espanol',
+'su'=>'Sundanese',
+'sw'=>'Swahili (Kiswahili)',
+'sv'=>'Svenska',
+'tl'=>'Tagalog',
+'tg'=>'Tajik',
+'ta'=>'Tamil',
+'tt'=>'Tatar',
+'te'=>'Telugu',
+'th'=>'Thai',
+'bo'=>'Tibetan',
+'ti'=>'Tigrinya',
+'to'=>'Tonga',
+'ts'=>'Tsonga',
+'tr'=>'Turkish',
+'tk'=>'Turkmen',
+'tw'=>'Twi',
+'ug'=>'Uighur',
+'uk'=>'Ukrainian',
+'ur'=>'Urdu',
+'uz'=>'Uzbek',
+'vi'=>'Vietnamese',
+'vo'=>'Volapük',
+'cy'=>'Welsh',
+'wo'=>'Wolof',
+'xh'=>'Xhosa',
+'yi'=>'Yiddish',
+'ji'=>'Yiddish',
+'yo'=>'Yoruba',
+'zu'=>'Zulu',
+);
diff --git a/settings/personal.php b/settings/personal.php
new file mode 100644
index 0000000000000000000000000000000000000000..e6d2d44db761b453c1f88de95fcd9e7c18b4feb5
--- /dev/null
+++ b/settings/personal.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once('../lib/base.php');
+if( !OC_User::isLoggedIn()){
+    header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+    exit();
+}
+
+// Highlight navigation entry
+OC_Util::addScript( "settings", "personal" );
+OC_Util::addStyle( "settings", "settings" );
+OC_App::setActiveNavigationEntry( "personal" );
+
+// calculate the disc space
+$used=OC_Filesystem::filesize('/');
+$free=OC_Filesystem::free_space();
+$total=$free+$used;
+$relative=round(($used/$total)*100);
+
+$lang=OC_Preferences::getValue( OC_User::getUser(), 'core', 'lang', 'en' );
+$languageCodes=OC_L10N::findAvailableLanguages();
+//put the current language in the front
+unset($languageCodes[array_search($lang,$languageCodes)]);
+array_unshift($languageCodes,$lang);
+$languageNames=include 'languageCodes.php';
+$languages=array();
+foreach($languageCodes as $lang){
+	$languages[]=array('code'=>$lang,'name'=>$languageNames[$lang]);
+}
+
+// Return template
+$tmpl = new OC_Template( "settings", "personal", "user");
+$tmpl->assign('usage',OC_Helper::humanFileSize($used));
+$tmpl->assign('total_space',OC_Helper::humanFileSize($total));
+$tmpl->assign('usage_relative',$relative);
+$tmpl->assign('languages',$languages);
+
+$forms=OC_App::getForms('personal');
+$tmpl->assign('forms',array());
+foreach($forms as $form){
+	$tmpl->append('forms',$form);
+}
+$tmpl->printPage();
+
+?>
diff --git a/settings/settings.php b/settings/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..724cf63aaf70c4a93106c535c09814b712cf374d
--- /dev/null
+++ b/settings/settings.php
@@ -0,0 +1,23 @@
+<?php
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once('../lib/base.php');
+if( !OC_User::isLoggedIn()){
+    header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+    exit();
+}
+
+OC_Util::addStyle( "settings", "settings" );
+OC_App::setActiveNavigationEntry( "settings" );
+
+$tmpl = new OC_Template( 'settings', 'settings', 'user');
+$forms=OC_App::getForms('settings');
+$tmpl->assign('forms',array());
+foreach($forms as $form){
+	$tmpl->append('forms',$form);
+}
+$tmpl->printPage();
\ No newline at end of file
diff --git a/settings/templates/admin.php b/settings/templates/admin.php
new file mode 100644
index 0000000000000000000000000000000000000000..98acd541e36648a7960da094edfbffedf3d71d99
--- /dev/null
+++ b/settings/templates/admin.php
@@ -0,0 +1,9 @@
+<?php /**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */?>
+
+<?php foreach($_['forms'] as $form){
+	echo $form;
+};?>
\ No newline at end of file
diff --git a/settings/templates/apps.php b/settings/templates/apps.php
new file mode 100644
index 0000000000000000000000000000000000000000..d8f3fb5e4f8996fd39e94d469363a0e235a5982b
--- /dev/null
+++ b/settings/templates/apps.php
@@ -0,0 +1,25 @@
+<?php /**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */?>
+
+<div id="controls">
+	<a class="button" target="_blank" href="http://apps.owncloud.com/"><?php echo $l->t('Add your application');?></a>
+</div>
+<ul id="leftcontent">
+	<?php foreach($_['apps'] as $app):?>
+	<li <?php if($app['active']) echo 'class="active"'?> data-id="<?php echo $app['id'] ?>">
+		<?php  echo $app['name'] ?>
+		<span class="hidden">
+			<?php echo json_encode($app) ?>
+		</span>
+	</li>
+	<?php endforeach;?>
+</ul>
+<div id="rightcontent">
+	<h3><strong><span class="name"><?php echo $l->t('Select an App');?></span></strong><span class="version"></span></h3>
+	<p class="description"></p>
+	<p class="hidden"><span class="licence"></span><?php echo $l->t('-licensed');?> <?php echo $l->t('by');?> <span class="author"></span></p>
+	<input class="enable hidden" type="submit" />
+</div>
diff --git a/settings/templates/help.php b/settings/templates/help.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e3cdd7b908cdc68d0167c3f474770703453812e
--- /dev/null
+++ b/settings/templates/help.php
@@ -0,0 +1,29 @@
+<?php /**
+ * Copyright (c) 2011, Frank Karlitschek karlitschek@kde.org
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */?>
+
+<div id="controls">
+	<a class="button newquestion" href="http://apps.owncloud.com/knowledgebase/editquestion.php?action=new" target="_blank"><?php echo $l->t( 'Ask a question' ); ?></a>
+	<?php
+		$url=OC_Helper::linkTo( "settings", "help.php" ).'?page=';
+		$pageNavi=OC_Util::getPageNavi($_['pagecount'],$_['page'],$url);
+		$pageNavi->printPage();
+	?>
+</diV>
+<?php if(is_null($_["kbe"])):?>
+	<div class="helpblock">
+		<p><?php echo $l->t('Problems connecting to help database.');?></p>
+		<p><a href="http://apps.owncloud.com/kb"><?php echo $l->t('Go there manually.');?></a></p>
+	</div>
+<?php else:?>
+	<?php foreach($_["kbe"] as $kb): ?>
+	<div class="helpblock">
+		<?php if($kb["preview1"] <> "") { echo('<img class="preview" src="'.$kb["preview1"].'" />'); } ?>
+		<?php if($kb['detailpage']<>'') echo('<p><a target="_blank" href="'.$kb['detailpage'].'"><strong>'.$kb["name"].'</strong></a></p>');?>
+		<p><?php echo $kb['description'];?></p>
+		<?php if($kb['answer']<>'') echo('<p><strong>'.$l->t('Answer').':</strong><p>'.$kb['answer'].'</p>');?>
+	</div>
+	<?php endforeach;
+endif?>
diff --git a/settings/templates/personal.php b/settings/templates/personal.php
new file mode 100644
index 0000000000000000000000000000000000000000..67e647ce92644b43e1558c84776850c0dfdaf6b3
--- /dev/null
+++ b/settings/templates/personal.php
@@ -0,0 +1,41 @@
+<?php /**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */?>
+
+<div id="quota" class="personalblock"><div style="width:<?php echo $_['usage_relative'];?>%;">
+	<p><?php echo $l->t('You use');?> <strong><?php echo $_['usage'];?></strong> <?php echo $l->t('of the available');?> <strong><?php echo $_['total_space'];?></strong></p>
+</div></div>
+
+<form id="passwordform">
+	<fieldset class="personalblock">
+		<div id="passwordchanged"><?php echo $l->t('Your password got changed');?></div>
+		<div id="passworderror"><?php echo $l->t('Unable to change your password');?></div>
+		<input type="password" id="pass1" name="oldpassword" placeholder="<?php echo $l->t('Current password');?>" />
+		<input type="password" id="pass2" name="password" placeholder="<?php echo $l->t('New password');?>" data-typetoggle="#show" />
+		<input type="checkbox" id="show" name="show" /><label for="show"><?php echo $l->t('show');?></label>
+		<input id="passwordbutton" type="submit" value="<?php echo $l->t('Change password');?>" />
+	</fieldset>
+</form>
+
+<form>
+	<fieldset class="personalblock">
+		<label for="languageinput"><strong><?php echo $l->t('Language');?></strong></label>
+		<select id="languageinput" class="chzen-select" name="lang" data-placeholder="<?php echo $l->t('Language');?>">
+		<?php foreach($_['languages'] as $language):?>
+			<option value="<?php echo $language['code'];?>"><?php echo $language['name'];?></option>
+		<?php endforeach;?>
+		</select>
+		<a href="https://www.transifex.net/projects/p/owncloud/team/<?php echo $_['languages'][0]['code'];?>/" target="_blank"><em><?php echo $l->t('Help translating');?></em></a>
+	</fieldset>
+</form>
+
+<p class="personalblock">
+	<label for="webdav"><strong>WebDAV</strong></label>
+	<input id="webdav" type="text" value="<?php echo ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http').'://'.$_SERVER['HTTP_HOST'].OC::$WEBROOT.'/files/webdav.php'; ?>" title="<?php echo $l->t('use this address to connect to your ownCloud in your file manager');?>" />
+</p>
+
+<?php foreach($_['forms'] as $form){
+	echo $form;
+};?>
diff --git a/settings/templates/settings.php b/settings/templates/settings.php
new file mode 100644
index 0000000000000000000000000000000000000000..98acd541e36648a7960da094edfbffedf3d71d99
--- /dev/null
+++ b/settings/templates/settings.php
@@ -0,0 +1,9 @@
+<?php /**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */?>
+
+<?php foreach($_['forms'] as $form){
+	echo $form;
+};?>
\ No newline at end of file
diff --git a/settings/templates/users.php b/settings/templates/users.php
new file mode 100644
index 0000000000000000000000000000000000000000..a97774cea0b87c89adb2596d93a5c8be12cd7385
--- /dev/null
+++ b/settings/templates/users.php
@@ -0,0 +1,54 @@
+<?php /**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+$allGroups=array();
+foreach($_["groups"] as $group) {
+	$allGroups[]=$group['name'];
+}
+?>
+
+<table data-groups="<?php echo implode(', ',$allGroups);?>">
+	<thead id="controls">
+		<tr><form id="newuser">
+			<th class="name"><input id="newusername" placeholder="<?php echo $l->t('Name')?>" /></th>
+			<th class="password"><input type="password" id="newuserpassword" placeholder="<?php echo $l->t('Password')?>" /></th>
+			<th class="groups"><select id="newusergroups" data-placeholder="groups" title="<?php echo $l->t('Groups')?>" multiple="multiple">
+			<?php foreach($_["groups"] as $group): ?>
+				<option value="<?php echo $group['name'];?>"><?php echo $group['name'];?></option>
+			<?php endforeach;?>
+			</select></th>
+			<th class="quota"></th>
+			<th><input type="submit" value="<?php echo $l->t('Create')?>" /></th>
+		</form></tr>
+	</thead>
+	<tbody>
+	<?php foreach($_["users"] as $user): ?>
+		<tr data-uid="<?php echo $user["name"] ?>">
+			<td class="name"><?php echo $user["name"]; ?></td>
+			<td class="password">
+				<span>●●●●●●●</span>
+				<img class="svg action" src="<?php echo image_path('core','actions/rename.svg')?>" alt="set new password" title="set new password" />
+			</td>
+			<td class="groups">
+				<select data-username="<?php echo $user['name'] ;?>" data-user-groups="<?php echo $user['groups'] ;?>" data-placeholder="groups" title="<?php echo $l->t('Groups')?>" multiple="multiple">
+					<?php foreach($_["groups"] as $group): ?>
+						<option value="<?php echo $group['name'];?>"><?php echo $group['name'];?></option>
+					<?php endforeach;?>
+				</select>
+			</td>
+			<td class="quota" data-quota="<?php echo $user['quota']?>">
+				<span><?php echo ($user['quota']>0)?$user['quota']:'None';?></span>
+				<img class="svg action" src="<?php echo image_path('core','actions/rename.svg')?>" alt="set new password" title="set quota" />
+			</td>
+			<td class="remove">
+				<?php if($user['name']!=OC_User::getUser()):?>
+					<img alt="Delete" title="<?php echo $l->t('Delete')?>" class="svg action" src="<?php echo image_path('core','actions/delete.svg') ?>" />
+				<?php endif;?>
+			</td>
+		</tr>
+	<?php endforeach; ?>
+	</tbody>
+</table>
diff --git a/settings/users.php b/settings/users.php
new file mode 100644
index 0000000000000000000000000000000000000000..5aae4ce43effd9a8301770d6c5eaa7dfaeb517fe
--- /dev/null
+++ b/settings/users.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Copyright (c) 2011, Robin Appelman <icewind1991@gmail.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+require_once('../lib/base.php');
+if( !OC_User::isLoggedIn() || !OC_Group::inGroup( OC_User::getUser(), 'admin' )){
+	header( "Location: ".OC_Helper::linkTo( "", "index.php" ));
+	exit();
+}
+
+// We have some javascript foo!
+OC_Util::addScript( 'settings', 'users' );
+OC_Util::addStyle( 'settings', 'settings' );
+OC_Util::addScript( '3rdparty', 'chosen/chosen.jquery.min' );
+OC_Util::addStyle( '3rdparty', 'chosen' );
+OC_App::setActiveNavigationEntry( 'core_users' );
+
+$users = array();
+$groups = array();
+
+foreach( OC_User::getUsers() as $i ){
+	$users[] = array( "name" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ),'quota'=>OC_Helper::humanFileSize(OC_Preferences::getValue($i,'files','quota',0)));
+}
+
+foreach( OC_Group::getGroups() as $i ){
+	// Do some more work here soon
+	$groups[] = array( "name" => $i );
+}
+
+$tmpl = new OC_Template( "settings", "users", "user" );
+$tmpl->assign( "users", $users );
+$tmpl->assign( "groups", $groups );
+$tmpl->printPage();
+
+?>
+
diff --git a/tests/index.php b/tests/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..efa730f6f8f88b35883a9e5e9c5473ed48a9a38c
--- /dev/null
+++ b/tests/index.php
@@ -0,0 +1,77 @@
+<?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/>.
+*
+*/
+
+
+/**
+ * run all test cases
+ */
+ $RUNTIME_NOSETUPFS=true;
+require_once('../lib/base.php');
+
+$testCases=loadFiles(__DIR__,array('index.php','templates'));
+ob_end_clean();
+$testResults=array();
+foreach($testCases as $testCaseClass){
+	$testCase=new $testCaseClass();
+	$results=array();
+	foreach($testCase->getTests() as $test){
+		$testCase->setup();
+		try{
+			$testCase->$test();
+			$results[$test]='Ok';
+		}catch(Exception $e){
+			$results[$test]=$e->getMessage();
+		}
+		$testCase->tearDown();
+	}
+	$testResults[$testCaseClass]=$results;
+}
+
+$tmpl = new OC_Template( 'tests', 'index');
+$tmpl->assign('tests',$testResults);
+$tmpl->printPage();
+
+/**
+ * recursively load all files in a folder
+ * @param string $path
+ * @param array $exclude list of files to exclude
+ */
+function loadFiles($path,$exclude=false){
+	$results=array();
+	if(!$exclude){
+		$exclude=array();
+	}
+	$dh=opendir($path);
+	while($file=readdir($dh)){
+		if($file!='.' && $file!='..' && array_search($file,$exclude)===false){
+			if(is_file($path.'/'.$file) and substr($file,-3)=='php'){
+				$result=require_once($path.'/'.$file);
+				$results[]=$result;
+			}elseif(is_dir($path.'/'.$file)){
+				$subResults=loadFiles($path.'/'.$file);
+				$results=array_merge($results,$subResults);
+			}
+		}
+	}
+	return $results;
+}
+?>
\ No newline at end of file
diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php
new file mode 100644
index 0000000000000000000000000000000000000000..4bfa23884f4ea74e88cad28694b03d1cbfd859ce
--- /dev/null
+++ b/tests/lib/filesystem.php
@@ -0,0 +1,222 @@
+<?php
+class OC_FILEYSYSTEM_Test extends OC_TestCase
+{
+	public function setup(){
+		OC_Util::setupFS('testuser','testcase');
+	}
+	public function tearDown(){
+		OC_Filesystem::chroot('');
+		OC_Filesystem::delTree('/testuser');
+		OC_Util::tearDownFS();
+	}
+	
+	public function isDir(){
+		$this->assertEquals(true, OC_Filesystem::is_dir('/'),'Root is not a directory');
+	}
+	
+	public function fileExists(){
+		$this->assertEquals(false, OC_Filesystem::file_exists('/dummy'),'Unexpected result with non-existing file');
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals(true, OC_Filesystem::file_exists('/dummy'),'Unexpected result with existing file');
+	}
+
+	public function mkdir(){
+		OC_Filesystem::mkdir('/dummy');
+		$this->assertEquals(true, OC_Filesystem::file_exists('/dummy'),'No such file or directory after creating folder');
+		$this->assertEquals(true, OC_Filesystem::is_dir('/dummy'),'File created instead of filder');
+	}
+
+	public function rmdir(){
+		OC_Filesystem::mkdir('/dummy');
+		OC_Filesystem::rmdir('/dummy');
+		$this->assertEquals(false, OC_Filesystem::file_exists('/dummy'),'Folder still exists after removing');
+	}
+
+	public function isFile(){
+		$this->assertEquals(false, OC_Filesystem::is_file('/'),'Root is a file');
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals(true, OC_Filesystem::is_file('/dummy'),'Created file is not said to be a file');
+	}
+
+	public function opendir(){
+		OC_Filesystem::file_put_contents('/dummy1','foo');
+		OC_Filesystem::file_put_contents('/dummy2','foo');
+		$dh=OC_Filesystem::opendir('/');
+		if(!$dh){
+			$this->fail('Failed to open root');
+		}
+		$dummy1Found=false;
+		$dummy2Found=false;
+		while($file=readdir($dh)){
+			if($file=='dummy1'){
+				$dummy1Found=true;
+			}elseif($file=='dummy2'){
+				$dummy2Found=true;
+			}elseif($file!='.' and $file!='..'){
+				$this->fail('Unexpected filename when reading opened dir');
+			}
+		}
+		$this->assertEquals(true,$dummy1Found,'Not all files found when reading opened dir');
+		$this->assertEquals(true,$dummy2Found,'Not all files found when reading opened dir');
+	}
+
+	public function filesize(){
+		OC_Filesystem::file_put_contents('/dummy','1234567890');
+		$this->assertEquals(10, OC_Filesystem::filesize('/dummy'),'Unexpected filesize');
+	}
+
+	public function stat(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$time=time();
+		$stat=OC_Filesystem::stat('/dummy');
+		$this->assertEquals(true,abs($time-$stat['atime'])<1,'Unexpected access time');//there can be small difference between those values due to running time
+		$this->assertEquals(true,abs($time-$stat['ctime'])<1,'Unexpected creation time');
+		$this->assertEquals(true,abs($time-$stat['mtime'])<1,'Unexpected modified time');
+		$this->assertEquals(3,$stat['size'],'Unexpected filesize');
+	}
+
+	public function filetype(){
+		OC_Filesystem::file_put_contents('/dummyFile','foo');
+		OC_Filesystem::mkdir('/dummyFolder');
+		$this->assertEquals('file', OC_Filesystem::filetype('/dummyFile'),'Unexpected filetype of file');
+		$this->assertEquals('dir', OC_Filesystem::filetype('/dummyFolder'),'Unexpected filetype of folder');
+	}
+
+	public function readfile(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		ob_start();
+		OC_Filesystem::readfile('/dummy');
+		$this->assertEquals('foo', ob_get_contents(),'Unexpected output of readfile');
+		ob_end_clean();
+	}
+
+	public function isReadable(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals(true, OC_Filesystem::is_readable('/dummy'),'Can\'t read created file');
+	}
+
+	public function isWritable(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals(true, OC_Filesystem::is_writeable('/dummy'),'Can\'t write created file');
+	}
+
+	public function fileatime(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$time=time();
+		$this->assertEquals(true,abs($time-OC_Filesystem::fileatime('/dummy'))<1,'Unexpected access time');//there can be small difference between those values due to running time
+	}
+
+	public function filectime(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$time=time();
+		$this->assertEquals(true,abs($time-OC_Filesystem::filectime('/dummy'))<1,'Unexpected creation time');//there can be small difference between those values due to running time
+	}
+
+	public function filemtime(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$time=time();
+		$this->assertEquals(true,abs($time-OC_Filesystem::filemtime('/dummy'))<1,'Unexpected modified time');//there can be small difference between those values due to running time
+	}
+
+	public function fileGetContents(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals('foo', OC_Filesystem::file_get_contents('/dummy'),'Unexpected content of file');
+	}
+
+	public function unlink(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		OC_Filesystem::unlink('/dummy');
+		$this->assertEquals(false, OC_Filesystem::file_exists('/dummy'),'File still exists after deletion');
+	}
+
+	public function rename(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		OC_Filesystem::rename('/dummy','/bar');
+		$this->assertEquals(true, OC_Filesystem::file_exists('/bar'),'New file doesnt exists after moving');
+		$this->assertEquals(false, OC_Filesystem::file_exists('/dummy'),'Old file still exists after moving');
+		$this->assertEquals('foo', OC_Filesystem::file_get_contents('/bar'),'Unexpected content of file after moving');
+	}
+
+	public function copy(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		OC_Filesystem::copy('/dummy','/bar');
+		$this->assertEquals(true, OC_Filesystem::file_exists('/bar'),'New file doesnt exists after copying');
+		$this->assertEquals(true, OC_Filesystem::file_exists('/dummy'),'Old file doesnt exists after copying');
+		$this->assertEquals('foo', OC_Filesystem::file_get_contents('/bar'),'Unexpected content of file after copying');
+	}
+
+	public function fopen(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$fh=OC_Filesystem::fopen('/dummy','r');
+		if(!$fh){
+			$this->fail('Cant open file for reading');
+		}
+		$content=fread($fh,3);
+		$this->assertEquals('foo', $content,'Unexpected content of file');
+		fclose($fh);
+		$fh=OC_Filesystem::fopen('/dummy','a');
+		fwrite($fh,'bar',3);
+		fclose($fh);
+		$this->assertEquals('foobar', OC_Filesystem::file_get_contents('/dummy'),'Unexpected content of file after appending');
+		$fh=OC_Filesystem::fopen('/dummy','w');
+		fwrite($fh,'bar',3);
+		fclose($fh);
+		$this->assertEquals('bar', OC_Filesystem::file_get_contents('/dummy'),'Unexpected content of file after writing');
+	}
+
+	public function toTmpFile(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$tmp=OC_Filesystem::toTmpFile('/dummy');
+		$this->assertEquals('foo', file_get_contents($tmp),'Unexpected content of temporary file');
+		unlink($tmp);
+	}
+
+	public function fromTmpFile(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$tmp=OC_Filesystem::toTmpFile('/dummy');
+		OC_Filesystem::fromTmpFile($tmp,'/bar');
+		$this->assertEquals('foo', OC_Filesystem::file_get_contents('/bar'),'Unexpected content of new file');
+		$this->assertEquals(false, file_exists($tmp),'Temporary file still exists');
+	}
+
+	public function getMimeType(){
+		OC_Filesystem::file_put_contents('/dummy','some plain text');
+		$this->assertEquals('text/plain', OC_Filesystem::getMimeType('/dummy'),'Unexpected mimetype of pain text file');
+		OC_Filesystem::file_put_contents('/dummy',"<?xml version='1.0'?>\n</dummy>");
+		$this->assertEquals('application/xml', OC_Filesystem::getMimeType('/dummy'),'Unexpected mimetype of xml file');
+	}
+
+	public function delTree(){
+		OC_Filesystem::mkdir('/dummy');
+		OC_Filesystem::file_put_contents('/dummy/bar','foo');
+		OC_Filesystem::delTree('/dummy');
+		$this->assertEquals(false, OC_Filesystem::file_exists('/dummy/bar'),'File in deleted folder still exists');
+		$this->assertEquals(false, OC_Filesystem::file_exists('/dummy'),'Deleted folder still exists');
+	}
+
+	public function getTree(){
+		OC_Filesystem::mkdir('/dummy');
+		OC_Filesystem::file_put_contents('/dummy/bar','foo');
+		$expected=array('/dummy','/dummy/bar');
+		$this->assertEquals($expected, OC_Filesystem::getTree('/dummy'),'Unexpected filelist returned');
+	}
+
+	public function hash(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals(md5('foo'), OC_Filesystem::hash('md5','/dummy'),'Unexpected md5 hash of file');
+	}
+
+	public function freeSpace(){
+		$oldSpace=OC_Filesystem::free_space('/');
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$newSpace=OC_Filesystem::free_space('/');
+		$this->assertEquals(true, $newSpace<$oldSpace,'free space not smaller after creating a non empty file');
+	}
+
+	public function search(){
+		OC_Filesystem::file_put_contents('/dummy','foo');
+		$this->assertEquals(array('/dummy'),OC_Filesystem::search('my'),'unexpected file list after search');
+	}
+}
+return 'OC_FILEYSYSTEM_Test';
+?>
\ No newline at end of file
diff --git a/tests/templates/index.php b/tests/templates/index.php
new file mode 100644
index 0000000000000000000000000000000000000000..b8d3dac06662be0ead6f93d383837832cb608e90
--- /dev/null
+++ b/tests/templates/index.php
@@ -0,0 +1,11 @@
+<?php foreach($_['tests'] as $name=>$results):?>
+	<h2><?php echo $name;?></h2>
+	<ul>
+		<?php foreach($results as $test=>$result):?>
+			<li>
+				<b><?php echo $test;?></b>
+				<?php echo $result;?>
+			</il>
+		<?php endforeach ?>
+	</ul>
+<?php endforeach ?>
\ No newline at end of file